From 75214737b9e27c2dcf5e3b75cce80c2a2e873706 Mon Sep 17 00:00:00 2001 From: benya Date: Sun, 5 Apr 2026 15:04:01 +0300 Subject: [PATCH] fix: reconcile realtime mention counters locally fix: update unread mention badges from websocket events fix: clear stale notifications when active chats receive messages --- .../messenger/data/chat/local/dao/ChatDao.kt | 6 ++- .../usecase/HandleRealtimeEventsUseCase.kt | 37 +++++++++++-------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/android/app/src/main/java/ru/daemonlord/messenger/data/chat/local/dao/ChatDao.kt b/android/app/src/main/java/ru/daemonlord/messenger/data/chat/local/dao/ChatDao.kt index 38efb20..c10f9a7 100644 --- a/android/app/src/main/java/ru/daemonlord/messenger/data/chat/local/dao/ChatDao.kt +++ b/android/app/src/main/java/ru/daemonlord/messenger/data/chat/local/dao/ChatDao.kt @@ -136,11 +136,15 @@ interface ChatDao { SET unread_count = CASE WHEN :incrementBy > 0 THEN unread_count + :incrementBy ELSE unread_count + END, + unread_mentions_count = CASE + WHEN :mentionIncrementBy > 0 THEN unread_mentions_count + :mentionIncrementBy + ELSE unread_mentions_count END WHERE id = :chatId """ ) - suspend fun incrementUnread(chatId: Long, incrementBy: Int = 1) + suspend fun incrementUnread(chatId: Long, incrementBy: Int = 1, mentionIncrementBy: Int = 0) @Query( """ diff --git a/android/app/src/main/java/ru/daemonlord/messenger/domain/realtime/usecase/HandleRealtimeEventsUseCase.kt b/android/app/src/main/java/ru/daemonlord/messenger/domain/realtime/usecase/HandleRealtimeEventsUseCase.kt index 8eba8b3..296db63 100644 --- a/android/app/src/main/java/ru/daemonlord/messenger/domain/realtime/usecase/HandleRealtimeEventsUseCase.kt +++ b/android/app/src/main/java/ru/daemonlord/messenger/domain/realtime/usecase/HandleRealtimeEventsUseCase.kt @@ -54,6 +54,21 @@ class HandleRealtimeEventsUseCase @Inject constructor( val activeChatId = activeChatTracker.activeChatId.value val activeUserId = tokenRepository.getActiveUserId() val isOwnMessage = activeUserId != null && event.senderId == activeUserId + val myUsername = activeUserId?.let { userId -> + tokenRepository.getAccounts() + .firstOrNull { it.userId == userId } + ?.username + ?.trim() + ?.removePrefix("@") + ?.lowercase() + } + val isMentionByText = if (myUsername.isNullOrBlank()) { + false + } else { + Regex("(^|\\W)@${Regex.escape(myUsername)}(\\W|$)", RegexOption.IGNORE_CASE) + .containsMatchIn(event.text.orEmpty()) + } + val isMention = event.isMention || isMentionByText val lastMessagePreview = event.text?.takeIf { it.isNotBlank() } ?: fallbackMessagePreview(event.type) messageDao.upsertMessages( @@ -89,25 +104,15 @@ class HandleRealtimeEventsUseCase @Inject constructor( if (activeChatId == event.chatId) { chatDao.markChatRead(chatId = event.chatId) messageRepository.syncRecentMessages(chatId = event.chatId) + notificationDispatcher.clearChatNotifications(event.chatId) } else if (!isOwnMessage) { - chatDao.incrementUnread(chatId = event.chatId) + chatDao.incrementUnread( + chatId = event.chatId, + incrementBy = 1, + mentionIncrementBy = if (isMention) 1 else 0, + ) } chatRepository.refreshChat(chatId = event.chatId) - val myUsername = activeUserId?.let { userId -> - tokenRepository.getAccounts() - .firstOrNull { it.userId == userId } - ?.username - ?.trim() - ?.removePrefix("@") - ?.lowercase() - } - val isMentionByText = if (myUsername.isNullOrBlank()) { - false - } else { - Regex("(^|\\W)@${Regex.escape(myUsername)}(\\W|$)", RegexOption.IGNORE_CASE) - .containsMatchIn(event.text.orEmpty()) - } - val isMention = event.isMention || isMentionByText val muted = chatDao.isChatMuted(event.chatId) == true val shouldNotify = shouldShowMessageNotificationUseCase( chatId = event.chatId,