feat: Remove HideKeyboardOnDispose component to streamline keyboard management across screens
This commit is contained in:
@@ -26,7 +26,6 @@ import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
|
||||
import com.rosetta.messenger.ui.components.HideKeyboardOnDispose
|
||||
|
||||
// Beautiful solid colors that fit the theme
|
||||
private val wordColors = listOf(
|
||||
@@ -51,9 +50,6 @@ fun ConfirmSeedPhraseScreen(
|
||||
onBack: () -> Unit,
|
||||
onConfirmed: () -> Unit
|
||||
) {
|
||||
// 🔥 Автоматическое скрытие клавиатуры при выходе с экрана
|
||||
HideKeyboardOnDispose()
|
||||
|
||||
val backgroundColor = if (isDarkTheme) Color(0xFF1E1E1E) else Color(0xFFFFFFFF)
|
||||
val textColor = if (isDarkTheme) Color.White else Color.Black
|
||||
val secondaryTextColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666)
|
||||
|
||||
@@ -22,7 +22,6 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
|
||||
import com.rosetta.messenger.ui.components.HideKeyboardOnDispose
|
||||
|
||||
data class AccountInfo(
|
||||
val id: String,
|
||||
@@ -64,9 +63,6 @@ fun SelectAccountScreen(
|
||||
onImportSeed: () -> Unit,
|
||||
onDismissModal: () -> Unit
|
||||
) {
|
||||
// 🔥 Автоматическое скрытие клавиатуры при выходе с экрана
|
||||
HideKeyboardOnDispose()
|
||||
|
||||
val backgroundColor = if (isDarkTheme) Color(0xFF1E1E1E) else Color(0xFFFFFFFF)
|
||||
val textColor = if (isDarkTheme) Color.White else Color.Black
|
||||
val secondaryTextColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666)
|
||||
|
||||
@@ -31,7 +31,6 @@ import com.rosetta.messenger.data.DecryptedAccount
|
||||
import com.rosetta.messenger.data.EncryptedAccount
|
||||
import com.rosetta.messenger.network.ProtocolManager
|
||||
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
|
||||
import com.rosetta.messenger.ui.components.HideKeyboardOnDispose
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@@ -42,9 +41,6 @@ fun SetPasswordScreen(
|
||||
onBack: () -> Unit,
|
||||
onAccountCreated: (DecryptedAccount) -> Unit
|
||||
) {
|
||||
// 🔥 Автоматическое скрытие клавиатуры при выходе с экрана
|
||||
HideKeyboardOnDispose()
|
||||
|
||||
val themeAnimSpec = tween<Color>(durationMillis = 500, easing = CubicBezierEasing(0.4f, 0f, 0.2f, 1f))
|
||||
val backgroundColor by animateColorAsState(if (isDarkTheme) AuthBackground else AuthBackgroundLight, animationSpec = themeAnimSpec)
|
||||
val textColor by animateColorAsState(if (isDarkTheme) Color.White else Color.Black, animationSpec = themeAnimSpec)
|
||||
|
||||
@@ -42,7 +42,6 @@ import com.rosetta.messenger.network.ProtocolManager
|
||||
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
|
||||
import com.rosetta.messenger.ui.chats.getAvatarColor
|
||||
import com.rosetta.messenger.ui.chats.getAvatarText
|
||||
import com.rosetta.messenger.ui.components.HideKeyboardOnDispose
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@@ -61,9 +60,6 @@ fun UnlockScreen(
|
||||
onUnlocked: (DecryptedAccount) -> Unit,
|
||||
onSwitchAccount: () -> Unit = {}
|
||||
) {
|
||||
// 🔥 Автоматическое скрытие клавиатуры при выходе с экрана
|
||||
HideKeyboardOnDispose()
|
||||
|
||||
val themeAnimSpec = tween<Color>(durationMillis = 500, easing = CubicBezierEasing(0.4f, 0f, 0.2f, 1f))
|
||||
val backgroundColor by animateColorAsState(if (isDarkTheme) AuthBackground else AuthBackgroundLight, animationSpec = themeAnimSpec)
|
||||
val textColor by animateColorAsState(if (isDarkTheme) Color.White else Color.Black, animationSpec = themeAnimSpec)
|
||||
|
||||
@@ -67,8 +67,6 @@ import com.rosetta.messenger.network.SearchUser
|
||||
import com.rosetta.messenger.ui.components.AppleEmojiPickerPanel
|
||||
import com.rosetta.messenger.ui.components.AppleEmojiText
|
||||
import com.rosetta.messenger.ui.components.AppleEmojiTextField
|
||||
import com.rosetta.messenger.ui.components.HideKeyboardOnDispose
|
||||
import com.rosetta.messenger.ui.components.rememberKeyboardController
|
||||
import com.rosetta.messenger.ui.components.VerifiedBadge
|
||||
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
@@ -218,15 +216,11 @@ fun ChatDetailScreen(
|
||||
onUserProfileClick: () -> Unit = {},
|
||||
viewModel: ChatViewModel = viewModel()
|
||||
) {
|
||||
// 🔥 Backup: скрывает клавиатуру при dispose (если не скрыли раньше)
|
||||
HideKeyboardOnDispose()
|
||||
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
val focusManager = LocalFocusManager.current
|
||||
val clipboardManager = androidx.compose.ui.platform.LocalClipboardManager.current
|
||||
val context = LocalContext.current
|
||||
val view = LocalView.current
|
||||
val keyboard = rememberKeyboardController()
|
||||
val database = remember { com.rosetta.messenger.database.RosettaDatabase.getDatabase(context) }
|
||||
|
||||
// 🔔 Badge: количество непрочитанных сообщений из других чатов
|
||||
@@ -288,9 +282,9 @@ fun ChatDetailScreen(
|
||||
var selectedMessages by remember { mutableStateOf<Set<String>>(emptySet()) }
|
||||
val isSelectionMode = selectedMessages.isNotEmpty()
|
||||
|
||||
// 🔥 Простое закрытие
|
||||
// 🔥 Закрытие экрана
|
||||
val hideKeyboardAndBack: () -> Unit = {
|
||||
keyboard.hide()
|
||||
keyboardController?.hide()
|
||||
onBack()
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ import androidx.compose.ui.unit.sp
|
||||
import com.rosetta.messenger.data.RecentSearchesManager
|
||||
import com.rosetta.messenger.network.ProtocolState
|
||||
import com.rosetta.messenger.network.SearchUser
|
||||
import com.rosetta.messenger.ui.components.HideKeyboardOnDispose
|
||||
|
||||
// Primary Blue color
|
||||
private val PrimaryBlue = Color(0xFF54A9EB)
|
||||
@@ -44,9 +43,6 @@ fun SearchScreen(
|
||||
onBackClick: () -> Unit,
|
||||
onUserSelect: (SearchUser) -> Unit
|
||||
) {
|
||||
// 🔥 Автоматическое скрытие клавиатуры при выходе с экрана
|
||||
HideKeyboardOnDispose()
|
||||
|
||||
// Цвета ТОЧНО как в ChatsListScreen
|
||||
val backgroundColor = if (isDarkTheme) Color(0xFF1A1A1A) else Color(0xFFFFFFFF)
|
||||
val textColor = if (isDarkTheme) Color.White else Color(0xFF1a1a1a)
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
package com.rosetta.messenger.ui.components
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
|
||||
/**
|
||||
* 🎹 Контроллер клавиатуры для Rosetta Messenger
|
||||
*
|
||||
* Использует современный WindowInsetsController (API 30+) для мгновенного
|
||||
* скрытия клавиатуры. Решает проблему "залипания" клавиатуры при навигации.
|
||||
*
|
||||
* Использование:
|
||||
* ```kotlin
|
||||
* @Composable
|
||||
* fun MyScreen() {
|
||||
* // Автоматически скрывает клавиатуру при выходе с экрана
|
||||
* HideKeyboardOnDispose()
|
||||
*
|
||||
* // Или получите контроллер для ручного управления
|
||||
* val keyboard = rememberKeyboardController()
|
||||
* Button(onClick = { keyboard.hide() }) { Text("Hide") }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class KeyboardManager(
|
||||
private val view: View,
|
||||
private val context: Context
|
||||
) {
|
||||
/**
|
||||
* 🔥 Скрыть клавиатуру СИНХРОННО и МГНОВЕННО
|
||||
* Использует 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() {
|
||||
ViewCompat.getWindowInsetsController(view)?.hide(WindowInsetsCompat.Type.ime())
|
||||
}
|
||||
|
||||
/**
|
||||
* Показать клавиатуру
|
||||
*/
|
||||
fun show() {
|
||||
ViewCompat.getWindowInsetsController(view)?.show(WindowInsetsCompat.Type.ime())
|
||||
}
|
||||
|
||||
/**
|
||||
* Скрыть клавиатуру с fallback для старых устройств
|
||||
*/
|
||||
fun hideWithFallback() {
|
||||
// Сначала пробуем WindowInsetsController
|
||||
ViewCompat.getWindowInsetsController(view)?.hide(WindowInsetsCompat.Type.ime())
|
||||
|
||||
// Fallback через InputMethodManager
|
||||
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
|
||||
imm?.hideSoftInputFromWindow(view.windowToken, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверить, видна ли клавиатура
|
||||
*/
|
||||
fun isVisible(): Boolean {
|
||||
val insets = ViewCompat.getRootWindowInsets(view)
|
||||
return insets?.isVisible(WindowInsetsCompat.Type.ime()) == true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Composable для получения KeyboardManager
|
||||
*/
|
||||
@Composable
|
||||
fun rememberKeyboardController(): KeyboardManager {
|
||||
val view = LocalView.current
|
||||
val context = LocalContext.current
|
||||
return remember(view, context) { KeyboardManager(view, context) }
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔥 Автоматически скрывает клавиатуру при выходе с экрана (onDispose)
|
||||
*
|
||||
* ВАЖНО: Это backup. Основное скрытие должно быть в hideKeyboardAndBack()
|
||||
*
|
||||
* Добавьте в начало любого Composable экрана с клавиатурой:
|
||||
* ```kotlin
|
||||
* @Composable
|
||||
* fun ChatScreen() {
|
||||
* HideKeyboardOnDispose()
|
||||
* // ... остальной UI
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
@OptIn(ExperimentalComposeUiApi::class)
|
||||
@Composable
|
||||
fun HideKeyboardOnDispose() {
|
||||
val keyboard = rememberKeyboardController()
|
||||
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
// Backup скрытие при dispose
|
||||
keyboard.hideNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔥 Расширенная версия с очисткой фокуса
|
||||
*
|
||||
* Использовать когда нужно также сбросить фокус с TextField
|
||||
*/
|
||||
@OptIn(ExperimentalComposeUiApi::class)
|
||||
@Composable
|
||||
fun HideKeyboardAndClearFocusOnDispose() {
|
||||
val keyboard = rememberKeyboardController()
|
||||
val focusManager = LocalFocusManager.current
|
||||
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
// Сбрасываем фокус
|
||||
focusManager.clearFocus(force = true)
|
||||
// Мгновенное скрытие
|
||||
keyboard.hideNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension function для View - скрыть клавиатуру мгновенно
|
||||
*/
|
||||
fun View.hideKeyboardNow() {
|
||||
ViewCompat.getWindowInsetsController(this)?.hide(WindowInsetsCompat.Type.ime())
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension function для View - показать клавиатуру
|
||||
*/
|
||||
fun View.showKeyboardNow() {
|
||||
ViewCompat.getWindowInsetsController(this)?.show(WindowInsetsCompat.Type.ime())
|
||||
}
|
||||
Reference in New Issue
Block a user