Анимация удаления сообщений (Telegram-style): shrink + fade out 250ms
Двухэтапное удаление: pendingDeleteIds → AnimatedVisibility(shrinkVertically + fadeOut) → remove. Остальные сообщения плавно сдвигаются на место удалённого. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -803,6 +803,7 @@ fun ChatDetailScreen(
|
||||
// <20>🔥 Reply/Forward state
|
||||
val replyMessages by viewModel.replyMessages.collectAsState()
|
||||
val isForwardMode by viewModel.isForwardMode.collectAsState()
|
||||
val pendingDeleteIds by viewModel.pendingDeleteIds.collectAsState()
|
||||
|
||||
// Avatar-сообщения не должны попадать в selection ни при каких условиях.
|
||||
val avatarMessageIds =
|
||||
@@ -3120,6 +3121,15 @@ fun ChatDetailScreen(
|
||||
isTailPhase &&
|
||||
isGroupStart))
|
||||
|
||||
val isDeleting = message.id in pendingDeleteIds
|
||||
androidx.compose.animation.AnimatedVisibility(
|
||||
visible = !isDeleting,
|
||||
exit = androidx.compose.animation.shrinkVertically(
|
||||
animationSpec = androidx.compose.animation.core.tween(250, easing = androidx.compose.animation.core.FastOutSlowInEasing)
|
||||
) + androidx.compose.animation.fadeOut(
|
||||
animationSpec = androidx.compose.animation.core.tween(200)
|
||||
)
|
||||
) {
|
||||
Column {
|
||||
if (showDate
|
||||
) {
|
||||
@@ -3527,6 +3537,7 @@ fun ChatDetailScreen(
|
||||
} // contextMenuContent
|
||||
)
|
||||
}
|
||||
} // AnimatedVisibility
|
||||
}
|
||||
}
|
||||
androidx.compose.animation.AnimatedVisibility(
|
||||
|
||||
@@ -203,6 +203,10 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
||||
val replyMessages: StateFlow<List<ReplyMessage>> = _replyMessages.asStateFlow()
|
||||
|
||||
private val _isForwardMode = MutableStateFlow(false)
|
||||
|
||||
// Animated deletion: IDs of messages currently animating out
|
||||
private val _pendingDeleteIds = MutableStateFlow<Set<String>>(emptySet())
|
||||
val pendingDeleteIds: StateFlow<Set<String>> = _pendingDeleteIds.asStateFlow()
|
||||
val isForwardMode: StateFlow<Boolean> = _isForwardMode.asStateFlow()
|
||||
|
||||
// 📌 Pinned messages state
|
||||
@@ -2660,22 +2664,28 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
||||
val opponent = opponentKey ?: return
|
||||
val dialogKey = getDialogKey(account, opponent)
|
||||
|
||||
// Удаляем из UI сразу на main
|
||||
val updatedMessages = _messages.value.filter { it.id != messageId }
|
||||
_messages.value = updatedMessages
|
||||
// Синхронизируем глобальный кэш диалога, иначе удалённые сообщения могут вернуться
|
||||
// при повторном открытии чата из stale cache.
|
||||
updateCacheWithLimit(account, dialogKey, updatedMessages)
|
||||
messageRepository.clearDialogCache(opponent)
|
||||
// 1. Mark as pending delete (triggers shrink+fade animation)
|
||||
_pendingDeleteIds.value = _pendingDeleteIds.value + messageId
|
||||
|
||||
// Удаляем из БД в IO + удаляем pin если был
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
pinnedMessageDao.removePin(account, dialogKey, messageId)
|
||||
messageDao.deleteMessage(account, messageId)
|
||||
if (account == opponent) {
|
||||
dialogDao.updateSavedMessagesDialogFromMessages(account)
|
||||
} else {
|
||||
dialogDao.updateDialogFromMessages(account, opponent)
|
||||
// 2. After animation completes, remove from list and DB
|
||||
viewModelScope.launch {
|
||||
kotlinx.coroutines.delay(300) // wait for animation
|
||||
|
||||
val updatedMessages = _messages.value.filter { it.id != messageId }
|
||||
_messages.value = updatedMessages
|
||||
_pendingDeleteIds.value = _pendingDeleteIds.value - messageId
|
||||
|
||||
updateCacheWithLimit(account, dialogKey, updatedMessages)
|
||||
messageRepository.clearDialogCache(opponent)
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
pinnedMessageDao.removePin(account, dialogKey, messageId)
|
||||
messageDao.deleteMessage(account, messageId)
|
||||
if (account == opponent) {
|
||||
dialogDao.updateSavedMessagesDialogFromMessages(account)
|
||||
} else {
|
||||
dialogDao.updateDialogFromMessages(account, opponent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user