From d4e71881e835a9a92219902932e9ae7f704f2994 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 9 Jul 2026 23:17:03 +0000 Subject: [PATCH] fix: sign a q quote-tag when replying to a kind-9 group message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The NIP-29 group composer always built replies with ChatEvent.build and ignored replyTo entirely, so a reply to a kind-9 message quoted its parent in the UI but the signed event carried no NIP-18 `q` tag (and no `p` notify to the author) — unlike the public-chat and live-activity paths, which use `.reply(...)`. Route group replies through ChatEvent.reply when replyTo is set: it emits the `q` quote tag, and we add a `p` tag to the parent's author. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01B5MLY4hq5LXJ2D5WeLRyXj --- .../send/ChannelNewMessageViewModel.kt | 40 ++++++++++++++----- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/ChannelNewMessageViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/ChannelNewMessageViewModel.kt index 87d0976198..f870b0595b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/ChannelNewMessageViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/ChannelNewMessageViewModel.kt @@ -77,6 +77,7 @@ import com.vitorpamplona.quartz.nip01Core.tags.events.ETag import com.vitorpamplona.quartz.nip01Core.tags.geohash.geohash import com.vitorpamplona.quartz.nip01Core.tags.geohash.getGeoHash import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags +import com.vitorpamplona.quartz.nip01Core.tags.people.pTag import com.vitorpamplona.quartz.nip01Core.tags.people.toPTag import com.vitorpamplona.quartz.nip01Core.tags.references.references import com.vitorpamplona.quartz.nip10Notes.content.findHashtags @@ -550,17 +551,38 @@ open class ChannelNewMessageViewModel : // NIP-29 group message: a kind-9 chat scoped to the group with an // `h` tag. The event is published only to the group's host relay // (channel.relays()), where relay29 authorizes and routes it. - ChatEvent.build(tagger.message) { - hTag(channel.groupId.id) + val replyingToEvent = replyTo.value?.toEventHint() + if (replyingToEvent != null) { + // A reply quotes its parent via a NIP-18 `q` tag (added by ChatEvent.reply) + // + a `p` notify to its author. The group scope (`h`) alone doesn't link a + // reply to what it answers — without the `q` tag the quote renders in the + // composer but is missing from the signed event. + ChatEvent.reply(tagger.message, replyingToEvent) { + hTag(channel.groupId.id) + pTag(replyingToEvent.toPTag()) - hashtags(findHashtags(tagger.message)) - references(findURLs(tagger.message)) - quotes(findNostrUris(tagger.message)) - contentWarningReason?.let { contentWarning(it) } - localExpirationDate?.let { expiration(it) } + hashtags(findHashtags(tagger.message)) + references(findURLs(tagger.message)) + quotes(findNostrUris(tagger.message)) + contentWarningReason?.let { contentWarning(it) } + localExpirationDate?.let { expiration(it) } - emojis(emojis) - imetas(usedAttachments) + emojis(emojis) + imetas(usedAttachments) + } + } else { + ChatEvent.build(tagger.message) { + hTag(channel.groupId.id) + + hashtags(findHashtags(tagger.message)) + references(findURLs(tagger.message)) + quotes(findNostrUris(tagger.message)) + contentWarningReason?.let { contentWarning(it) } + localExpirationDate?.let { expiration(it) } + + emojis(emojis) + imetas(usedAttachments) + } } }