fix: render p-tagged NIP-29 group replies on the Notifications tab

A reply to my message inside a joined NIP-29 relay group is a kind-9
ChatEvent that p-tags me (see ChannelNewMessageViewModel). It is already
fetched at startup by filterGroupNotificationsToPubkey (scoped `#p`=me +
`#h`=my groups on the group's host relay), but NotificationFeedFilter's
`acceptableEvent` checks `kind in NOTIFICATION_KINDS` before the p-tag
gate, and kind 9 (ChatEvent) was missing from that set — so those replies
were dropped before ever reaching the p-tag check and never surfaced.

Add ChatEvent.KIND to NOTIFICATION_KINDS. The existing gates handle the
rest: my own messages are excluded (author != me), the reply p-tags me
(isTaggedUser), and its `q`-tag to my message makes it pass
tagsAnEventByUser in Selected mode too. Pin the behaviour with a tripwire
test in NotificationKindsContractTest.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VxpT7J4xt37EF5yJDw1htK
This commit is contained in:
Claude
2026-07-14 22:45:25 +00:00
parent 9d5bd48164
commit ccd4677f5d
2 changed files with 27 additions and 0 deletions
@@ -81,6 +81,7 @@ import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceEvent
import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceReplyEvent
import com.vitorpamplona.quartz.nipA4PublicMessages.PublicMessageEvent
import com.vitorpamplona.quartz.nipBCOnchainZaps.zap.OnchainZapEvent
import com.vitorpamplona.quartz.nipC7Chats.ChatEvent
import com.vitorpamplona.quartz.nipF4Podcasts.episode.PodcastEpisodeEvent
import com.vitorpamplona.quartz.nipF4Podcasts.metadata.PodcastMetadataEvent
import kotlinx.coroutines.flow.MutableStateFlow
@@ -136,6 +137,12 @@ class NotificationFeedFilter(
setOf(
BadgeAwardEvent.KIND,
ChannelMessageEvent.KIND,
// NIP-29 group chat (kind 9). A reply to my group message is a
// kind-9 that p-tags me (see ChannelNewMessageViewModel), fetched
// at startup by filterGroupNotificationsToPubkey. Without kind 9
// here the acceptableEvent kind gate drops it before the p-tag
// check, so those replies never render on the Notifications tab.
ChatEvent.KIND,
ChatMessageEvent.KIND,
ChatMessageEncryptedFileHeaderEvent.KIND,
CommentEvent.KIND,
@@ -23,6 +23,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.dal
import com.vitorpamplona.amethyst.commons.moderation.notifications.NotificationKinds
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.EphemeralGiftWrapEvent
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
import com.vitorpamplona.quartz.nipC7Chats.ChatEvent
import org.junit.Assert.assertTrue
import org.junit.Test
@@ -73,4 +74,23 @@ class NotificationKindsContractTest {
unaccounted.isEmpty(),
)
}
/**
* A reply to my message inside a NIP-29 relay group is a kind-9 [ChatEvent]
* that p-tags me. It is fetched at startup by `filterGroupNotificationsToPubkey`
* (scoped `#p`=me + `#h`=my groups on the group's host relay), but the
* `acceptableEvent` gate first checks `kind in NOTIFICATION_KINDS`, so without
* kind 9 in the set the reply is dropped before the p-tag check and never
* surfaces on the Notifications tab. Pin its presence so it can't silently
* regress.
*/
@Test
fun `nip-29 group chat replies render on the Android notifications tab`() {
assertTrue(
"ChatEvent.KIND (9) is missing from NOTIFICATION_KINDS. NIP-29 group " +
"replies that p-tag the user would be dropped by the acceptableEvent " +
"kind gate before the p-tag check and never notify.",
ChatEvent.KIND in NotificationFeedFilter.NOTIFICATION_KINDS,
)
}
}