diff --git a/app/src/main/java/com/rosetta/messenger/push/RosettaFirebaseMessagingService.kt b/app/src/main/java/com/rosetta/messenger/push/RosettaFirebaseMessagingService.kt index 27c8ae0..d8b9633 100644 --- a/app/src/main/java/com/rosetta/messenger/push/RosettaFirebaseMessagingService.kt +++ b/app/src/main/java/com/rosetta/messenger/push/RosettaFirebaseMessagingService.kt @@ -73,10 +73,13 @@ class RosettaFirebaseMessagingService : FirebaseMessagingService() { val variants = buildDialogKeyVariants(senderPublicKey) for (key in variants) { notificationManager.cancel(getNotificationIdForChat(key)) + lastNotifTimestamps.remove(key) } // Fallback: некоторые серверные payload могут прийти без sender key. // Для них используется ID от пустой строки — тоже очищаем при входе в диалог. notificationManager.cancel(getNotificationIdForChat("")) + lastNotifTimestamps.remove("__no_sender__") + lastNotifTimestamps.remove("__simple__") } private fun buildDialogKeyVariants(rawKey: String): Set { @@ -196,8 +199,14 @@ class RosettaFirebaseMessagingService : FirebaseMessagingService() { when { isReadEvent -> { - if (!dialogKey.isNullOrBlank()) { - cancelNotificationForChat(applicationContext, dialogKey) + val keysToClear = collectReadDialogKeys(data, dialogKey, senderPublicKey) + if (keysToClear.isEmpty()) { + Log.d(TAG, "READ push received but no dialog key in payload: $data") + } else { + keysToClear.forEach { key -> + cancelNotificationForChat(applicationContext, key) + } + Log.d(TAG, "READ push cleared notifications for keys=$keysToClear") } handledByData = true } @@ -541,6 +550,44 @@ class RosettaFirebaseMessagingService : FirebaseMessagingService() { return null } + /** + * Builds a robust list of dialog keys for silent READ pushes. + * Server payloads may evolve (dialog/from/to/peer/group_* aliases), so we parse + * all known aliases and exclude current account public key. + */ + private fun collectReadDialogKeys( + data: Map, + parsedDialogKey: String?, + parsedSenderKey: String? + ): Set { + val currentAccount = AccountManager(applicationContext).getLastLoggedPublicKey().orEmpty().trim() + val candidates = linkedSetOf() + + fun addCandidate(raw: String?) { + val value = raw?.trim().orEmpty() + if (value.isBlank()) return + if (currentAccount.isNotBlank() && value == currentAccount) return + candidates.add(value) + } + + addCandidate(parsedDialogKey) + addCandidate(parsedSenderKey) + addCandidate(firstNonBlank(data, "dialog", "dialog_key", "dialogPublicKey", "dialog_public_key")) + addCandidate(firstNonBlank(data, "peer", "peer_key", "peerPublicKey", "peer_public_key", "chat", "chat_key")) + addCandidate(firstNonBlank(data, "to", "toPublicKey", "to_public_key", "dst", "dst_public_key")) + addCandidate(firstNonBlank(data, "from", "fromPublicKey", "from_public_key", "src", "src_public_key")) + + // Group aliases from some server payloads + val groupId = firstNonBlank(data, "group", "group_id", "groupId") + if (!groupId.isNullOrBlank()) { + addCandidate(groupId) + addCandidate("group:$groupId") + addCandidate("#group:$groupId") + } + + return candidates + } + private fun isAvatarInNotificationsEnabled(): Boolean { return runCatching { runBlocking(Dispatchers.IO) {