feat: Optimize AppleEmojiPicker with caching and enhance UI with Liquid Glass style
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user