diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt index 91234aadf7..7c67921322 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt @@ -2632,6 +2632,10 @@ class Account( Log.d("MarmotDbg") { "sendMarmotGroupMessage: built outer kind:${outbound.signedEvent.kind} id=${outbound.signedEvent.id.take(8)}…" } + // Link the envelope to the inner message we just encrypted so relay + // OK acceptances drill down to the note the chat renders (see + // LocalCache.addRelayToNoteAndInners). + outbound.signedEvent.innerEventId = innerEvent.id cache.justConsumeMyOwnEvent(outbound.signedEvent) // Sending a message moves the group out of "New Requests" into // "Known" — do this eagerly before relay round-trip so the UI diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/DecryptAndIndexProcessor.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/DecryptAndIndexProcessor.kt index e05a9627a1..7c5eb319e8 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/DecryptAndIndexProcessor.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/DecryptAndIndexProcessor.kt @@ -617,6 +617,18 @@ class GroupEventHandler( } } + // Link the envelope to its inner note and copy over the + // relays that delivered/accepted the kind-445 so far, so + // the chat row shows relay icons. Later acceptances drill + // down on their own via LocalCache.addRelayToNoteAndInners + // once innerEventId is set. The outer note is looked up by + // event.id — NOT eventNote/publicNote, which belong to the + // *triggering* event when this runs from retryPendingFor. + event.innerEventId = innerEvent.id + cache.getNoteIfExists(event.id)?.let { outerNote -> + cache.copyRelaysFromTo(outerNote, innerEvent.id) + } + // Track the message in the Marmot group chatroom account.marmotGroupList.addMessage(result.groupId, innerNote) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/marmot/mip03GroupMessages/GroupEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/marmot/mip03GroupMessages/GroupEvent.kt index 396aacdcd7..de02441755 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/marmot/mip03GroupMessages/GroupEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/marmot/mip03GroupMessages/GroupEvent.kt @@ -25,7 +25,9 @@ import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate +import com.vitorpamplona.quartz.nip59Giftwrap.HasInnerEvent import com.vitorpamplona.quartz.utils.TimeUtils +import kotlin.concurrent.Volatile /** * Marmot Group Event (MIP-03) — kind 445. @@ -59,7 +61,19 @@ class GroupEvent( tags: Array>, content: String, sig: HexKey, -) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { +) : Event(id, pubKey, createdAt, KIND, tags, content, sig), + HasInnerEvent { + // Set when the app layer learns the envelope ↔ inner mapping: on decrypt + // for inbound events, at build time for outbound ones. Lets relay + // attribution (OK acceptances, duplicate deliveries) drill from the + // kind-445 envelope down to the inner note the chat UI renders. + // `@Volatile`: written by the decrypt coroutine, read by relay socket + // threads. + @kotlinx.serialization.Transient + @kotlin.jvm.Transient + @Volatile + override var innerEventId: HexKey? = null + /** * Base64-encoded encrypted content: nonce(12 bytes) || ciphertext. * Decrypt with ChaCha20-Poly1305 using the MLS exporter-derived key.