diff --git a/app/src/main/java/com/rosetta/messenger/network/ProtocolManager.kt b/app/src/main/java/com/rosetta/messenger/network/ProtocolManager.kt index b6e5af2..d067cd7 100644 --- a/app/src/main/java/com/rosetta/messenger/network/ProtocolManager.kt +++ b/app/src/main/java/com/rosetta/messenger/network/ProtocolManager.kt @@ -53,6 +53,15 @@ object ProtocolManager { setupPacketHandlers() } + /** + * 🔥 Инициализация аккаунта - КРИТИЧНО для получения сообщений! + * Должен вызываться после авторизации пользователя + */ + fun initializeAccount(publicKey: String, privateKey: String) { + addLog("🔐 Initializing account for message handling: ${publicKey.take(16)}...") + messageRepository?.initialize(publicKey, privateKey) + } + /** * Настройка обработчиков пакетов */ diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatDetailScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatDetailScreen.kt index 7901e64..c3bcacd 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatDetailScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatDetailScreen.kt @@ -218,7 +218,7 @@ fun ChatDetailScreen( onUserProfileClick: () -> Unit = {}, viewModel: ChatViewModel = viewModel() ) { - // 🔥 Автоматическое скрытие клавиатуры при выходе с экрана + // 🔥 Backup: скрывает клавиатуру при dispose (если не скрыли раньше) HideKeyboardOnDispose() val keyboardController = LocalSoftwareKeyboardController.current @@ -288,14 +288,16 @@ fun ChatDetailScreen( var selectedMessages by remember { mutableStateOf>(emptySet()) } val isSelectionMode = selectedMessages.isNotEmpty() - // 🔥 Быстрое закрытие - мгновенный выход без дополнительной анимации + // 🔥 Быстрое закрытие - СНАЧАЛА скрываем клавиатуру, ПОТОМ выходим val hideKeyboardAndBack: () -> Unit = { - // Мгновенно убираем фокус и клавиатуру + // 1. Убираем фокус (это важно сделать первым!) focusManager.clearFocus(force = true) - keyboardController?.hide() - // Сразу выходим - анимация в MainActivity + + // 2. Мгновенное синхронное скрытие клавиатуры через InputMethodManager + keyboard.hideNow() + + // 3. Теперь выходим - клавиатура уже скрыта onBack() - Unit } // Определяем это Saved Messages или обычный чат @@ -378,8 +380,9 @@ fun ChatDetailScreen( // 🔥 Cleanup при выходе из экрана DisposableEffect(Unit) { onDispose { - focusManager.clearFocus() - keyboardController?.hide() + // Скрываем клавиатуру синхронно (backup если не скрыли раньше) + focusManager.clearFocus(force = true) + keyboard.hideNow() // 🔥 Закрываем диалог - сообщения больше не будут читаться автоматически viewModel.closeDialog() } @@ -527,11 +530,15 @@ fun ChatDetailScreen( ) { // 🔔 Кнопка назад с badge непрочитанных сообщений Box { - IconButton(onClick = hideKeyboardAndBack) { + IconButton( + onClick = hideKeyboardAndBack, + modifier = Modifier.size(40.dp) + ) { Icon( - Icons.Default.ArrowBack, + Icons.Default.KeyboardArrowLeft, contentDescription = "Back", - tint = headerIconColor + tint = headerIconColor, + modifier = Modifier.size(32.dp) ) } // Badge с количеством непрочитанных из других чатов @@ -542,7 +549,7 @@ fun ChatDetailScreen( .offset(x = (-4).dp, y = 6.dp) .size(if (totalUnreadFromOthers > 9) 20.dp else 18.dp) .clip(CircleShape) - .background(PrimaryBlue), + .background(Color(0xFFFF3B30)), // Красный цвет как в iOS contentAlignment = Alignment.Center ) { Text( @@ -558,6 +565,8 @@ fun ChatDetailScreen( } } + Spacer(modifier = Modifier.width(4.dp)) + // Аватар Box( modifier = diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt index 15d22ac..af6b37f 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt @@ -189,6 +189,8 @@ fun ChatsListScreen( chatsViewModel.setAccount(accountPublicKey, accountPrivateKey) // Устанавливаем аккаунт для RecentSearchesManager RecentSearchesManager.setAccount(accountPublicKey) + // 🔥 КРИТИЧНО: Инициализируем MessageRepository для обработки входящих сообщений + ProtocolManager.initializeAccount(accountPublicKey, accountPrivateKey) } } diff --git a/app/src/main/java/com/rosetta/messenger/ui/components/KeyboardController.kt b/app/src/main/java/com/rosetta/messenger/ui/components/KeyboardController.kt index e9eb7ec..17c68c1 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/components/KeyboardController.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/components/KeyboardController.kt @@ -1,5 +1,6 @@ package com.rosetta.messenger.ui.components +import android.app.Activity import android.content.Context import android.view.View import android.view.inputmethod.InputMethodManager @@ -38,10 +39,29 @@ class KeyboardManager( private val context: Context ) { /** - * Скрыть клавиатуру мгновенно (WindowInsetsController - API 30+) + * 🔥 Скрыть клавиатуру СИНХРОННО и МГНОВЕННО + * Использует InputMethodManager - самый надёжный способ + */ + fun hideNow() { + // 1. Убираем фокус с текущего view + view.clearFocus() + + // 2. InputMethodManager - синхронное скрытие + val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager + imm?.hideSoftInputFromWindow(view.windowToken, 0) + + // 3. WindowInsetsController как backup + try { + ViewCompat.getWindowInsetsController(view)?.hide(WindowInsetsCompat.Type.ime()) + } catch (e: Exception) { + // Ignore + } + } + + /** + * Скрыть клавиатуру (асинхронно через WindowInsetsController) */ fun hide() { - // WindowInsetsController - самый быстрый способ (API 30+) ViewCompat.getWindowInsetsController(view)?.hide(WindowInsetsCompat.Type.ime()) } @@ -86,6 +106,8 @@ fun rememberKeyboardController(): KeyboardManager { /** * 🔥 Автоматически скрывает клавиатуру при выходе с экрана (onDispose) * + * ВАЖНО: Это backup. Основное скрытие должно быть в hideKeyboardAndBack() + * * Добавьте в начало любого Composable экрана с клавиатурой: * ```kotlin * @Composable @@ -99,14 +121,11 @@ fun rememberKeyboardController(): KeyboardManager { @Composable fun HideKeyboardOnDispose() { val keyboard = rememberKeyboardController() - val keyboardController = LocalSoftwareKeyboardController.current DisposableEffect(Unit) { onDispose { - // WindowInsetsController - мгновенное скрытие - keyboard.hide() - // Fallback для Compose - keyboardController?.hide() + // Backup скрытие при dispose + keyboard.hideNow() } } } @@ -120,17 +139,14 @@ fun HideKeyboardOnDispose() { @Composable fun HideKeyboardAndClearFocusOnDispose() { val keyboard = rememberKeyboardController() - val keyboardController = LocalSoftwareKeyboardController.current val focusManager = LocalFocusManager.current DisposableEffect(Unit) { onDispose { // Сбрасываем фокус focusManager.clearFocus(force = true) - // WindowInsetsController - мгновенное скрытие - keyboard.hide() - // Fallback для Compose - keyboardController?.hide() + // Мгновенное скрытие + keyboard.hideNow() } } }