diff --git a/app/src/main/java/com/rosetta/messenger/data/MessageRepository.kt b/app/src/main/java/com/rosetta/messenger/data/MessageRepository.kt index 94ab4f3..019432f 100644 --- a/app/src/main/java/com/rosetta/messenger/data/MessageRepository.kt +++ b/app/src/main/java/com/rosetta/messenger/data/MessageRepository.kt @@ -285,6 +285,31 @@ class MessageRepository private constructor(private val context: Context) { // 🔥 КРИТИЧНО: Обновляем диалог через updateDialogFromMessages dialogDao.updateDialogFromMessages(account, toPublicKey) + // 📁 Для saved messages - гарантируем создание/обновление dialog + if (isSavedMessages) { + val existing = dialogDao.getDialog(account, account) + dialogDao.insertDialog( + com.rosetta.messenger.database.DialogEntity( + id = existing?.id ?: 0, + account = account, + opponentKey = account, + opponentTitle = existing?.opponentTitle ?: "Saved Messages", + opponentUsername = existing?.opponentUsername ?: "", + lastMessage = encryptedPlainMessage, + lastMessageTimestamp = timestamp, + unreadCount = 0, + isOnline = 0, + lastSeen = existing?.lastSeen ?: 0, + verified = existing?.verified ?: 0, + iHaveSent = 1, + lastMessageFromMe = 1, + lastMessageDelivered = 1, + lastMessageRead = 1, + lastMessageAttachments = serializeAttachments(attachments) + ) + ) + } + // 🔥 Логируем что записалось в диалог val dialog = dialogDao.getDialog(account, toPublicKey) MessageLogger.logDialogUpdate( diff --git a/app/src/main/java/com/rosetta/messenger/database/MessageEntities.kt b/app/src/main/java/com/rosetta/messenger/database/MessageEntities.kt index 622c0cb..20273ed 100644 --- a/app/src/main/java/com/rosetta/messenger/database/MessageEntities.kt +++ b/app/src/main/java/com/rosetta/messenger/database/MessageEntities.kt @@ -579,8 +579,11 @@ interface DialogDao { */ @Transaction suspend fun updateDialogFromMessages(account: String, opponentKey: String) { + // 📁 Для saved messages dialogKey = account (не "$account:$account") val dialogKey = - if (account < opponentKey) "$account:$opponentKey" else "$opponentKey:$account" + if (account == opponentKey) account + else if (account < opponentKey) "$account:$opponentKey" + else "$opponentKey:$account" // 1. Получаем последнее сообщение — O(1) по индексу (account, dialog_key, timestamp) val lastMsg = getLastMessageByDialogKey(account, dialogKey) ?: return @@ -624,7 +627,8 @@ interface DialogDao { */ @Transaction suspend fun updateSavedMessagesDialogFromMessages(account: String) { - val dialogKey = "$account:$account" + // 📁 dialogKey для saved messages = account (не "$account:$account") + val dialogKey = account val lastMsg = getLastMessageByDialogKey(account, dialogKey) ?: return val existing = getDialog(account, account) diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListViewModel.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListViewModel.kt index 0602059..d2df47d 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListViewModel.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListViewModel.kt @@ -536,9 +536,11 @@ class ChatsListViewModel(application: Application) : AndroidViewModel(applicatio // 🔥 Обновляем счетчик requests _requestsCount.value = _requests.value.size - // Вычисляем правильный dialog_key (отсортированная комбинация ключей) + // Вычисляем правильный dialog_key val dialogKey = - if (currentAccount < opponentKey) { + if (currentAccount == opponentKey) { + currentAccount // 📁 Saved Messages + } else if (currentAccount < opponentKey) { "$currentAccount:$opponentKey" } else { "$opponentKey:$currentAccount" diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/SearchScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/SearchScreen.kt index a5fd0e6..f51ecb0 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/SearchScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/SearchScreen.kt @@ -62,11 +62,15 @@ fun SearchScreen( val view = LocalView.current val focusManager = LocalFocusManager.current if (!view.isInEditMode) { - SideEffect { + DisposableEffect(isDarkTheme) { val window = (view.context as android.app.Activity).window val insetsController = androidx.core.view.WindowCompat.getInsetsController(window, view) insetsController.isAppearanceLightStatusBars = !isDarkTheme window.statusBarColor = android.graphics.Color.TRANSPARENT + onDispose { + // Restore white status bar icons for chat list header + insetsController.isAppearanceLightStatusBars = false + } } } diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/input/ChatDetailInput.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/input/ChatDetailInput.kt index a0d8c5a..7b63d6d 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/input/ChatDetailInput.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/input/ChatDetailInput.kt @@ -227,12 +227,28 @@ fun MessageInputBar( }, hideEmoji = { onToggleEmojiPicker(false) } ) - } else { - // KEYBOARD → EMOJI + } else if (coordinator.isKeyboardVisible || coordinator.keyboardHeight > 0.dp) { + // KEYBOARD → EMOJI (клавиатура открыта) coordinator.requestShowEmoji( hideKeyboard = { imm.hideSoftInputFromWindow(view.windowToken, 0) }, showEmoji = { onToggleEmojiPicker(true) } ) + } else { + // Клавиатура НЕ открыта → открываем emoji напрямую + // Устанавливаем высоту из сохранённой или дефолт + if (coordinator.emojiHeight == 0.dp) { + val savedPx = com.rosetta.messenger.ui.components.KeyboardHeightProvider + .getSavedKeyboardHeight(context) + if (savedPx > 0) { + coordinator.emojiHeight = with(density) { savedPx.toDp() } + } else { + coordinator.emojiHeight = 300.dp // разумный дефолт + } + } + coordinator.isEmojiBoxVisible = true + coordinator.openEmojiOnly( + showEmoji = { onToggleEmojiPicker(true) } + ) } } diff --git a/app/src/main/java/com/rosetta/messenger/ui/settings/OtherProfileScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/settings/OtherProfileScreen.kt index 2016926..bfd7c36 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/settings/OtherProfileScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/settings/OtherProfileScreen.kt @@ -420,11 +420,18 @@ fun OtherProfileScreen( } } - // DEBUG LOGS + // Коллапс при отсутствии аватара + LaunchedEffect(hasAvatar) { + if (!hasAvatar) { + isPulledDown = false + overscrollOffset = 0f + } + } + // ═══════════════════════════════════════════════════════════════ // NESTED SCROLL - Telegram style with overscroll support // ═══════════════════════════════════════════════════════════════ - val nestedScrollConnection = remember { + val nestedScrollConnection = remember(hasAvatar) { object : NestedScrollConnection { override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { val delta = available.y @@ -474,8 +481,8 @@ fun OtherProfileScreen( available: Offset, source: NestedScrollSource ): Offset { - // Overscroll при свайпе вниз от верха - if (available.y > 0 && scrollOffset == 0f) { + // Overscroll при свайпе вниз от верха (только если есть аватар) + if (available.y > 0 && scrollOffset == 0f && hasAvatar) { // Telegram: сопротивление если ещё не isPulledDown val resistance = if (isPulledDown) 1f else 0.5f val delta = available.y * resistance diff --git a/app/src/main/java/com/rosetta/messenger/ui/theme/Theme.kt b/app/src/main/java/com/rosetta/messenger/ui/theme/Theme.kt index 63456bc..35d7eaf 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/theme/Theme.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/theme/Theme.kt @@ -71,7 +71,7 @@ fun RosettaAndroidTheme( val insetsController = WindowCompat.getInsetsController(window, view) // Make status bar transparent for wave animation overlay window.statusBarColor = AndroidColor.TRANSPARENT - insetsController.isAppearanceLightStatusBars = !darkTheme + // Note: isAppearanceLightStatusBars is managed per-screen, not globally // Navigation bar: показываем только если есть нативные кнопки NavigationModeUtils.applyNavigationBarVisibility(insetsController, context, darkTheme)