feat: Update dialog deletion logic to use sorted dialog keys for message removal

This commit is contained in:
k1ngsterr1
2026-01-15 18:34:15 +05:00
parent 36976dc747
commit f2d744a442
2 changed files with 180 additions and 116 deletions

View File

@@ -211,6 +211,9 @@ fun ChatsListScreen(
var dialogToDelete by remember { mutableStateOf<DialogUiModel?>(null) } var dialogToDelete by remember { mutableStateOf<DialogUiModel?>(null) }
var dialogToBlock by remember { mutableStateOf<DialogUiModel?>(null) } var dialogToBlock by remember { mutableStateOf<DialogUiModel?>(null) }
var dialogToUnblock by remember { mutableStateOf<DialogUiModel?>(null) } var dialogToUnblock by remember { mutableStateOf<DialogUiModel?>(null) }
// Trigger для обновления статуса блокировки
var blocklistUpdateTrigger by remember { mutableStateOf(0) }
// Dev console dialog - commented out for now // Dev console dialog - commented out for now
/* /*
@@ -520,7 +523,7 @@ fun ChatsListScreen(
val isSavedMessages = dialog.opponentKey == accountPublicKey val isSavedMessages = dialog.opponentKey == accountPublicKey
// Check if user is blocked // Check if user is blocked
var isBlocked by remember { mutableStateOf(false) } var isBlocked by remember { mutableStateOf(false) }
LaunchedEffect(dialog.opponentKey) { LaunchedEffect(dialog.opponentKey, blocklistUpdateTrigger) {
isBlocked = chatsViewModel.isUserBlocked(dialog.opponentKey) isBlocked = chatsViewModel.isUserBlocked(dialog.opponentKey)
} }
@@ -577,37 +580,54 @@ fun ChatsListScreen(
) )
}, },
text = { text = {
val displayName = dialog.opponentTitle.ifEmpty { dialog.opponentKey.take(8) } Column(
Text( modifier = Modifier.fillMaxWidth(),
text = "All messages with $displayName will be permanently deleted. This action cannot be undone.", horizontalAlignment = Alignment.CenterHorizontally
fontSize = 15.sp, ) {
color = if (isDarkTheme) Color(0xFFAAAAAA) else Color(0xFF666666), val displayName = dialog.opponentTitle.ifEmpty { dialog.opponentKey.take(8) }
textAlign = TextAlign.Center, Text(
modifier = Modifier.fillMaxWidth() 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),
confirmButton = { textAlign = TextAlign.Center,
TextButton( modifier = Modifier.fillMaxWidth()
onClick = {
scope.launch {
chatsViewModel.deleteDialog(dialog.opponentKey)
dialogToDelete = null
}
},
colors = ButtonDefaults.textButtonColors(
contentColor = Color(0xFFFF3B30)
) )
) {
Text("Delete", fontWeight = FontWeight.SemiBold) Spacer(modifier = Modifier.height(16.dp))
}
}, Row(
dismissButton = { modifier = Modifier.fillMaxWidth(),
TextButton( horizontalArrangement = Arrangement.Center,
onClick = { dialogToDelete = null } verticalAlignment = Alignment.CenterVertically
) { ) {
Text("Cancel", fontWeight = FontWeight.Medium) 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, containerColor = if (isDarkTheme) Color(0xFF2C2C2E) else Color.White,
shape = RoundedCornerShape(16.dp) shape = RoundedCornerShape(16.dp)
) )
@@ -635,37 +655,55 @@ fun ChatsListScreen(
) )
}, },
text = { text = {
val displayName = dialog.opponentTitle.ifEmpty { dialog.opponentKey.take(8) } Column(
Text( modifier = Modifier.fillMaxWidth(),
text = "$displayName will no longer be able to send you messages. You can unblock them later.", horizontalAlignment = Alignment.CenterHorizontally
fontSize = 15.sp, ) {
color = if (isDarkTheme) Color(0xFFAAAAAA) else Color(0xFF666666), val displayName = dialog.opponentTitle.ifEmpty { dialog.opponentKey.take(8) }
textAlign = TextAlign.Center, Text(
modifier = Modifier.fillMaxWidth() 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),
confirmButton = { textAlign = TextAlign.Center,
TextButton( modifier = Modifier.fillMaxWidth()
onClick = {
scope.launch {
chatsViewModel.blockUser(dialog.opponentKey)
dialogToBlock = null
}
},
colors = ButtonDefaults.textButtonColors(
contentColor = Color(0xFFFF3B30)
) )
) {
Text("Block", fontWeight = FontWeight.SemiBold) Spacer(modifier = Modifier.height(16.dp))
}
}, Row(
dismissButton = { modifier = Modifier.fillMaxWidth(),
TextButton( horizontalArrangement = Arrangement.Center,
onClick = { dialogToBlock = null } verticalAlignment = Alignment.CenterVertically
) { ) {
Text("Cancel", fontWeight = FontWeight.Medium) 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, containerColor = if (isDarkTheme) Color(0xFF2C2C2E) else Color.White,
shape = RoundedCornerShape(16.dp) shape = RoundedCornerShape(16.dp)
) )
@@ -693,37 +731,55 @@ fun ChatsListScreen(
) )
}, },
text = { text = {
val displayName = dialog.opponentTitle.ifEmpty { dialog.opponentKey.take(8) } Column(
Text( modifier = Modifier.fillMaxWidth(),
text = "$displayName will be able to send you messages again.", horizontalAlignment = Alignment.CenterHorizontally
fontSize = 15.sp, ) {
color = if (isDarkTheme) Color(0xFFAAAAAA) else Color(0xFF666666), val displayName = dialog.opponentTitle.ifEmpty { dialog.opponentKey.take(8) }
textAlign = TextAlign.Center, Text(
modifier = Modifier.fillMaxWidth() text = "$displayName will be able to send you messages again.",
) fontSize = 15.sp,
}, color = if (isDarkTheme) Color(0xFFAAAAAA) else Color(0xFF666666),
confirmButton = { textAlign = TextAlign.Center,
TextButton( modifier = Modifier.fillMaxWidth()
onClick = {
scope.launch {
chatsViewModel.unblockUser(dialog.opponentKey)
dialogToUnblock = null
}
},
colors = ButtonDefaults.textButtonColors(
contentColor = Color(0xFF4CAF50)
) )
) {
Text("Unblock", fontWeight = FontWeight.SemiBold) Spacer(modifier = Modifier.height(16.dp))
}
}, Row(
dismissButton = { modifier = Modifier.fillMaxWidth(),
TextButton( horizontalArrangement = Arrangement.Center,
onClick = { dialogToUnblock = null } verticalAlignment = Alignment.CenterVertically
) { ) {
Text("Cancel", fontWeight = FontWeight.Medium) 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, containerColor = if (isDarkTheme) Color(0xFF2C2C2E) else Color.White,
shape = RoundedCornerShape(16.dp) shape = RoundedCornerShape(16.dp)
) )
@@ -1106,7 +1162,7 @@ fun SwipeableDialogItem(
} }
// 2. КОНТЕНТ - поверх кнопок, сдвигается при свайпе // 2. КОНТЕНТ - поверх кнопок, сдвигается при свайпе
Box( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.offset { IntOffset(animatedOffsetX.toInt(), 0) } .offset { IntOffset(animatedOffsetX.toInt(), 0) }
@@ -1132,23 +1188,30 @@ fun SwipeableDialogItem(
) )
} }
) { ) {
DialogItem( DialogItemContent(
dialog = dialog, dialog = dialog,
isDarkTheme = isDarkTheme, isDarkTheme = isDarkTheme,
isTyping = isTyping, isTyping = isTyping,
onClick = onClick 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 @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 textColor = remember(isDarkTheme) { if (isDarkTheme) Color.White else Color.Black }
val secondaryTextColor = remember(isDarkTheme) { if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666) } 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 avatarColors = remember(dialog.opponentKey, isDarkTheme) { getAvatarColor(dialog.opponentKey, isDarkTheme) }
val displayName = remember(dialog.opponentTitle, dialog.opponentKey) { val displayName = remember(dialog.opponentTitle, dialog.opponentKey) {
@@ -1166,24 +1229,23 @@ fun DialogItem(dialog: DialogUiModel, isDarkTheme: Boolean, isTyping: Boolean =
} }
} }
Column { Row(
Row( modifier =
modifier = Modifier.fillMaxWidth()
Modifier.fillMaxWidth() .clickable(onClick = onClick)
.clickable(onClick = onClick) .padding(horizontal = 16.dp, vertical = 12.dp),
.padding(horizontal = 16.dp, vertical = 12.dp), verticalAlignment = Alignment.CenterVertically
verticalAlignment = Alignment.CenterVertically ) {
) { // Avatar container with online indicator
// Avatar container with online indicator Box(modifier = Modifier.size(56.dp)) {
Box(modifier = Modifier.size(56.dp)) { // Avatar
// Avatar Box(
Box( modifier =
modifier = Modifier.fillMaxSize()
Modifier.fillMaxSize() .clip(CircleShape)
.clip(CircleShape) .background(avatarColors.backgroundColor),
.background(avatarColors.backgroundColor), contentAlignment = Alignment.Center
contentAlignment = Alignment.Center ) {
) {
Text( Text(
text = initials, text = initials,
color = avatarColors.textColor, 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
)
}
} }
/** /**

View File

@@ -176,10 +176,19 @@ class ChatsListViewModel(application: Application) : AndroidViewModel(applicatio
if (currentAccount.isEmpty()) return if (currentAccount.isEmpty()) return
try { 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 // Удаляем все сообщения из диалога по dialog_key
database.messageDao().deleteDialog( database.messageDao().deleteDialog(
account = currentAccount, account = currentAccount,
dialogKey = opponentKey dialogKey = dialogKey
) )
// Также удаляем по from/to ключам (на всякий случай) // Также удаляем по from/to ключам (на всякий случай)
database.messageDao().deleteMessagesBetweenUsers( database.messageDao().deleteMessagesBetweenUsers(