feat: Optimize AppleEmojiPicker with caching and enhance UI with Liquid Glass style

This commit is contained in:
k1ngsterr1
2026-01-12 02:09:56 +05:00
parent 5f348f329e
commit 7bac22850e
2 changed files with 214 additions and 62 deletions

View File

@@ -56,8 +56,11 @@ import com.rosetta.messenger.ui.components.VerifiedBadge
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
import android.view.inputmethod.InputMethodManager
import android.content.Context
import android.graphics.Rect
import android.view.ViewTreeObserver
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.platform.LocalDensity
import java.text.SimpleDateFormat
import java.util.*
import kotlinx.coroutines.delay
@@ -284,14 +287,41 @@ fun ChatDetailScreen(
// 🔥 Обработка системной кнопки назад
BackHandler { hideKeyboardAndBack() }
// 🔥 Cleanup при выходе из экрана
DisposableEffect(Unit) {
// 🔥 Ручное отслеживание высоты клавиатуры
val view = LocalView.current
val density = LocalDensity.current
var keyboardHeight by remember { mutableStateOf(0.dp) }
DisposableEffect(view) {
val listener = ViewTreeObserver.OnGlobalLayoutListener {
val rect = Rect()
view.getWindowVisibleDisplayFrame(rect)
val screenHeight = view.rootView.height
val keyboardHeightPx = screenHeight - rect.bottom
// Учитываем navigation bar (если клавиатура меньше 150px - это не клавиатура)
val actualKeyboardHeight = if (keyboardHeightPx > 150) keyboardHeightPx else 0
keyboardHeight = with(density) { actualKeyboardHeight.toDp() }
}
view.viewTreeObserver.addOnGlobalLayoutListener(listener)
onDispose {
view.viewTreeObserver.removeOnGlobalLayoutListener(listener)
focusManager.clearFocus()
keyboardController?.hide()
}
}
// Автоматический скролл вниз при появлении клавиатуры
LaunchedEffect(keyboardHeight) {
if (keyboardHeight > 0.dp && messages.isNotEmpty()) {
delay(50)
listState.animateScrollToItem(0)
}
}
// Инициализируем ViewModel с ключами и открываем диалог
LaunchedEffect(user.publicKey) {
@@ -549,15 +579,21 @@ fun ChatDetailScreen(
},
containerColor = Color.Transparent
) { paddingValues ->
// 🔥 Column с imePadding - весь контент поднимается с клавиатурой
Column(
// 🔥 Box с фиксированным Input внизу и сообщениями выше
Box(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
.imePadding() // Контент поднимается над клавиатурой
) {
// Список сообщений - занимает всё доступное пространство
Box(modifier = Modifier.weight(1f).fillMaxWidth()) {
// Список сообщений - занимает всё пространство, с padding снизу для input и клавиатуры
val inputBarHeight = 60.dp
val bottomPadding = inputBarHeight + keyboardHeight
Box(
modifier = Modifier
.fillMaxSize()
.padding(bottom = bottomPadding)
) {
if (messages.isEmpty()) {
// Пустое состояние
Column(
@@ -723,35 +759,42 @@ fun ChatDetailScreen(
)
}
}
}
} // Закрытие внутреннего Box для сообщений
// 🔥 INPUT BAR - внизу Column, автоматически над клавиатурой
MessageInputBar(
value = inputText,
onValueChange = {
viewModel.updateInputText(it)
// Отправляем индикатор печатания
if (it.isNotEmpty() && !isSavedMessages) {
viewModel.sendTypingIndicator()
}
},
onSend = {
// Скрываем кнопку scroll на время отправки
isSendingMessage = true
viewModel.sendMessage()
// Скроллим к новому сообщению
scope.launch {
delay(100)
listState.animateScrollToItem(0)
delay(300) // Ждём завершения анимации
isSendingMessage = false
}
},
isDarkTheme = isDarkTheme,
backgroundColor = inputBackgroundColor,
textColor = textColor,
placeholderColor = secondaryTextColor
)
// 🔥 INPUT BAR - фиксированный элемент внизу экрана
Box(
modifier = Modifier
.align(Alignment.BottomCenter)
.fillMaxWidth()
.padding(bottom = keyboardHeight) // Ручной padding над клавиатурой
) {
MessageInputBar(
value = inputText,
onValueChange = {
viewModel.updateInputText(it)
// Отправляем индикатор печатания
if (it.isNotEmpty() && !isSavedMessages) {
viewModel.sendTypingIndicator()
}
},
onSend = {
// Скрываем кнопку scroll на время отправки
isSendingMessage = true
viewModel.sendMessage()
// Скроллим к новому сообщению
scope.launch {
delay(100)
listState.animateScrollToItem(0)
delay(300) // Ждём завершения анимации
isSendingMessage = false
}
},
isDarkTheme = isDarkTheme,
backgroundColor = inputBackgroundColor,
textColor = textColor,
placeholderColor = secondaryTextColor
)
}
}
}
} // Закрытие Box с fade-in