feat: Refactor MessageInputBar to improve emoji picker integration and UI responsiveness
This commit is contained in:
@@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user