feat: Refactor MessageInputBar to improve emoji picker integration and UI responsiveness

This commit is contained in:
k1ngsterr1
2026-01-10 20:42:40 +05:00
parent 69ed43d26e
commit 9c0fae385c

View File

@@ -19,8 +19,10 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
@@ -107,7 +109,7 @@ fun ChatDetailScreen(
// Кастомный TopAppBar для чата
Surface(
color = backgroundColor,
shadowElevation = 4.dp
shadowElevation = 0.dp
) {
Row(
modifier = Modifier
@@ -209,7 +211,6 @@ fun ChatDetailScreen(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
.imePadding() // Весь контент поднимается с клавиатурой
) {
// Список сообщений
Box(
@@ -407,29 +408,23 @@ private fun MessageInputBar(
// Состояние эмодзи пикера
var showEmojiPicker by remember { mutableStateOf(false) }
val keyboardController = LocalSoftwareKeyboardController.current
// Скрываем клавиатуру когда открыт эмодзи пикер
LaunchedEffect(showEmojiPicker) {
if (showEmojiPicker) {
keyboardController?.hide()
}
}
val focusManager = LocalFocusManager.current
// Цвета для glass morphism эффекта
val glassBackground = if (isDarkTheme)
Color(0xFF1A1A1A).copy(alpha = 0.85f)
Color(0xFF1A1A1A).copy(alpha = 0.95f)
else
Color(0xFFFFFFFF).copy(alpha = 0.9f)
Color(0xFFFFFFFF).copy(alpha = 0.95f)
val inputGlass = if (isDarkTheme)
Color(0xFF2C2C2E).copy(alpha = 0.7f)
Color(0xFF2C2C2E).copy(alpha = 0.8f)
else
Color(0xFFF2F2F7).copy(alpha = 0.85f)
Color(0xFFF2F2F7).copy(alpha = 0.9f)
val inputBorder = if (isDarkTheme)
Color(0xFFFFFFFF).copy(alpha = 0.15f)
Color(0xFFFFFFFF).copy(alpha = 0.12f)
else
Color(0xFF000000).copy(alpha = 0.08f)
Color(0xFF000000).copy(alpha = 0.06f)
val iconColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666)
@@ -458,33 +453,11 @@ private fun MessageInputBar(
)
Column(
modifier = Modifier.fillMaxWidth()
modifier = Modifier
.fillMaxWidth()
.imePadding() // Только инпут поднимается с клавиатурой
) {
// Эмодзи пикер (показывается над инпутом)
AnimatedVisibility(
visible = showEmojiPicker,
enter = slideInVertically(
initialOffsetY = { it },
animationSpec = spring(
dampingRatio = Spring.DampingRatioMediumBouncy,
stiffness = Spring.StiffnessMediumLow
)
) + fadeIn(),
exit = slideOutVertically(
targetOffsetY = { it },
animationSpec = tween(200)
) + fadeOut()
) {
EmojiPickerPanel(
isDarkTheme = isDarkTheme,
onEmojiSelected = { emoji ->
onValueChange(value + emoji)
},
onClose = { showEmojiPicker = false }
)
}
// Основной контейнер
// Основной контейнер инпута
Box(
modifier = Modifier
.fillMaxWidth()
@@ -493,9 +466,7 @@ private fun MessageInputBar(
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 12.dp, vertical = 8.dp)
.padding(bottom = 4.dp)
.navigationBarsPadding(),
.padding(horizontal = 12.dp, vertical = 8.dp),
verticalAlignment = Alignment.Bottom
) {
// Единый стеклянный контейнер для всего инпута
@@ -514,9 +485,9 @@ private fun MessageInputBar(
.background(
brush = Brush.verticalGradient(
colors = listOf(
Color.White.copy(alpha = if (isDarkTheme) 0.06f else 0.4f),
Color.White.copy(alpha = if (isDarkTheme) 0.05f else 0.3f),
Color.Transparent,
Color.Black.copy(alpha = if (isDarkTheme) 0.03f else 0.02f)
Color.Black.copy(alpha = if (isDarkTheme) 0.02f else 0.01f)
),
startY = 0f,
endY = 80f
@@ -532,7 +503,14 @@ private fun MessageInputBar(
) {
// Кнопка смайликов (слева внутри инпута)
IconButton(
onClick = { showEmojiPicker = !showEmojiPicker },
onClick = {
if (showEmojiPicker) {
showEmojiPicker = false
} else {
keyboardController?.hide()
showEmojiPicker = true
}
},
modifier = Modifier.size(36.dp)
) {
Icon(
@@ -547,7 +525,10 @@ private fun MessageInputBar(
Box(
modifier = Modifier
.weight(1f)
.padding(vertical = 8.dp),
.padding(vertical = 8.dp)
.clickable {
showEmojiPicker = false
},
contentAlignment = Alignment.CenterStart
) {
BasicTextField(
@@ -558,7 +539,13 @@ private fun MessageInputBar(
fontSize = 16.sp
),
cursorBrush = SolidColor(PrimaryBlue),
modifier = Modifier.fillMaxWidth(),
modifier = Modifier
.fillMaxWidth()
.onFocusChanged { focusState ->
if (focusState.isFocused) {
showEmojiPicker = false
}
},
maxLines = 5,
decorationBox = { innerTextField ->
Box(
@@ -645,6 +632,35 @@ private fun MessageInputBar(
}
}
}
// Эмодзи пикер (показывается под инпутом, заменяя клавиатуру)
AnimatedVisibility(
visible = showEmojiPicker,
enter = expandVertically(
expandFrom = Alignment.Top,
animationSpec = spring(
dampingRatio = Spring.DampingRatioMediumBouncy,
stiffness = Spring.StiffnessMediumLow
)
) + fadeIn(animationSpec = tween(150)),
exit = shrinkVertically(
shrinkTowards = Alignment.Top,
animationSpec = tween(200)
) + fadeOut(animationSpec = tween(100))
) {
EmojiPickerPanel(
isDarkTheme = isDarkTheme,
onEmojiSelected = { emoji ->
onValueChange(value + emoji)
},
onClose = { showEmojiPicker = false }
)
}
// Spacer для navigation bar когда эмодзи пикер НЕ открыт
if (!showEmojiPicker) {
Spacer(modifier = Modifier.navigationBarsPadding())
}
}
}