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.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
|
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
|
||||||
import com.rosetta.messenger.ui.components.HideKeyboardOnDispose
|
|
||||||
|
|
||||||
// Beautiful solid colors that fit the theme
|
// Beautiful solid colors that fit the theme
|
||||||
private val wordColors = listOf(
|
private val wordColors = listOf(
|
||||||
@@ -51,9 +50,6 @@ fun ConfirmSeedPhraseScreen(
|
|||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
onConfirmed: () -> Unit
|
onConfirmed: () -> Unit
|
||||||
) {
|
) {
|
||||||
// 🔥 Автоматическое скрытие клавиатуры при выходе с экрана
|
|
||||||
HideKeyboardOnDispose()
|
|
||||||
|
|
||||||
val backgroundColor = if (isDarkTheme) Color(0xFF1E1E1E) else Color(0xFFFFFFFF)
|
val backgroundColor = if (isDarkTheme) Color(0xFF1E1E1E) else Color(0xFFFFFFFF)
|
||||||
val textColor = if (isDarkTheme) Color.White else Color.Black
|
val textColor = if (isDarkTheme) Color.White else Color.Black
|
||||||
val secondaryTextColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666)
|
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.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
|
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
|
||||||
import com.rosetta.messenger.ui.components.HideKeyboardOnDispose
|
|
||||||
|
|
||||||
data class AccountInfo(
|
data class AccountInfo(
|
||||||
val id: String,
|
val id: String,
|
||||||
@@ -64,9 +63,6 @@ fun SelectAccountScreen(
|
|||||||
onImportSeed: () -> Unit,
|
onImportSeed: () -> Unit,
|
||||||
onDismissModal: () -> Unit
|
onDismissModal: () -> Unit
|
||||||
) {
|
) {
|
||||||
// 🔥 Автоматическое скрытие клавиатуры при выходе с экрана
|
|
||||||
HideKeyboardOnDispose()
|
|
||||||
|
|
||||||
val backgroundColor = if (isDarkTheme) Color(0xFF1E1E1E) else Color(0xFFFFFFFF)
|
val backgroundColor = if (isDarkTheme) Color(0xFF1E1E1E) else Color(0xFFFFFFFF)
|
||||||
val textColor = if (isDarkTheme) Color.White else Color.Black
|
val textColor = if (isDarkTheme) Color.White else Color.Black
|
||||||
val secondaryTextColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666)
|
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.data.EncryptedAccount
|
||||||
import com.rosetta.messenger.network.ProtocolManager
|
import com.rosetta.messenger.network.ProtocolManager
|
||||||
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
|
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
|
||||||
import com.rosetta.messenger.ui.components.HideKeyboardOnDispose
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@@ -42,9 +41,6 @@ fun SetPasswordScreen(
|
|||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
onAccountCreated: (DecryptedAccount) -> Unit
|
onAccountCreated: (DecryptedAccount) -> Unit
|
||||||
) {
|
) {
|
||||||
// 🔥 Автоматическое скрытие клавиатуры при выходе с экрана
|
|
||||||
HideKeyboardOnDispose()
|
|
||||||
|
|
||||||
val themeAnimSpec = tween<Color>(durationMillis = 500, easing = CubicBezierEasing(0.4f, 0f, 0.2f, 1f))
|
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 backgroundColor by animateColorAsState(if (isDarkTheme) AuthBackground else AuthBackgroundLight, animationSpec = themeAnimSpec)
|
||||||
val textColor by animateColorAsState(if (isDarkTheme) Color.White else Color.Black, 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.onboarding.PrimaryBlue
|
||||||
import com.rosetta.messenger.ui.chats.getAvatarColor
|
import com.rosetta.messenger.ui.chats.getAvatarColor
|
||||||
import com.rosetta.messenger.ui.chats.getAvatarText
|
import com.rosetta.messenger.ui.chats.getAvatarText
|
||||||
import com.rosetta.messenger.ui.components.HideKeyboardOnDispose
|
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@@ -61,9 +60,6 @@ fun UnlockScreen(
|
|||||||
onUnlocked: (DecryptedAccount) -> Unit,
|
onUnlocked: (DecryptedAccount) -> Unit,
|
||||||
onSwitchAccount: () -> Unit = {}
|
onSwitchAccount: () -> Unit = {}
|
||||||
) {
|
) {
|
||||||
// 🔥 Автоматическое скрытие клавиатуры при выходе с экрана
|
|
||||||
HideKeyboardOnDispose()
|
|
||||||
|
|
||||||
val themeAnimSpec = tween<Color>(durationMillis = 500, easing = CubicBezierEasing(0.4f, 0f, 0.2f, 1f))
|
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 backgroundColor by animateColorAsState(if (isDarkTheme) AuthBackground else AuthBackgroundLight, animationSpec = themeAnimSpec)
|
||||||
val textColor by animateColorAsState(if (isDarkTheme) Color.White else Color.Black, 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.AppleEmojiPickerPanel
|
||||||
import com.rosetta.messenger.ui.components.AppleEmojiText
|
import com.rosetta.messenger.ui.components.AppleEmojiText
|
||||||
import com.rosetta.messenger.ui.components.AppleEmojiTextField
|
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.components.VerifiedBadge
|
||||||
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
|
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
@@ -218,15 +216,11 @@ fun ChatDetailScreen(
|
|||||||
onUserProfileClick: () -> Unit = {},
|
onUserProfileClick: () -> Unit = {},
|
||||||
viewModel: ChatViewModel = viewModel()
|
viewModel: ChatViewModel = viewModel()
|
||||||
) {
|
) {
|
||||||
// 🔥 Backup: скрывает клавиатуру при dispose (если не скрыли раньше)
|
|
||||||
HideKeyboardOnDispose()
|
|
||||||
|
|
||||||
val keyboardController = LocalSoftwareKeyboardController.current
|
val keyboardController = LocalSoftwareKeyboardController.current
|
||||||
val focusManager = LocalFocusManager.current
|
val focusManager = LocalFocusManager.current
|
||||||
val clipboardManager = androidx.compose.ui.platform.LocalClipboardManager.current
|
val clipboardManager = androidx.compose.ui.platform.LocalClipboardManager.current
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val view = LocalView.current
|
val view = LocalView.current
|
||||||
val keyboard = rememberKeyboardController()
|
|
||||||
val database = remember { com.rosetta.messenger.database.RosettaDatabase.getDatabase(context) }
|
val database = remember { com.rosetta.messenger.database.RosettaDatabase.getDatabase(context) }
|
||||||
|
|
||||||
// 🔔 Badge: количество непрочитанных сообщений из других чатов
|
// 🔔 Badge: количество непрочитанных сообщений из других чатов
|
||||||
@@ -288,9 +282,9 @@ fun ChatDetailScreen(
|
|||||||
var selectedMessages by remember { mutableStateOf<Set<String>>(emptySet()) }
|
var selectedMessages by remember { mutableStateOf<Set<String>>(emptySet()) }
|
||||||
val isSelectionMode = selectedMessages.isNotEmpty()
|
val isSelectionMode = selectedMessages.isNotEmpty()
|
||||||
|
|
||||||
// 🔥 Простое закрытие
|
// 🔥 Закрытие экрана
|
||||||
val hideKeyboardAndBack: () -> Unit = {
|
val hideKeyboardAndBack: () -> Unit = {
|
||||||
keyboard.hide()
|
keyboardController?.hide()
|
||||||
onBack()
|
onBack()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import androidx.compose.ui.unit.sp
|
|||||||
import com.rosetta.messenger.data.RecentSearchesManager
|
import com.rosetta.messenger.data.RecentSearchesManager
|
||||||
import com.rosetta.messenger.network.ProtocolState
|
import com.rosetta.messenger.network.ProtocolState
|
||||||
import com.rosetta.messenger.network.SearchUser
|
import com.rosetta.messenger.network.SearchUser
|
||||||
import com.rosetta.messenger.ui.components.HideKeyboardOnDispose
|
|
||||||
|
|
||||||
// Primary Blue color
|
// Primary Blue color
|
||||||
private val PrimaryBlue = Color(0xFF54A9EB)
|
private val PrimaryBlue = Color(0xFF54A9EB)
|
||||||
@@ -44,9 +43,6 @@ fun SearchScreen(
|
|||||||
onBackClick: () -> Unit,
|
onBackClick: () -> Unit,
|
||||||
onUserSelect: (SearchUser) -> Unit
|
onUserSelect: (SearchUser) -> Unit
|
||||||
) {
|
) {
|
||||||
// 🔥 Автоматическое скрытие клавиатуры при выходе с экрана
|
|
||||||
HideKeyboardOnDispose()
|
|
||||||
|
|
||||||
// Цвета ТОЧНО как в ChatsListScreen
|
// Цвета ТОЧНО как в ChatsListScreen
|
||||||
val backgroundColor = if (isDarkTheme) Color(0xFF1A1A1A) else Color(0xFFFFFFFF)
|
val backgroundColor = if (isDarkTheme) Color(0xFF1A1A1A) else Color(0xFFFFFFFF)
|
||||||
val textColor = if (isDarkTheme) Color.White else Color(0xFF1a1a1a)
|
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