diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt index fcde1fde6..7770df44e 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt @@ -406,7 +406,7 @@ class Account( ) } - broadcastPrivately(giftWraps) + broadcastPrivately(NIP24Factory.Result(senderReaction, giftWraps)) } else { val giftWraps = NIP24Factory().createReactionWithinGroup( emojiUrl = emojiUrl, @@ -459,7 +459,7 @@ class Account( recipientPubKey = it ) - broadcastPrivately(listOf(giftWraps)) + broadcastPrivately(NIP24Factory.Result(senderReaction, listOf(giftWraps))) } } else { val giftWraps = NIP24Factory().createReactionWithinGroup( @@ -1662,7 +1662,7 @@ class Account( event = sealedEvent, recipientPubKey = it ) - broadcastPrivately(listOf(giftWraps)) + broadcastPrivately(NIP24Factory.Result(chatMessageEvent, listOf(giftWraps))) } } } else { @@ -1683,42 +1683,53 @@ class Account( } } - fun broadcastPrivately(signedEvents: List) { - signedEvents.forEach { - Client.send(it) + fun broadcastPrivately(signedEvents: NIP24Factory.Result) { + val mine = signedEvents.wraps.filter { + (it.recipientPubKey() == keyPair.pubKey.toHexKey()) + } + mine.forEach { // Only keep in cache the GiftWrap for the account. - if (it.recipientPubKey() == keyPair.pubKey.toHexKey()) { - if (loginWithExternalSigner) { - ExternalSignerUtils.decrypt(it.content, it.pubKey, it.id, SignerType.NIP44_DECRYPT) - val decryptedContent = ExternalSignerUtils.cachedDecryptedContent[it.id] ?: "" - if (decryptedContent.isEmpty()) return - it.cachedGift(keyPair.pubKey, decryptedContent)?.let { cached -> - if (cached is SealedGossipEvent) { - ExternalSignerUtils.decrypt(cached.content, cached.pubKey, cached.id, SignerType.NIP44_DECRYPT) - val localDecryptedContent = ExternalSignerUtils.cachedDecryptedContent[cached.id] ?: "" - if (localDecryptedContent.isEmpty()) return - cached.cachedGossip(keyPair.pubKey, localDecryptedContent)?.let { gossip -> - LocalCache.justConsume(gossip, null) - } - } else { - LocalCache.justConsume(it, null) - } - } - } else { - it.cachedGift(keyPair.privKey!!)?.let { - if (it is SealedGossipEvent) { - it.cachedGossip(keyPair.privKey!!)?.let { - LocalCache.justConsume(it, null) - } - } else { - LocalCache.justConsume(it, null) + if (loginWithExternalSigner) { + ExternalSignerUtils.decrypt(it.content, it.pubKey, it.id, SignerType.NIP44_DECRYPT) + val decryptedContent = ExternalSignerUtils.cachedDecryptedContent[it.id] ?: "" + if (decryptedContent.isEmpty()) return + it.cachedGift(keyPair.pubKey, decryptedContent)?.let { cached -> + if (cached is SealedGossipEvent) { + ExternalSignerUtils.decrypt(cached.content, cached.pubKey, cached.id, SignerType.NIP44_DECRYPT) + val localDecryptedContent = ExternalSignerUtils.cachedDecryptedContent[cached.id] ?: "" + if (localDecryptedContent.isEmpty()) return + cached.cachedGossip(keyPair.pubKey, localDecryptedContent)?.let { gossip -> + LocalCache.justConsume(gossip, null) } + } else { + LocalCache.justConsume(it, null) + } + } + } else { + it.cachedGift(keyPair.privKey!!)?.let { + if (it is SealedGossipEvent) { + it.cachedGossip(keyPair.privKey!!)?.let { + LocalCache.justConsume(it, null) + } + } else { + LocalCache.justConsume(it, null) } } - - LocalCache.consume(it, null) } + + LocalCache.consume(it, null) + } + + val mineNote = LocalCache.getNoteIfExists(mine.first().id) + + signedEvents.wraps.forEach { + // Creates an alias + if (mineNote != null && it.recipientPubKey() != keyPair.pubKey.toHexKey()) { + LocalCache.getOrAddAliasNote(it.id, mineNote) + } + + Client.send(it) } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt index 74493605e..729ff274b 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt @@ -166,6 +166,18 @@ object LocalCache { return null } + fun getOrAddAliasNote(idHex: String, note: Note): Note { + checkNotInMainThread() + + return notes.get(idHex) ?: run { + require(isValidHex(idHex)) { + "$idHex is not a valid hex" + } + + notes.putIfAbsent(idHex, note) ?: note + } + } + fun getOrCreateNote(idHex: String): Note { checkNotInMainThread() diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/NostrAccountDataSource.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/NostrAccountDataSource.kt index 90467bf12..eb612c223 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/NostrAccountDataSource.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/NostrAccountDataSource.kt @@ -17,6 +17,7 @@ import com.vitorpamplona.quartz.events.ChannelMessageEvent import com.vitorpamplona.quartz.events.ContactListEvent import com.vitorpamplona.quartz.events.EmojiPackSelectionEvent import com.vitorpamplona.quartz.events.Event +import com.vitorpamplona.quartz.events.EventInterface import com.vitorpamplona.quartz.events.GenericRepostEvent import com.vitorpamplona.quartz.events.GiftWrapEvent import com.vitorpamplona.quartz.events.LnZapEvent @@ -208,6 +209,28 @@ object NostrAccountDataSource : NostrDataSource("AccountData") { } } + override fun markAsSeenOnRelay(eventId: String, relay: Relay) { + super.markAsSeenOnRelay(eventId, relay) + + val note = LocalCache.getNoteIfExists(eventId) ?: return + val privKey = account.keyPair.privKey ?: return + + val noteEvent = note.event ?: return + markInnerAsSeenOnRelay(noteEvent, privKey, relay) + } + + private fun markInnerAsSeenOnRelay(noteEvent: EventInterface, privKey: ByteArray, relay: Relay) { + LocalCache.getNoteIfExists(noteEvent.id())?.addRelay(relay) + + if (noteEvent is GiftWrapEvent) { + val gift = noteEvent.cachedGift(privKey) ?: return + markInnerAsSeenOnRelay(gift, privKey, relay) + } else if (noteEvent is SealedGossipEvent) { + val rumor = noteEvent.cachedGossip(privKey) ?: return + markInnerAsSeenOnRelay(rumor, privKey, relay) + } + } + override fun updateChannelFilters() { return if (hasLoadedTheBasics[account.userProfile()] != null) { // gets everthing about the user logged in diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/NIP24Factory.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/NIP24Factory.kt index 4fa6945a9..44fe02a65 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/NIP24Factory.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/NIP24Factory.kt @@ -7,6 +7,8 @@ import com.vitorpamplona.quartz.encoders.ATag import com.vitorpamplona.quartz.encoders.HexKey class NIP24Factory { + data class Result(val msg: Event, val wraps: List) + fun createMsgNIP24( msg: String, to: List, @@ -18,7 +20,7 @@ class NIP24Factory { markAsSensitive: Boolean = false, zapRaiserAmount: Long? = null, geohash: String? = null - ): List { + ): Result { val senderPublicKey = keyPair.pubKey.toHexKey() val senderMessage = ChatMessageEvent.create( @@ -34,19 +36,22 @@ class NIP24Factory { geohash = geohash ) - return to.plus(senderPublicKey).map { - GiftWrapEvent.create( - event = SealedGossipEvent.create( - event = senderMessage, - encryptTo = it, - privateKey = keyPair.privKey!! - ), - recipientPubKey = it - ) - } + return Result( + msg = senderMessage, + wraps = to.plus(senderPublicKey).map { + GiftWrapEvent.create( + event = SealedGossipEvent.create( + event = senderMessage, + encryptTo = it, + privateKey = keyPair.privKey!! + ), + recipientPubKey = it + ) + } + ) } - fun createReactionWithinGroup(content: String, originalNote: EventInterface, to: List, from: KeyPair): List { + fun createReactionWithinGroup(content: String, originalNote: EventInterface, to: List, from: KeyPair): Result { val senderPublicKey = from.pubKey.toHexKey() val senderReaction = ReactionEvent.create( @@ -55,19 +60,22 @@ class NIP24Factory { from ) - return to.plus(senderPublicKey).map { - GiftWrapEvent.create( - event = SealedGossipEvent.create( - event = senderReaction, - encryptTo = it, - privateKey = from.privKey!! - ), - recipientPubKey = it - ) - } + return Result( + msg = senderReaction, + wraps = to.plus(senderPublicKey).map { + GiftWrapEvent.create( + event = SealedGossipEvent.create( + event = senderReaction, + encryptTo = it, + privateKey = from.privKey!! + ), + recipientPubKey = it + ) + } + ) } - fun createReactionWithinGroup(emojiUrl: EmojiUrl, originalNote: EventInterface, to: List, from: KeyPair): List { + fun createReactionWithinGroup(emojiUrl: EmojiUrl, originalNote: EventInterface, to: List, from: KeyPair): Result { val senderPublicKey = from.pubKey.toHexKey() val senderReaction = ReactionEvent.create( @@ -76,16 +84,19 @@ class NIP24Factory { from ) - return to.plus(senderPublicKey).map { - GiftWrapEvent.create( - event = SealedGossipEvent.create( - event = senderReaction, - encryptTo = it, - privateKey = from.privKey!! - ), - recipientPubKey = it - ) - } + return Result( + msg = senderReaction, + wraps = to.plus(senderPublicKey).map { + GiftWrapEvent.create( + event = SealedGossipEvent.create( + event = senderReaction, + encryptTo = it, + privateKey = from.privKey!! + ), + recipientPubKey = it + ) + } + ) } fun createTextNoteNIP24( @@ -103,7 +114,7 @@ class NIP24Factory { directMentions: Set, zapRaiserAmount: Long? = null, geohash: String? = null - ): List { + ): Result { val senderPublicKey = keyPair.pubKey.toHexKey() val senderMessage = TextNoteEvent.create( @@ -122,15 +133,18 @@ class NIP24Factory { geohash = geohash ) - return to.plus(senderPublicKey).map { - GiftWrapEvent.create( - event = SealedGossipEvent.create( - event = senderMessage, - encryptTo = it, - privateKey = keyPair.privKey!! - ), - recipientPubKey = it - ) - } + return Result( + msg = senderMessage, + wraps = to.plus(senderPublicKey).map { + GiftWrapEvent.create( + event = SealedGossipEvent.create( + event = senderMessage, + encryptTo = it, + privateKey = keyPair.privKey!! + ), + recipientPubKey = it + ) + } + ) } }