fix(notifications): route Welcome events via direct invocation
WelcomeEvent (kind 444) has no `p` tag, so the cache-observer path's tag-based account matching in consumeFromCache silently dropped them. Route Welcomes instead via a dedicated notifyWelcome entry point invoked from processMarmotWelcomeFlow — the one place we reliably know which account the invite was for (the one whose signer just unsealed it and joined the MLS group). Drop the dead processWelcome workaround from the old notify(WelcomeEvent) body; with the cache-first architecture, MLS processing always completes before the notification fires. - NotificationDispatcher: remove WelcomeEvent from the observer filter, expose a public `notifyWelcome(event, account)` entry. - EventNotificationConsumer: rename notify(WelcomeEvent) to public notifyWelcome, add notification-enabled + foreground-suppression checks, drop the manager.processWelcome fallback. - DecryptAndIndexProcessor: on WelcomeResult.Joined, invoke Amethyst.instance.notificationDispatcher.notifyWelcome to fire the "You've been added to <group>" notification.
This commit is contained in:
+17
-27
@@ -49,7 +49,6 @@ import com.vitorpamplona.amethyst.ui.MainActivity
|
||||
import com.vitorpamplona.amethyst.ui.note.showAmount
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.quartz.experimental.notifications.wake.WakeUpEvent
|
||||
import com.vitorpamplona.quartz.marmot.WelcomeResult
|
||||
import com.vitorpamplona.quartz.marmot.mip02Welcome.WelcomeEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.core.hexToByteArray
|
||||
@@ -179,7 +178,8 @@ class EventNotificationConsumer(
|
||||
is ReactionEvent -> notify(event, account)
|
||||
is LiveChessGameAcceptEvent -> notifyChessEvent(event, account, R.string.app_notification_chess_challenge_accepted)
|
||||
is LiveChessMoveEvent -> notifyChessEvent(event, account, R.string.app_notification_chess_your_turn)
|
||||
is WelcomeEvent -> notify(event, account)
|
||||
// WelcomeEvent is dispatched directly from processMarmotWelcomeFlow
|
||||
// (no `p` tag, so tag-based matching doesn't work).
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,38 +371,28 @@ class EventNotificationConsumer(
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun notify(
|
||||
/**
|
||||
* Welcomes have no `p` tag, so [consumeFromCache]'s tag-based account match
|
||||
* can't route them. They are instead dispatched here directly by
|
||||
* [com.vitorpamplona.amethyst.ui.screen.loggedIn.processMarmotWelcomeFlow]
|
||||
* after [MarmotManager.processWelcome] joins the group — which is also the
|
||||
* only place we reliably know which account the invite was for.
|
||||
*/
|
||||
suspend fun notifyWelcome(
|
||||
event: WelcomeEvent,
|
||||
account: Account,
|
||||
) {
|
||||
) = withWakeLock {
|
||||
Log.d(TAG, "New Marmot Welcome to Notify")
|
||||
|
||||
if (!notificationManager().areNotificationsEnabled()) return@withWakeLock
|
||||
if (MainActivity.isResumed) return@withWakeLock
|
||||
|
||||
// old event being re-broadcast
|
||||
if (event.createdAt < TimeUtils.fifteenMinutesAgo()) return
|
||||
if (event.createdAt < TimeUtils.fifteenMinutesAgo()) return@withWakeLock
|
||||
// a welcome we ourselves emitted
|
||||
if (event.pubKey == account.signer.pubKey) return
|
||||
if (event.pubKey == account.signer.pubKey) return@withWakeLock
|
||||
|
||||
val nostrGroupId = event.nostrGroupId() ?: return
|
||||
val manager = account.marmotManager ?: return
|
||||
|
||||
// Best-effort: process the welcome here so the chatroom is hydrated
|
||||
// before composing the notification body. The push-notification
|
||||
// background path does NOT go through Account.eventProcessor, so
|
||||
// without this the invitee would only join the group later, when
|
||||
// they next open the app and the relay subscription redelivers.
|
||||
if (!manager.isMember(nostrGroupId)) {
|
||||
try {
|
||||
val result = manager.processWelcome(event, nostrGroupId)
|
||||
if (result is WelcomeResult.Joined) {
|
||||
val chatroom = account.marmotGroupList.getOrCreateGroup(result.nostrGroupId)
|
||||
manager.syncMetadataTo(result.nostrGroupId, chatroom)
|
||||
account.marmotGroupList.notifyGroupChanged(result.nostrGroupId)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException) throw e
|
||||
Log.w(TAG) { "Failed to process Marmot Welcome from notification path: ${e.message}" }
|
||||
}
|
||||
}
|
||||
val nostrGroupId = event.nostrGroupId() ?: return@withWakeLock
|
||||
|
||||
val chatroom = account.marmotGroupList.getOrCreateGroup(nostrGroupId)
|
||||
val groupName = chatroom.displayName.value?.takeIf { it.isNotBlank() } ?: "a private group"
|
||||
|
||||
+24
-1
@@ -21,6 +21,7 @@
|
||||
package com.vitorpamplona.amethyst.service.notifications
|
||||
|
||||
import android.content.Context
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.quartz.experimental.notifications.wake.WakeUpEvent
|
||||
import com.vitorpamplona.quartz.marmot.mip02Welcome.WelcomeEvent
|
||||
@@ -63,6 +64,11 @@ class NotificationDispatcher(
|
||||
// listed here — by the time we care, Account.newNotesPreProcessor has
|
||||
// already unwrapped them and inserted the inner payload into LocalCache,
|
||||
// which fires the observer a second time on the inner event.
|
||||
//
|
||||
// WelcomeEvent (kind:444) is also excluded: it has no `p` tag, so
|
||||
// consumeFromCache can't route it. It's delivered directly via
|
||||
// [notifyWelcome] from processMarmotWelcomeFlow, which does know the
|
||||
// recipient account.
|
||||
private val NOTIFICATION_KINDS =
|
||||
listOf(
|
||||
// Direct-arrival
|
||||
@@ -75,7 +81,6 @@ class NotificationDispatcher(
|
||||
// Unwrapped from GiftWrap → Seal
|
||||
ChatMessageEvent.KIND,
|
||||
ChatMessageEncryptedFileHeaderEvent.KIND,
|
||||
WelcomeEvent.KIND,
|
||||
// Unwrapped from EphemeralGiftWrap
|
||||
CallOfferEvent.KIND,
|
||||
)
|
||||
@@ -106,4 +111,22 @@ class NotificationDispatcher(
|
||||
job?.cancel()
|
||||
job = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Direct-invocation entry point for [WelcomeEvent]. Bypasses the
|
||||
* cache-observer path because Welcomes have no `p` tag for account
|
||||
* routing. Called from processMarmotWelcomeFlow once MLS group join
|
||||
* succeeds — at which point we know which account the invite was for.
|
||||
*/
|
||||
suspend fun notifyWelcome(
|
||||
event: WelcomeEvent,
|
||||
account: Account,
|
||||
) {
|
||||
try {
|
||||
consumer.notifyWelcome(event, account)
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException) throw e
|
||||
Log.e(TAG, "Failed to dispatch Welcome notification ${event.id}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn
|
||||
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.commons.call.CallManager
|
||||
import com.vitorpamplona.amethyst.commons.model.privateChats.ChatroomList
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
@@ -397,6 +398,11 @@ private suspend fun processMarmotWelcomeFlow(
|
||||
if (result.needsKeyPackageRotation) {
|
||||
account.publishMarmotKeyPackages()
|
||||
}
|
||||
|
||||
// Fire the "You've been added to <group>" notification. Welcomes
|
||||
// have no `p` tag, so the cache-observer path can't route them;
|
||||
// this is the single point where we know the recipient account.
|
||||
Amethyst.instance.notificationDispatcher.notifyWelcome(innerEvent, account)
|
||||
}
|
||||
|
||||
is WelcomeResult.AlreadyJoined -> {
|
||||
|
||||
Reference in New Issue
Block a user