diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt index 0cb147a..bbbf25b 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListScreen.kt @@ -211,6 +211,9 @@ fun ChatsListScreen( var dialogToDelete by remember { mutableStateOf(null) } var dialogToBlock by remember { mutableStateOf(null) } var dialogToUnblock by remember { mutableStateOf(null) } + + // Trigger для обновления статуса блокировки + var blocklistUpdateTrigger by remember { mutableStateOf(0) } // Dev console dialog - commented out for now /* @@ -520,7 +523,7 @@ fun ChatsListScreen( val isSavedMessages = dialog.opponentKey == accountPublicKey // Check if user is blocked var isBlocked by remember { mutableStateOf(false) } - LaunchedEffect(dialog.opponentKey) { + LaunchedEffect(dialog.opponentKey, blocklistUpdateTrigger) { isBlocked = chatsViewModel.isUserBlocked(dialog.opponentKey) } @@ -577,37 +580,54 @@ fun ChatsListScreen( ) }, text = { - val displayName = dialog.opponentTitle.ifEmpty { dialog.opponentKey.take(8) } - Text( - text = "All messages with $displayName will be permanently deleted. This action cannot be undone.", - fontSize = 15.sp, - color = if (isDarkTheme) Color(0xFFAAAAAA) else Color(0xFF666666), - textAlign = TextAlign.Center, - modifier = Modifier.fillMaxWidth() - ) - }, - confirmButton = { - TextButton( - onClick = { - scope.launch { - chatsViewModel.deleteDialog(dialog.opponentKey) - dialogToDelete = null - } - }, - colors = ButtonDefaults.textButtonColors( - contentColor = Color(0xFFFF3B30) + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + val displayName = dialog.opponentTitle.ifEmpty { dialog.opponentKey.take(8) } + Text( + text = "All messages with $displayName will be permanently deleted. This action cannot be undone.", + fontSize = 15.sp, + color = if (isDarkTheme) Color(0xFFAAAAAA) else Color(0xFF666666), + textAlign = TextAlign.Center, + modifier = Modifier.fillMaxWidth() ) - ) { - Text("Delete", fontWeight = FontWeight.SemiBold) - } - }, - dismissButton = { - TextButton( - onClick = { dialogToDelete = null } - ) { - Text("Cancel", fontWeight = FontWeight.Medium) + + Spacer(modifier = Modifier.height(16.dp)) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically + ) { + TextButton( + onClick = { dialogToDelete = null }, + modifier = Modifier.weight(1f) + ) { + Text("Cancel", fontWeight = FontWeight.Medium) + } + + Spacer(modifier = Modifier.width(8.dp)) + + TextButton( + onClick = { + scope.launch { + chatsViewModel.deleteDialog(dialog.opponentKey) + dialogToDelete = null + } + }, + colors = ButtonDefaults.textButtonColors( + contentColor = Color(0xFFFF3B30) + ), + modifier = Modifier.weight(1f) + ) { + Text("Delete", fontWeight = FontWeight.SemiBold) + } + } } }, + confirmButton = {}, + dismissButton = {}, containerColor = if (isDarkTheme) Color(0xFF2C2C2E) else Color.White, shape = RoundedCornerShape(16.dp) ) @@ -635,37 +655,55 @@ fun ChatsListScreen( ) }, text = { - val displayName = dialog.opponentTitle.ifEmpty { dialog.opponentKey.take(8) } - Text( - text = "$displayName will no longer be able to send you messages. You can unblock them later.", - fontSize = 15.sp, - color = if (isDarkTheme) Color(0xFFAAAAAA) else Color(0xFF666666), - textAlign = TextAlign.Center, - modifier = Modifier.fillMaxWidth() - ) - }, - confirmButton = { - TextButton( - onClick = { - scope.launch { - chatsViewModel.blockUser(dialog.opponentKey) - dialogToBlock = null - } - }, - colors = ButtonDefaults.textButtonColors( - contentColor = Color(0xFFFF3B30) + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + val displayName = dialog.opponentTitle.ifEmpty { dialog.opponentKey.take(8) } + Text( + text = "$displayName will no longer be able to send you messages. You can unblock them later.", + fontSize = 15.sp, + color = if (isDarkTheme) Color(0xFFAAAAAA) else Color(0xFF666666), + textAlign = TextAlign.Center, + modifier = Modifier.fillMaxWidth() ) - ) { - Text("Block", fontWeight = FontWeight.SemiBold) - } - }, - dismissButton = { - TextButton( - onClick = { dialogToBlock = null } - ) { - Text("Cancel", fontWeight = FontWeight.Medium) + + Spacer(modifier = Modifier.height(16.dp)) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically + ) { + TextButton( + onClick = { dialogToBlock = null }, + modifier = Modifier.weight(1f) + ) { + Text("Cancel", fontWeight = FontWeight.Medium) + } + + Spacer(modifier = Modifier.width(8.dp)) + + TextButton( + onClick = { + scope.launch { + chatsViewModel.blockUser(dialog.opponentKey) + dialogToBlock = null + blocklistUpdateTrigger++ + } + }, + colors = ButtonDefaults.textButtonColors( + contentColor = Color(0xFFFF3B30) + ), + modifier = Modifier.weight(1f) + ) { + Text("Block", fontWeight = FontWeight.SemiBold) + } + } } }, + confirmButton = {}, + dismissButton = {}, containerColor = if (isDarkTheme) Color(0xFF2C2C2E) else Color.White, shape = RoundedCornerShape(16.dp) ) @@ -693,37 +731,55 @@ fun ChatsListScreen( ) }, text = { - val displayName = dialog.opponentTitle.ifEmpty { dialog.opponentKey.take(8) } - Text( - text = "$displayName will be able to send you messages again.", - fontSize = 15.sp, - color = if (isDarkTheme) Color(0xFFAAAAAA) else Color(0xFF666666), - textAlign = TextAlign.Center, - modifier = Modifier.fillMaxWidth() - ) - }, - confirmButton = { - TextButton( - onClick = { - scope.launch { - chatsViewModel.unblockUser(dialog.opponentKey) - dialogToUnblock = null - } - }, - colors = ButtonDefaults.textButtonColors( - contentColor = Color(0xFF4CAF50) + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + val displayName = dialog.opponentTitle.ifEmpty { dialog.opponentKey.take(8) } + Text( + text = "$displayName will be able to send you messages again.", + fontSize = 15.sp, + color = if (isDarkTheme) Color(0xFFAAAAAA) else Color(0xFF666666), + textAlign = TextAlign.Center, + modifier = Modifier.fillMaxWidth() ) - ) { - Text("Unblock", fontWeight = FontWeight.SemiBold) - } - }, - dismissButton = { - TextButton( - onClick = { dialogToUnblock = null } - ) { - Text("Cancel", fontWeight = FontWeight.Medium) + + Spacer(modifier = Modifier.height(16.dp)) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically + ) { + TextButton( + onClick = { dialogToUnblock = null }, + modifier = Modifier.weight(1f) + ) { + Text("Cancel", fontWeight = FontWeight.Medium) + } + + Spacer(modifier = Modifier.width(8.dp)) + + TextButton( + onClick = { + scope.launch { + chatsViewModel.unblockUser(dialog.opponentKey) + dialogToUnblock = null + blocklistUpdateTrigger++ + } + }, + colors = ButtonDefaults.textButtonColors( + contentColor = Color(0xFF4CAF50) + ), + modifier = Modifier.weight(1f) + ) { + Text("Unblock", fontWeight = FontWeight.SemiBold) + } + } } }, + confirmButton = {}, + dismissButton = {}, containerColor = if (isDarkTheme) Color(0xFF2C2C2E) else Color.White, shape = RoundedCornerShape(16.dp) ) @@ -1106,7 +1162,7 @@ fun SwipeableDialogItem( } // 2. КОНТЕНТ - поверх кнопок, сдвигается при свайпе - Box( + Column( modifier = Modifier .fillMaxSize() .offset { IntOffset(animatedOffsetX.toInt(), 0) } @@ -1132,23 +1188,30 @@ fun SwipeableDialogItem( ) } ) { - DialogItem( + DialogItemContent( dialog = dialog, isDarkTheme = isDarkTheme, isTyping = isTyping, onClick = onClick ) + + // Сепаратор внутри контента + val dividerColor = if (isDarkTheme) Color(0xFF3A3A3A) else Color(0xFFE8E8E8) + Divider( + modifier = Modifier.padding(start = 84.dp), + color = dividerColor, + thickness = 0.5.dp + ) } } } -/** Элемент диалога из базы данных - ОПТИМИЗИРОВАННЫЙ */ +/** Элемент диалога из базы данных - ОПТИМИЗИРОВАННЫЙ (без сепаратора для SwipeableDialogItem) */ @Composable -fun DialogItem(dialog: DialogUiModel, isDarkTheme: Boolean, isTyping: Boolean = false, onClick: () -> Unit) { +fun DialogItemContent(dialog: DialogUiModel, isDarkTheme: Boolean, isTyping: Boolean = false, onClick: () -> Unit) { // 🔥 ОПТИМИЗАЦИЯ: Кешируем цвета и строки val textColor = remember(isDarkTheme) { if (isDarkTheme) Color.White else Color.Black } val secondaryTextColor = remember(isDarkTheme) { if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666) } - val dividerColor = remember(isDarkTheme) { if (isDarkTheme) Color(0xFF3A3A3A) else Color(0xFFE8E8E8) } val avatarColors = remember(dialog.opponentKey, isDarkTheme) { getAvatarColor(dialog.opponentKey, isDarkTheme) } val displayName = remember(dialog.opponentTitle, dialog.opponentKey) { @@ -1166,24 +1229,23 @@ fun DialogItem(dialog: DialogUiModel, isDarkTheme: Boolean, isTyping: Boolean = } } - Column { - Row( - modifier = - Modifier.fillMaxWidth() - .clickable(onClick = onClick) - .padding(horizontal = 16.dp, vertical = 12.dp), - verticalAlignment = Alignment.CenterVertically - ) { - // Avatar container with online indicator - Box(modifier = Modifier.size(56.dp)) { - // Avatar - Box( - modifier = - Modifier.fillMaxSize() - .clip(CircleShape) - .background(avatarColors.backgroundColor), - contentAlignment = Alignment.Center - ) { + Row( + modifier = + Modifier.fillMaxWidth() + .clickable(onClick = onClick) + .padding(horizontal = 16.dp, vertical = 12.dp), + verticalAlignment = Alignment.CenterVertically + ) { + // Avatar container with online indicator + Box(modifier = Modifier.size(56.dp)) { + // Avatar + Box( + modifier = + Modifier.fillMaxSize() + .clip(CircleShape) + .background(avatarColors.backgroundColor), + contentAlignment = Alignment.Center + ) { Text( text = initials, color = avatarColors.textColor, @@ -1290,13 +1352,6 @@ fun DialogItem(dialog: DialogUiModel, isDarkTheme: Boolean, isTyping: Boolean = } } } - - Divider( - modifier = Modifier.padding(start = 84.dp), - color = dividerColor, - thickness = 0.5.dp - ) - } } /** diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListViewModel.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListViewModel.kt index 400c6aa..134dcfd 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListViewModel.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatsListViewModel.kt @@ -176,10 +176,19 @@ class ChatsListViewModel(application: Application) : AndroidViewModel(applicatio if (currentAccount.isEmpty()) return try { + // Вычисляем правильный dialog_key (отсортированная комбинация ключей) + val dialogKey = if (currentAccount < opponentKey) { + "$currentAccount:$opponentKey" + } else { + "$opponentKey:$currentAccount" + } + + android.util.Log.d("ChatsListViewModel", "Deleting dialog with key: $dialogKey") + // Удаляем все сообщения из диалога по dialog_key database.messageDao().deleteDialog( account = currentAccount, - dialogKey = opponentKey + dialogKey = dialogKey ) // Также удаляем по from/to ключам (на всякий случай) database.messageDao().deleteMessagesBetweenUsers(