feat: Add dropdown menu for chat options and confirmation dialogs for delete and block actions

This commit is contained in:
k1ngsterr1
2026-01-12 01:55:44 +05:00
parent a75dfaab98
commit 5f348f329e

View File

@@ -54,6 +54,10 @@ import com.rosetta.messenger.ui.components.AppleEmojiText
import com.rosetta.messenger.ui.components.AppleEmojiTextField import com.rosetta.messenger.ui.components.AppleEmojiTextField
import com.rosetta.messenger.ui.components.VerifiedBadge import com.rosetta.messenger.ui.components.VerifiedBadge
import com.rosetta.messenger.ui.onboarding.PrimaryBlue import com.rosetta.messenger.ui.onboarding.PrimaryBlue
import android.view.inputmethod.InputMethodManager
import android.content.Context
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@@ -227,6 +231,11 @@ fun ChatDetailScreen(
// val debugLogs by ProtocolManager.debugLogs.collectAsState() // val debugLogs by ProtocolManager.debugLogs.collectAsState()
val debugLogs = remember { emptyList<String>() } val debugLogs = remember { emptyList<String>() }
// Состояние выпадающего меню
var showMenu by remember { mutableStateOf(false) }
var showDeleteConfirm by remember { mutableStateOf(false) }
var showBlockConfirm by remember { mutableStateOf(false) }
// Подключаем к ViewModel // Подключаем к ViewModel
val messages by viewModel.messages.collectAsState() val messages by viewModel.messages.collectAsState()
val inputText by viewModel.inputText.collectAsState() val inputText by viewModel.inputText.collectAsState()
@@ -446,18 +455,82 @@ fun ChatDetailScreen(
) )
} }
IconButton( // Кнопка меню с выпадающим списком
onClick = { Box {
keyboardController?.hide() IconButton(
focusManager.clearFocus() onClick = {
/* TODO: More options */ keyboardController?.hide()
focusManager.clearFocus()
showMenu = true
}
) {
Icon(
Icons.Default.MoreVert,
contentDescription = "More",
tint = textColor
)
}
// Выпадающее меню
DropdownMenu(
expanded = showMenu,
onDismissRequest = { showMenu = false },
modifier = Modifier
.background(
color = if (isDarkTheme) Color(0xFF2C2C2E) else Color.White,
shape = RoundedCornerShape(12.dp)
)
) {
// Delete Chat
DropdownMenuItem(
text = {
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
Icons.Default.Delete,
contentDescription = null,
tint = PrimaryBlue,
modifier = Modifier.size(20.dp)
)
Spacer(modifier = Modifier.width(12.dp))
Text(
"Delete Chat",
color = textColor,
fontSize = 16.sp
)
}
},
onClick = {
showMenu = false
showDeleteConfirm = true
}
)
// Block User (не показываем для Saved Messages)
if (!isSavedMessages) {
DropdownMenuItem(
text = {
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
Icons.Default.Block,
contentDescription = null,
tint = Color(0xFFFF3B30),
modifier = Modifier.size(20.dp)
)
Spacer(modifier = Modifier.width(12.dp))
Text(
"Block User",
color = Color(0xFFFF3B30),
fontSize = 16.sp
)
}
},
onClick = {
showMenu = false
showBlockConfirm = true
}
)
} }
) { }
Icon(
Icons.Default.MoreVert,
contentDescription = "More",
tint = textColor
)
} }
} }
// Нижняя линия для разделения // Нижняя линия для разделения
@@ -725,6 +798,79 @@ fun ChatDetailScreen(
confirmButton = { TextButton(onClick = { showLogs = false }) { Text("Close") } } confirmButton = { TextButton(onClick = { showLogs = false }) { Text("Close") } }
) )
} }
// Диалог подтверждения удаления чата
if (showDeleteConfirm) {
AlertDialog(
onDismissRequest = { showDeleteConfirm = false },
containerColor = if (isDarkTheme) Color(0xFF2C2C2E) else Color.White,
title = {
Text(
"Delete Chat",
fontWeight = FontWeight.Bold,
color = textColor
)
},
text = {
Text(
"Are you sure you want to delete this chat? This action cannot be undone.",
color = secondaryTextColor
)
},
confirmButton = {
TextButton(
onClick = {
showDeleteConfirm = false
// TODO: Implement delete chat
onBack()
}
) {
Text("Delete", color = Color(0xFFFF3B30))
}
},
dismissButton = {
TextButton(onClick = { showDeleteConfirm = false }) {
Text("Cancel", color = PrimaryBlue)
}
}
)
}
// Диалог подтверждения блокировки
if (showBlockConfirm) {
AlertDialog(
onDismissRequest = { showBlockConfirm = false },
containerColor = if (isDarkTheme) Color(0xFF2C2C2E) else Color.White,
title = {
Text(
"Block ${user.title.ifEmpty { "User" }}",
fontWeight = FontWeight.Bold,
color = textColor
)
},
text = {
Text(
"Are you sure you want to block this user? They won't be able to send you messages.",
color = secondaryTextColor
)
},
confirmButton = {
TextButton(
onClick = {
showBlockConfirm = false
// TODO: Implement block user
}
) {
Text("Block", color = Color(0xFFFF3B30))
}
},
dismissButton = {
TextButton(onClick = { showBlockConfirm = false }) {
Text("Cancel", color = PrimaryBlue)
}
}
)
}
} }
/** 🚀 Анимация появления сообщения Telegram-style */ /** 🚀 Анимация появления сообщения Telegram-style */
@@ -900,9 +1046,16 @@ private fun MessageInputBar(
placeholderColor: Color placeholderColor: Color
) { ) {
var showEmojiPicker by remember { mutableStateOf(false) } var showEmojiPicker by remember { mutableStateOf(false) }
// Флаг для запуска закрытия клавиатуры перед открытием emoji picker
var pendingEmojiPicker by remember { mutableStateOf(false) }
val keyboardController = LocalSoftwareKeyboardController.current val keyboardController = LocalSoftwareKeyboardController.current
val focusManager = LocalFocusManager.current val focusManager = LocalFocusManager.current
val interactionSource = remember { MutableInteractionSource() } val interactionSource = remember { MutableInteractionSource() }
val scope = rememberCoroutineScope()
// Получаем context и view для гарантированного закрытия клавиатуры
val context = LocalContext.current
val view = LocalView.current
// Состояние отправки // Состояние отправки
val canSend = remember(value) { value.isNotBlank() } val canSend = remember(value) { value.isNotBlank() }
@@ -910,16 +1063,36 @@ private fun MessageInputBar(
// Easing анимации // Easing анимации
val backEasing = CubicBezierEasing(0.34f, 1.56f, 0.64f, 1f) val backEasing = CubicBezierEasing(0.34f, 1.56f, 0.64f, 1f)
// Функция для гарантированного закрытия клавиатуры через InputMethodManager
fun hideKeyboardCompletely() {
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(view.windowToken, 0)
focusManager.clearFocus(force = true)
}
// Эффект для закрытия клавиатуры и открытия emoji picker
LaunchedEffect(pendingEmojiPicker) {
if (pendingEmojiPicker) {
// Гарантированно закрываем клавиатуру через InputMethodManager
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(view.windowToken, 0)
focusManager.clearFocus(force = true)
// Ждём пока клавиатура закроется
delay(200)
// Теперь открываем emoji picker
showEmojiPicker = true
pendingEmojiPicker = false
}
}
// Функция переключения emoji picker // Функция переключения emoji picker
fun toggleEmojiPicker() { fun toggleEmojiPicker() {
if (showEmojiPicker) { if (showEmojiPicker) {
// Закрываем emoji picker
showEmojiPicker = false showEmojiPicker = false
} else { } else {
// Сначала скрываем клавиатуру, затем показываем emoji picker // Запускаем процесс: сначала закрыть клавиатуру, потом открыть picker
focusManager.clearFocus(force = true) pendingEmojiPicker = true
keyboardController?.hide()
// Небольшая задержка чтобы клавиатура успела закрыться
showEmojiPicker = true
} }
} }