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 7fd67ef..3fa35aa 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 @@ -2298,73 +2298,49 @@ private fun MessageInputBar( } // Закрытие внешней Column с border } // End of else (not blocked) - // � APPLE EMOJI PICKER - ОПТИМИЗИРОВАННАЯ АНИМАЦИЯ - // Используем slide up + alpha вместо height анимации (нет relayout = супер плавно) + + // 🔥 APPLE EMOJI PICKER - БЕЗ анимации когда клавиатура открывается if (!isBlocked) { - // Панель ВСЕГДА в DOM (pre-render), просто скрыта через offset/alpha - val shouldShow = showEmojiPicker && !isKeyboardVisible + // Высота панели: 0 если клавиатура видна или эмодзи закрыты, иначе emojiPanelHeight + val targetHeight = if (isKeyboardVisible || !showEmojiPicker) 0.dp else emojiPanelHeight - // 🚀 Animatable для максимального контроля (быстрее чем animateDpAsState) - val slideProgress = remember { Animatable(0f) } - - LaunchedEffect(shouldShow) { - if (shouldShow) { - // Открытие: быстрая spring анимация (critically damped = без пружинистости) - slideProgress.animateTo( - targetValue = 1f, - animationSpec = spring( - dampingRatio = 1f, // Critically damped - без bounce - stiffness = 800f // Высокая жёсткость = быстро - ) - ) + // Анимируем только когда клавиатура закрыта + val animatedHeight by animateDpAsState( + targetValue = targetHeight, + animationSpec = if (isKeyboardVisible) { + // Мгновенно когда клавиатура открывается + snap() } else { - // Закрытие: мгновенно если клавиатура открылась, иначе быстрая анимация - if (isKeyboardVisible) { - slideProgress.snapTo(0f) - } else { - slideProgress.animateTo( - targetValue = 0f, - animationSpec = tween(120, easing = FastOutSlowInEasing) + // Быстрая анимация (200ms) + tween(durationMillis = 200, easing = FastOutSlowInEasing) + }, + label = "EmojiPanelHeight" + ) + + // 🔥 Показываем Box только когда есть высота (НЕ занимает место когда скрыт!) + if (animatedHeight > 0.dp) { + Box( + modifier = Modifier + .fillMaxWidth() + .height(animatedHeight) + .padding(bottom = 16.dp) + ) { + if (showEmojiPicker) { + AppleEmojiPickerPanel( + isDarkTheme = isDarkTheme, + onEmojiSelected = { emoji -> + onValueChange(value + emoji) + }, + onClose = { + onToggleEmojiPicker(false) + }, + modifier = Modifier + .fillMaxWidth() + .height(emojiPanelHeight - 16.dp) ) } } } - - val panelHeightPx = with(LocalDensity.current) { (emojiPanelHeight - 16.dp).toPx() } - - Box( - modifier = Modifier - .fillMaxWidth() - .height(emojiPanelHeight) - .padding(bottom = 16.dp) - .graphicsLayer { - // 🚀 Slide up анимация через translationY (НЕ вызывает relayout!) - translationY = panelHeightPx * (1f - slideProgress.value) - alpha = slideProgress.value - // Hardware layer для плавности - if (slideProgress.value > 0f && slideProgress.value < 1f) { - shadowElevation = 0f - } - } - .clipToBounds() // Обрезаем контент при slide - ) { - // 🚀 Панель ВСЕГДА рендерится (pre-render для instant open) - // Но скрыта через alpha = 0 когда не нужна - if (slideProgress.value > 0f || shouldShow) { - AppleEmojiPickerPanel( - isDarkTheme = isDarkTheme, - onEmojiSelected = { emoji -> - onValueChange(value + emoji) - }, - onClose = { - onToggleEmojiPicker(false) - }, - modifier = Modifier - .fillMaxWidth() - .height(emojiPanelHeight - 16.dp) - ) - } - } } // End of if (!isBlocked) for emoji picker } }