diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/TelegramInputBar.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/TelegramInputBar.kt deleted file mode 100644 index 5e8204f..0000000 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/TelegramInputBar.kt +++ /dev/null @@ -1,506 +0,0 @@ -package com.rosetta.messenger.ui.chats - -import androidx.compose.animation.* -import androidx.compose.animation.core.* -import androidx.compose.foundation.* -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.* -import androidx.compose.material3.* -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.ExperimentalComposeUiApi -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.graphicsLayer -import androidx.compose.ui.platform.LocalFocusManager -import androidx.compose.ui.platform.LocalSoftwareKeyboardController -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.rosetta.messenger.ui.components.AppleEmojiPickerPanel -import com.rosetta.messenger.ui.components.AppleEmojiTextField -// Using TelegramEasing from ChatDetailScreen.kt - -// Attach menu items -data class AttachMenuItem( - val icon: @Composable () -> Unit, - val label: String, - val color: Color, - val onClick: () -> Unit -) - -/** - * Telegram-style input bar - exact 1:1 replica - * Based on ChatActivityEnterView.java from Telegram Android source - */ -@OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class) -@Composable -fun TelegramInputBar( - value: String, - onValueChange: (String) -> Unit, - onSend: () -> Unit, - isDarkTheme: Boolean, - onAttachPhoto: () -> Unit = {}, - onAttachFile: () -> Unit = {}, - onAttachLocation: () -> Unit = {}, - onAttachContact: () -> Unit = {}, - modifier: Modifier = Modifier -) { - val interactionSource = remember { MutableInteractionSource() } - - // Focus & keyboard management - val focusManager = LocalFocusManager.current - val keyboardController = LocalSoftwareKeyboardController.current - - - // States - var showEmojiPicker by remember { mutableStateOf(false) } - var showAttachMenu by remember { mutableStateOf(false) } - var isKeyboardVisible by remember { mutableStateOf(false) } - - // Can send message - val canSend = value.isNotBlank() - - // Send button animation (250ms CubicBezier like Telegram) - val sendScale by animateFloatAsState( - targetValue = if (canSend) 1f else 0.1f, - animationSpec = tween(durationMillis = 250, easing = TelegramEasing), - label = "send scale" - ) - - val sendAlpha by animateFloatAsState( - targetValue = if (canSend) 1f else 0f, - animationSpec = tween(durationMillis = 250, easing = TelegramEasing), - label = "send alpha" - ) - - // Mic button animation - val micScale by animateFloatAsState( - targetValue = if (canSend) 0.1f else 1f, - animationSpec = tween(durationMillis = 250, easing = TelegramEasing), - label = "mic scale" - ) - - val micAlpha by animateFloatAsState( - targetValue = if (canSend) 0f else 1f, - animationSpec = tween(durationMillis = 250, easing = TelegramEasing), - label = "mic alpha" - ) - - // Attach button rotation animation (like Telegram) - val attachRotation by animateFloatAsState( - targetValue = if (showAttachMenu) 45f else 0f, - animationSpec = tween(durationMillis = 200, easing = TelegramEasing), - label = "attach rotation" - ) - - // Our custom colors (not Telegram's gray) - val primaryBlue = Color(0xFF007AFF) - val inputPanelBackground = if (isDarkTheme) Color(0xFF212121) else Color(0xFFFFFFFF) - val iconColor = if (isDarkTheme) Color.White.copy(alpha = 0.7f) else Color(0xFF8E8E93) - val activeIconColor = primaryBlue - val textColor = if (isDarkTheme) Color.White else Color.Black - val hintColor = if (isDarkTheme) Color.White.copy(alpha = 0.4f) else Color(0xFF999999) - val dividerColor = if (isDarkTheme) Color.White.copy(alpha = 0.12f) else Color.Black.copy(alpha = 0.08f) - - // Attach menu items with colors - val attachMenuItems = remember { - listOf( - AttachMenuItem( - icon = { Icon(Icons.Default.Image, contentDescription = null, tint = Color.White, modifier = Modifier.size(24.dp)) }, - label = "Photo", - color = Color(0xFF007AFF), - onClick = onAttachPhoto - ), - AttachMenuItem( - icon = { Icon(Icons.Default.InsertDriveFile, contentDescription = null, tint = Color.White, modifier = Modifier.size(24.dp)) }, - label = "File", - color = Color(0xFF34C759), - onClick = onAttachFile - ), - AttachMenuItem( - icon = { Icon(Icons.Default.LocationOn, contentDescription = null, tint = Color.White, modifier = Modifier.size(24.dp)) }, - label = "Location", - color = Color(0xFFFF9500), - onClick = onAttachLocation - ), - AttachMenuItem( - icon = { Icon(Icons.Default.Person, contentDescription = null, tint = Color.White, modifier = Modifier.size(24.dp)) }, - label = "Contact", - color = Color(0xFFAF52DE), - onClick = onAttachContact - ) - ) - } - - // Toggle emoji picker (Telegram behavior) - fun toggleEmojiPicker() { - if (showEmojiPicker) { - // Closing emoji - show keyboard - showEmojiPicker = false - isKeyboardVisible = true - } else { - // Opening emoji - hide keyboard first - keyboardController?.hide() - focusManager.clearFocus() - showAttachMenu = false - showEmojiPicker = true - isKeyboardVisible = false - } - } - - // Open keyboard (when tapping on text field or closing emoji) - fun openKeyboard() { - showEmojiPicker = false - showAttachMenu = false - isKeyboardVisible = true - } - - // Toggle attach menu - fun toggleAttachMenu() { - if (showAttachMenu) { - showAttachMenu = false - } else { - keyboardController?.hide() - focusManager.clearFocus() - showEmojiPicker = false - showAttachMenu = true - isKeyboardVisible = false - } - } - - fun handleSend() { - if (value.isNotBlank()) { - onSend() - onValueChange("") - showEmojiPicker = false - showAttachMenu = false - } - } - - Column( - modifier = modifier - .fillMaxWidth() - .then(if (!showEmojiPicker && !showAttachMenu) Modifier.imePadding() else Modifier) - ) { - // Attach menu (bottom sheet style like Telegram) - AnimatedVisibility( - visible = showAttachMenu, - enter = expandVertically(expandFrom = Alignment.Bottom) + fadeIn(tween(200)), - exit = shrinkVertically(shrinkTowards = Alignment.Bottom) + fadeOut(tween(150)) - ) { - AttachMenuPanel( - items = attachMenuItems, - isDarkTheme = isDarkTheme, - onDismiss = { showAttachMenu = false } - ) - } - - // Telegram input panel container - Box( - modifier = Modifier - .fillMaxWidth() - .background(inputPanelBackground) - ) { - // Top divider (как в Telegram) - Box( - modifier = Modifier - .align(Alignment.TopCenter) - .fillMaxWidth() - .height(0.5.dp) - .background(dividerColor) - ) - - // textFieldContainer - padding(0, dp(1), 0, 0) - Box( - modifier = Modifier - .fillMaxWidth() - .padding(top = 1.dp) - ) { - Row( - modifier = Modifier - .fillMaxWidth() - .heightIn(min = 48.dp, max = 192.dp) // DEFAULT_HEIGHT = 48dp, max 6 lines - .padding(start = 3.dp, end = 0.dp, bottom = 0.dp), - verticalAlignment = Alignment.Bottom - ) { - // EMOJI BUTTON - 48dp, toggles between emoji/keyboard icon - Box( - modifier = Modifier - .size(48.dp) - .clickable( - interactionSource = interactionSource, - indication = null, - onClick = { toggleEmojiPicker() } - ) - .padding(7.5.dp), - contentAlignment = Alignment.Center - ) { - // Crossfade animation between emoji and keyboard icons - Crossfade( - targetState = showEmojiPicker, - animationSpec = tween(200), - label = "emoji icon" - ) { isEmojiOpen -> - Icon( - imageVector = if (isEmojiOpen) Icons.Default.Keyboard else Icons.Default.EmojiEmotions, - contentDescription = if (isEmojiOpen) "Keyboard" else "Emoji", - tint = if (isEmojiOpen) activeIconColor else iconColor, - modifier = Modifier.size(24.dp) - ) - } - } - - // MESSAGE EDIT TEXT CONTAINER - weight(1), margin right 50dp for attachLayout - Box( - modifier = Modifier - .weight(1f) - .heightIn(min = 48.dp, max = 192.dp) - .padding(end = 50.dp) - .clickable( - interactionSource = interactionSource, - indication = null - ) { - // Tap on text field - close emoji/attach, open keyboard - if (showEmojiPicker || showAttachMenu) { - openKeyboard() - } - }, - contentAlignment = Alignment.CenterStart - ) { - // EditText - setTextSize(18sp), setPadding(0, dp(9), 0, dp(10)) - AppleEmojiTextField( - value = value, - onValueChange = onValueChange, - textColor = textColor, - textSize = 18f, // EXACT 18sp from Telegram - hint = "Message", - hintColor = hintColor, - modifier = Modifier - .fillMaxWidth() - .padding(start = 0.dp, end = 0.dp, top = 9.dp, bottom = 10.dp) // EXACT padding from Telegram - ) - } - } - - // ATTACH LAYOUT (positioned absolutely at right) - contains attachButton - Box( - modifier = Modifier - .align(Alignment.BottomEnd) - .width(50.dp) - .height(48.dp) - .padding(end = 0.dp) - ) { - // ATTACH BUTTON - 48dp with rotation animation - Box( - modifier = Modifier - .fillMaxSize() - .clickable( - interactionSource = interactionSource, - indication = null, - onClick = { toggleAttachMenu() } - ) - .padding(7.5.dp), - contentAlignment = Alignment.Center - ) { - Icon( - imageVector = Icons.Default.AttachFile, - contentDescription = "Attach", - tint = if (showAttachMenu) activeIconColor else iconColor, - modifier = Modifier - .size(24.dp) - .graphicsLayer { - rotationZ = 45f + attachRotation // Base 45° + animation when open - } - ) - } - } - - // SEND BUTTON CONTAINER - 100dp width, right position (overlay over attachLayout) - Box( - modifier = Modifier - .align(Alignment.BottomEnd) - .width(100.dp) - .height(48.dp) - ) { - // AUDIO/VIDEO BUTTON (microphone) - 48dp - Box( - modifier = Modifier - .align(Alignment.CenterEnd) - .size(48.dp) - .graphicsLayer { - scaleX = micScale - scaleY = micScale - alpha = micAlpha - } - .clickable( - interactionSource = interactionSource, - indication = null, - enabled = !canSend - ) { /* TODO: voice recording */ } - .padding(7.5.dp), - contentAlignment = Alignment.Center - ) { - Icon( - imageVector = Icons.Default.Mic, - contentDescription = "Voice", - tint = iconColor, - modifier = Modifier.size(24.dp) - ) - } - - // SEND BUTTON - 48dp with scale/alpha animation (250ms TelegramEasing) - Box( - modifier = Modifier - .align(Alignment.CenterEnd) - .size(48.dp) - .graphicsLayer { - scaleX = sendScale - scaleY = sendScale - alpha = sendAlpha - } - .clip(CircleShape) - .background(primaryBlue) // Our blue color - .clickable( - interactionSource = interactionSource, - indication = null, - enabled = canSend, - onClick = { handleSend() } - ) - .padding(7.5.dp), - contentAlignment = Alignment.Center - ) { - Icon( - imageVector = Icons.Default.Send, - contentDescription = "Send", - tint = Color.White, - modifier = Modifier.size(24.dp) - ) - } - } - } - } - - // Emoji picker with animation - AnimatedVisibility( - visible = showEmojiPicker, - enter = expandVertically(expandFrom = Alignment.Bottom) + fadeIn(), - exit = shrinkVertically(shrinkTowards = Alignment.Bottom) + fadeOut() - ) { - AppleEmojiPickerPanel( - isDarkTheme = isDarkTheme, - onEmojiSelected = { emoji -> - onValueChange(value + emoji) - }, - onClose = { showEmojiPicker = false } - ) - } - - if (!showEmojiPicker && !showAttachMenu) { - Spacer(modifier = Modifier.navigationBarsPadding()) - } - } -} - -/** - * Attach Menu Panel - Telegram style bottom sheet with action buttons - */ -@Composable -private fun AttachMenuPanel( - items: List, - isDarkTheme: Boolean, - onDismiss: () -> Unit, - modifier: Modifier = Modifier -) { - val panelBackground = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFF8F8FA) - val labelColor = if (isDarkTheme) Color.White.copy(alpha = 0.9f) else Color.Black.copy(alpha = 0.8f) - val dividerColor = if (isDarkTheme) Color.White.copy(alpha = 0.12f) else Color.Black.copy(alpha = 0.08f) - - Column( - modifier = modifier - .fillMaxWidth() - .background(panelBackground) - ) { - // Top divider - Box( - modifier = Modifier - .fillMaxWidth() - .height(0.5.dp) - .background(dividerColor) - ) - - // Buttons grid - Row( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp, vertical = 20.dp), - horizontalArrangement = Arrangement.SpaceEvenly - ) { - items.forEach { item -> - AttachMenuButton( - item = item, - labelColor = labelColor, - onDismiss = onDismiss - ) - } - } - - Spacer(modifier = Modifier.navigationBarsPadding()) - } -} - -/** - * Single attach menu button with icon and label - */ -@Composable -private fun AttachMenuButton( - item: AttachMenuItem, - labelColor: Color, - onDismiss: () -> Unit -) { - val interactionSource = remember { MutableInteractionSource() } - - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier - .clickable( - interactionSource = interactionSource, - indication = null - ) { - item.onClick() - onDismiss() - } - ) { - // Colored circle with icon - Box( - modifier = Modifier - .size(56.dp) - .clip(CircleShape) - .background(item.color) - .clickable( - interactionSource = interactionSource, - indication = null - ) { - item.onClick() - onDismiss() - }, - contentAlignment = Alignment.Center - ) { - item.icon() - } - - Spacer(modifier = Modifier.height(8.dp)) - - // Label - Text( - text = item.label, - color = labelColor, - fontSize = 12.sp, - fontWeight = FontWeight.Medium - ) - } -}