feat: Update ChatDetailScreen to use inputBackgroundColor for dropdown menus and menu items
This commit is contained in:
@@ -28,6 +28,7 @@ import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -71,6 +72,7 @@ import com.rosetta.messenger.ui.components.AppleEmojiTextField
|
||||
import com.rosetta.messenger.ui.components.VerifiedBadge
|
||||
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
|
||||
import app.rosette.android.ui.keyboard.rememberKeyboardTransitionCoordinator
|
||||
import app.rosette.android.ui.keyboard.KeyboardTransitionCoordinator
|
||||
import app.rosette.android.ui.keyboard.AnimatedKeyboardTransition
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.content.Context
|
||||
@@ -219,6 +221,8 @@ fun ChatDetailScreen(
|
||||
onUserProfileClick: () -> Unit = {},
|
||||
viewModel: ChatViewModel = viewModel()
|
||||
) {
|
||||
// 🔥 ОПТИМИЗАЦИЯ: Убрано логирование из композиции чтобы не засорять logcat
|
||||
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
val focusManager = LocalFocusManager.current
|
||||
val clipboardManager = androidx.compose.ui.platform.LocalClipboardManager.current
|
||||
@@ -247,26 +251,36 @@ fun ChatDetailScreen(
|
||||
|
||||
// 🔥 Emoji picker state (поднят из MessageInputBar для KeyboardAvoidingView)
|
||||
var showEmojiPicker by remember { mutableStateOf(false) }
|
||||
// Высота эмодзи панели - берём высоту клавиатуры если она открыта, иначе 280dp
|
||||
val imeInsets = WindowInsets.ime
|
||||
val imeHeight = with(density) { imeInsets.getBottom(density).toDp() }
|
||||
val isKeyboardVisible = imeHeight > 50.dp
|
||||
|
||||
// 🔥 Запоминаем высоту клавиатуры когда она открыта
|
||||
var savedKeyboardHeight by remember { mutableStateOf(280.dp) }
|
||||
LaunchedEffect(imeHeight) {
|
||||
if (imeHeight > 50.dp) {
|
||||
savedKeyboardHeight = imeHeight
|
||||
// 🎯 Координатор плавных переходов клавиатуры (Telegram-style)
|
||||
val coordinator = rememberKeyboardTransitionCoordinator()
|
||||
|
||||
// 🔥 ОПТИМИЗАЦИЯ: НЕ читаем imeHeight напрямую в композиции!
|
||||
// Используем snapshotFlow чтобы избежать рекомпозиции на каждый пиксель анимации
|
||||
val imeInsets = WindowInsets.ime
|
||||
|
||||
// 🔥 Синхронизируем coordinator с IME высотой через snapshotFlow (БЕЗ рекомпозиции!)
|
||||
LaunchedEffect(Unit) {
|
||||
snapshotFlow {
|
||||
with(density) { imeInsets.getBottom(density).toDp() }
|
||||
}.collect { currentImeHeight ->
|
||||
coordinator.updateKeyboardHeight(currentImeHeight)
|
||||
if (currentImeHeight > 100.dp) {
|
||||
coordinator.syncHeights()
|
||||
}
|
||||
}
|
||||
}
|
||||
// 🔥 Высота панели эмодзи = сохранённая высота клавиатуры (минимум 280.dp)
|
||||
val emojiPanelHeight = maxOf(savedKeyboardHeight, 280.dp)
|
||||
|
||||
// 🔥 Флаг видимости панели эмодзи (тот же что в MessageInputBar) - единый источник правды
|
||||
val isEmojiPanelVisible = showEmojiPicker && !isKeyboardVisible
|
||||
|
||||
// <20> Простой отступ без анимации - AnimatedVisibility сама анимирует
|
||||
val emojiPanelPadding = if (isEmojiPanelVisible) emojiPanelHeight else 0.dp
|
||||
// 🔥 Инициализируем высоту emoji панели из сохранённой высоты клавиатуры
|
||||
LaunchedEffect(Unit) {
|
||||
val savedHeightPx = com.rosetta.messenger.ui.components.KeyboardHeightProvider.getSavedKeyboardHeight(context)
|
||||
if (savedHeightPx > 0) {
|
||||
val savedHeightDp = with(density) { savedHeightPx.toDp() }
|
||||
coordinator.initializeEmojiHeight(savedHeightDp)
|
||||
} else {
|
||||
coordinator.initializeEmojiHeight(280.dp) // fallback
|
||||
}
|
||||
}
|
||||
|
||||
// 🔥 Reply/Forward state
|
||||
val replyMessages by viewModel.replyMessages.collectAsState()
|
||||
@@ -736,7 +750,7 @@ fun ChatDetailScreen(
|
||||
// Выпадающее меню - чистый дизайн без артефактов
|
||||
MaterialTheme(
|
||||
colorScheme = MaterialTheme.colorScheme.copy(
|
||||
surface = if (isDarkTheme) Color(0xFF1C1C1E) else Color.White
|
||||
surface = inputBackgroundColor
|
||||
)
|
||||
) {
|
||||
DropdownMenu(
|
||||
@@ -745,7 +759,7 @@ fun ChatDetailScreen(
|
||||
modifier = Modifier
|
||||
.width(220.dp)
|
||||
.background(
|
||||
color = if (isDarkTheme) Color(0xFF1C1C1E) else Color.White,
|
||||
color = inputBackgroundColor,
|
||||
shape = RoundedCornerShape(16.dp)
|
||||
)
|
||||
) {
|
||||
@@ -775,7 +789,8 @@ fun ChatDetailScreen(
|
||||
showMenu = false
|
||||
showDeleteConfirm = true
|
||||
},
|
||||
modifier = Modifier.padding(horizontal = 8.dp),
|
||||
modifier = Modifier.padding(horizontal = 8.dp)
|
||||
.background(inputBackgroundColor),
|
||||
colors = MenuDefaults.itemColors(
|
||||
textColor = Color(0xFFE53935)
|
||||
)
|
||||
@@ -822,7 +837,8 @@ fun ChatDetailScreen(
|
||||
showBlockConfirm = true
|
||||
}
|
||||
},
|
||||
modifier = Modifier.padding(horizontal = 8.dp),
|
||||
modifier = Modifier.padding(horizontal = 8.dp)
|
||||
.background(inputBackgroundColor),
|
||||
colors = MenuDefaults.itemColors(
|
||||
textColor = PrimaryBlue
|
||||
)
|
||||
@@ -863,7 +879,8 @@ fun ChatDetailScreen(
|
||||
showMenu = false
|
||||
showLogs = true
|
||||
},
|
||||
modifier = Modifier.padding(horizontal = 8.dp),
|
||||
modifier = Modifier.padding(horizontal = 8.dp)
|
||||
.background(inputBackgroundColor),
|
||||
colors = MenuDefaults.itemColors(
|
||||
textColor = textColor
|
||||
)
|
||||
@@ -937,7 +954,9 @@ fun ChatDetailScreen(
|
||||
showEmojiPicker = showEmojiPicker,
|
||||
onToggleEmojiPicker = { showEmojiPicker = it },
|
||||
// Focus requester для автофокуса при reply
|
||||
focusRequester = inputFocusRequester
|
||||
focusRequester = inputFocusRequester,
|
||||
// Coordinator для плавных переходов
|
||||
coordinator = coordinator
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1978,8 +1997,12 @@ private fun MessageInputBar(
|
||||
showEmojiPicker: Boolean = false,
|
||||
onToggleEmojiPicker: (Boolean) -> Unit = {},
|
||||
// Focus requester для автофокуса при reply
|
||||
focusRequester: FocusRequester? = null
|
||||
focusRequester: FocusRequester? = null,
|
||||
// Coordinator для плавных переходов клавиатуры
|
||||
coordinator: KeyboardTransitionCoordinator
|
||||
) {
|
||||
// 🔥 ОПТИМИЗАЦИЯ: Убрано логирование чтобы не засорять logcat
|
||||
|
||||
val hasReply = replyMessages.isNotEmpty()
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
val focusManager = LocalFocusManager.current
|
||||
@@ -1991,9 +2014,6 @@ private fun MessageInputBar(
|
||||
val view = LocalView.current
|
||||
val density = LocalDensity.current
|
||||
|
||||
// 🎯 Координатор плавных переходов клавиатуры (Telegram-style)
|
||||
val coordinator = rememberKeyboardTransitionCoordinator()
|
||||
|
||||
// 🔥 Ссылка на EditText для программного фокуса
|
||||
var editTextView by remember { mutableStateOf<com.rosetta.messenger.ui.components.AppleEmojiEditTextView?>(null) }
|
||||
|
||||
@@ -2021,56 +2041,47 @@ private fun MessageInputBar(
|
||||
}
|
||||
}
|
||||
|
||||
// 🔥 Отслеживаем высоту клавиатуры (Telegram-style)
|
||||
// 🔥 ОПТИМИЗАЦИЯ: НЕ читаем imeHeight напрямую - это вызывает рекомпозицию!
|
||||
val imeInsets = WindowInsets.ime
|
||||
val imeHeight = with(density) { imeInsets.getBottom(density).toDp() }
|
||||
val isKeyboardVisible = imeHeight > 50.dp // 🔥 Согласованный порог с ChatDetailScreen
|
||||
|
||||
// 🔥 Флаг "клавиатура в процессе анимации"
|
||||
var isKeyboardAnimating by remember { mutableStateOf(false) }
|
||||
// 🔥 Флаг "клавиатура видна" - обновляется через snapshotFlow, НЕ вызывает рекомпозицию
|
||||
var isKeyboardVisible by remember { mutableStateOf(false) }
|
||||
var lastStableKeyboardHeight by remember { mutableStateOf(0.dp) }
|
||||
|
||||
// 🔥 Логирование изменений высоты клавиатуры + обновление coordinator
|
||||
LaunchedEffect(imeHeight) {
|
||||
android.util.Log.d("KeyboardHeight", "═══════════════════════════════════════════════════════")
|
||||
android.util.Log.d("KeyboardHeight", "📊 IME height changed: $imeHeight")
|
||||
android.util.Log.d("KeyboardHeight", " isKeyboardVisible=$isKeyboardVisible")
|
||||
android.util.Log.d("KeyboardHeight", " showEmojiPicker=$showEmojiPicker")
|
||||
android.util.Log.d("KeyboardHeight", " isKeyboardAnimating=$isKeyboardAnimating")
|
||||
|
||||
// Обновляем coordinator с актуальной высотой клавиатуры
|
||||
android.util.Log.d("KeyboardHeight", "🔄 Updating coordinator...")
|
||||
coordinator.updateKeyboardHeight(imeHeight)
|
||||
|
||||
// Синхронизируем высоту emoji с клавиатурой
|
||||
if (imeHeight > 100.dp) {
|
||||
android.util.Log.d("KeyboardHeight", "🔄 Syncing heights...")
|
||||
coordinator.syncHeights()
|
||||
// 🔥 Обновляем coordinator через snapshotFlow (БЕЗ рекомпозиции!)
|
||||
LaunchedEffect(Unit) {
|
||||
snapshotFlow {
|
||||
with(density) { imeInsets.getBottom(density).toDp() }
|
||||
}.collect { currentImeHeight ->
|
||||
// Обновляем флаг видимости (это НЕ вызывает рекомпозицию напрямую)
|
||||
isKeyboardVisible = currentImeHeight > 50.dp
|
||||
|
||||
// Обновляем coordinator
|
||||
coordinator.updateKeyboardHeight(currentImeHeight)
|
||||
if (currentImeHeight > 100.dp) {
|
||||
coordinator.syncHeights()
|
||||
lastStableKeyboardHeight = currentImeHeight
|
||||
}
|
||||
}
|
||||
android.util.Log.d("KeyboardHeight", "✅ Coordinator updated")
|
||||
}
|
||||
|
||||
// 🔥 Запоминаем высоту клавиатуры когда она открыта (Telegram-style with SharedPreferences)
|
||||
LaunchedEffect(Unit) {
|
||||
// Загружаем сохранённую высоту при старте
|
||||
val savedHeightPx = com.rosetta.messenger.ui.components.KeyboardHeightProvider.getSavedKeyboardHeight(context)
|
||||
android.util.Log.d("KeyboardHeight", "📱 MessageInputBar initialized, loaded height: ${savedHeightPx}px")
|
||||
com.rosetta.messenger.ui.components.KeyboardHeightProvider.getSavedKeyboardHeight(context)
|
||||
}
|
||||
|
||||
// 🔥 Сохраняем высоту ТОЛЬКО когда клавиатура стабильна
|
||||
// Используем отдельный LaunchedEffect который НЕ реагирует на каждое изменение
|
||||
LaunchedEffect(isKeyboardVisible, showEmojiPicker) {
|
||||
// Если клавиатура стала видимой и emoji закрыт
|
||||
if (isKeyboardVisible && !showEmojiPicker && !isKeyboardAnimating) {
|
||||
if (isKeyboardVisible && !showEmojiPicker) {
|
||||
// Ждем стабилизации
|
||||
isKeyboardAnimating = true
|
||||
kotlinx.coroutines.delay(300) // Анимация клавиатуры ~250ms
|
||||
isKeyboardAnimating = false
|
||||
kotlinx.coroutines.delay(350) // Анимация клавиатуры ~300ms
|
||||
|
||||
// Сохраняем только если всё еще видна и emoji закрыт
|
||||
if (isKeyboardVisible && !showEmojiPicker && imeHeight > 300.dp) {
|
||||
val heightPx = with(density) { imeHeight.toPx().toInt() }
|
||||
if (isKeyboardVisible && !showEmojiPicker && lastStableKeyboardHeight > 300.dp) {
|
||||
val heightPx = with(density) { lastStableKeyboardHeight.toPx().toInt() }
|
||||
com.rosetta.messenger.ui.components.KeyboardHeightProvider.saveKeyboardHeight(context, heightPx)
|
||||
android.util.Log.d("KeyboardHeight", "✅ Stable keyboard height saved: ${imeHeight} (${heightPx}px)")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2343,8 +2354,6 @@ private fun MessageInputBar(
|
||||
android.util.Log.d("EmojiPicker", "🔄 TextField focused while emoji open → closing emoji")
|
||||
android.util.Log.d("EmojiPicker", " 📞 Calling onToggleEmojiPicker(false)...")
|
||||
onToggleEmojiPicker(false)
|
||||
android.util.Log.d("EmojiPicker", " 📞 Setting coordinator.isEmojiVisible = false...")
|
||||
coordinator.isEmojiVisible = false
|
||||
android.util.Log.d("EmojiPicker", " ✅ Emoji close requested")
|
||||
} else if (hasFocus && !showEmojiPicker) {
|
||||
android.util.Log.d("EmojiPicker", "⌨️ TextField focused with emoji closed → normal keyboard behavior")
|
||||
|
||||
Reference in New Issue
Block a user