mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-11-11 05:16:36 +01:00
Improves caching of encrypted DMs:
- Migrates caching of decrypted value outside of the Event class - Removes encrypted parts of NIP-17 from the cache - Removes old NIP-04 messages from the cache - Avoids deleting new NIP-17 plain text chats from memory
This commit is contained in:
@@ -2290,12 +2290,14 @@ class Account(
|
|||||||
val mine = signedEvents.wraps.filter { (it.recipientPubKey() == signer.pubKey) }
|
val mine = signedEvents.wraps.filter { (it.recipientPubKey() == signer.pubKey) }
|
||||||
|
|
||||||
mine.forEach { giftWrap ->
|
mine.forEach { giftWrap ->
|
||||||
giftWrap.cachedGift(signer) { gift ->
|
giftWrap.unwrap(signer) { gift ->
|
||||||
if (gift is SealedGossipEvent) {
|
if (gift is SealedGossipEvent) {
|
||||||
gift.cachedGossip(signer) { gossip -> LocalCache.justConsume(gossip, null) }
|
gift.unseal(signer) { gossip ->
|
||||||
} else {
|
LocalCache.justConsume(gossip, null)
|
||||||
LocalCache.justConsume(gift, null)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LocalCache.justConsume(gift, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalCache.consume(giftWrap, null)
|
LocalCache.consume(giftWrap, null)
|
||||||
@@ -2842,7 +2844,7 @@ class Account(
|
|||||||
) {
|
) {
|
||||||
if (!isWriteable()) return
|
if (!isWriteable()) return
|
||||||
|
|
||||||
return event.cachedGift(signer, onReady)
|
return event.unwrap(signer, onReady)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun unseal(
|
fun unseal(
|
||||||
@@ -2851,7 +2853,7 @@ class Account(
|
|||||||
) {
|
) {
|
||||||
if (!isWriteable()) return
|
if (!isWriteable()) return
|
||||||
|
|
||||||
return event.cachedGossip(signer, onReady)
|
return event.unseal(signer, onReady)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cachedDecryptContent(note: Note): String? = cachedDecryptContent(note.event)
|
fun cachedDecryptContent(note: Note): String? = cachedDecryptContent(note.event)
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ package com.vitorpamplona.amethyst.model
|
|||||||
|
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
||||||
|
import com.vitorpamplona.amethyst.ui.dal.DefaultFeedOrder
|
||||||
import com.vitorpamplona.quartz.encoders.HexKey
|
import com.vitorpamplona.quartz.encoders.HexKey
|
||||||
|
import com.vitorpamplona.quartz.events.PrivateDmEvent
|
||||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
@@ -75,7 +77,7 @@ class Chatroom {
|
|||||||
fun senderIntersects(keySet: Set<HexKey>): Boolean = authors.any { it.pubkeyHex in keySet }
|
fun senderIntersects(keySet: Set<HexKey>): Boolean = authors.any { it.pubkeyHex in keySet }
|
||||||
|
|
||||||
fun pruneMessagesToTheLatestOnly(): Set<Note> {
|
fun pruneMessagesToTheLatestOnly(): Set<Note> {
|
||||||
val sorted = roomMessages.sortedWith(compareBy({ it.createdAt() }, { it.idHex })).reversed()
|
val sorted = roomMessages.sortedWith(DefaultFeedOrder)
|
||||||
|
|
||||||
val toKeep =
|
val toKeep =
|
||||||
if ((sorted.firstOrNull()?.createdAt() ?: 0) > TimeUtils.oneWeekAgo()) {
|
if ((sorted.firstOrNull()?.createdAt() ?: 0) > TimeUtils.oneWeekAgo()) {
|
||||||
@@ -84,7 +86,7 @@ class Chatroom {
|
|||||||
} else {
|
} else {
|
||||||
// Old messages, keep the last one.
|
// Old messages, keep the last one.
|
||||||
sorted.take(1).toSet()
|
sorted.take(1).toSet()
|
||||||
} + sorted.filter { it.liveSet?.isInUse() ?: false }
|
} + sorted.filter { it.liveSet?.isInUse() ?: false } + sorted.filter { it.event !is PrivateDmEvent }
|
||||||
|
|
||||||
val toRemove = roomMessages.minus(toKeep)
|
val toRemove = roomMessages.minus(toKeep)
|
||||||
roomMessages = toKeep
|
roomMessages = toKeep
|
||||||
|
|||||||
@@ -299,99 +299,121 @@ object NostrAccountDataSource : AmethystNostrDataSource("AccountData") {
|
|||||||
override fun consume(
|
override fun consume(
|
||||||
event: Event,
|
event: Event,
|
||||||
relay: Relay,
|
relay: Relay,
|
||||||
|
) {
|
||||||
|
if (LocalCache.justVerify(event)) {
|
||||||
|
consumeAlreadyVerified(event, relay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun consumeAlreadyVerified(
|
||||||
|
event: Event,
|
||||||
|
relay: Relay,
|
||||||
) {
|
) {
|
||||||
checkNotInMainThread()
|
checkNotInMainThread()
|
||||||
|
|
||||||
if (LocalCache.justVerify(event)) {
|
when (event) {
|
||||||
when (event) {
|
is PrivateOutboxRelayListEvent -> {
|
||||||
is PrivateOutboxRelayListEvent -> {
|
val note = LocalCache.getAddressableNoteIfExists(event.addressTag())
|
||||||
|
val noteEvent = note?.event
|
||||||
|
if (noteEvent == null || event.createdAt > noteEvent.createdAt()) {
|
||||||
|
event.privateTags(account.signer) {
|
||||||
|
LocalCache.justConsume(event, relay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is DraftEvent -> {
|
||||||
|
// Avoid decrypting over and over again if the event already exist.
|
||||||
|
|
||||||
|
if (!event.isDeleted()) {
|
||||||
val note = LocalCache.getAddressableNoteIfExists(event.addressTag())
|
val note = LocalCache.getAddressableNoteIfExists(event.addressTag())
|
||||||
val noteEvent = note?.event
|
val noteEvent = note?.event
|
||||||
if (noteEvent == null || event.createdAt > noteEvent.createdAt()) {
|
|
||||||
event.privateTags(account.signer) {
|
|
||||||
LocalCache.justConsume(event, relay)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is DraftEvent -> {
|
|
||||||
// Avoid decrypting over and over again if the event already exist.
|
|
||||||
|
|
||||||
if (!event.isDeleted()) {
|
|
||||||
val note = LocalCache.getAddressableNoteIfExists(event.addressTag())
|
|
||||||
val noteEvent = note?.event
|
|
||||||
if (noteEvent != null) {
|
|
||||||
if (event.createdAt > noteEvent.createdAt() || relay.brief !in note.relays) {
|
|
||||||
LocalCache.consume(event, relay)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// decrypts
|
|
||||||
event.cachedDraft(account.signer) {}
|
|
||||||
|
|
||||||
LocalCache.justConsume(event, relay)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is GiftWrapEvent -> {
|
|
||||||
// Avoid decrypting over and over again if the event already exist.
|
|
||||||
val note = LocalCache.getNoteIfExists(event.id)
|
|
||||||
val noteEvent = note?.event as? GiftWrapEvent
|
|
||||||
if (noteEvent != null) {
|
if (noteEvent != null) {
|
||||||
if (relay.brief !in note.relays) {
|
if (event.createdAt > noteEvent.createdAt() || relay.brief !in note.relays) {
|
||||||
noteEvent.cachedGift(account.signer) {
|
LocalCache.consume(event, relay)
|
||||||
LocalCache.justConsume(noteEvent, relay)
|
|
||||||
this.consume(it, relay)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// new event
|
// decrypts
|
||||||
event.cachedGift(account.signer) {
|
event.cachedDraft(account.signer) {}
|
||||||
LocalCache.justConsume(event, relay)
|
|
||||||
this.consume(it, relay)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is SealedGossipEvent -> {
|
|
||||||
// Avoid decrypting over and over again if the event already exist.
|
|
||||||
val note = LocalCache.getNoteIfExists(event.id)
|
|
||||||
val noteEvent = note?.event as? SealedGossipEvent
|
|
||||||
if (noteEvent != null) {
|
|
||||||
if (relay.brief !in note.relays) {
|
|
||||||
// adds the relay to seal and inner chat
|
|
||||||
noteEvent.cachedGossip(account.signer) {
|
|
||||||
LocalCache.consume(noteEvent, relay)
|
|
||||||
LocalCache.justConsume(it, relay)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// new event
|
|
||||||
event.cachedGossip(account.signer) {
|
|
||||||
LocalCache.justConsume(event, relay)
|
|
||||||
LocalCache.justConsume(it, relay)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is LnZapEvent -> {
|
|
||||||
// Avoid decrypting over and over again if the event already exist.
|
|
||||||
val note = LocalCache.getNoteIfExists(event.id)
|
|
||||||
if (note?.event == null) {
|
|
||||||
event.zapRequest?.let {
|
|
||||||
if (it.isPrivateZap()) {
|
|
||||||
it.decryptPrivateZap(account.signer) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalCache.justConsume(event, relay)
|
LocalCache.justConsume(event, relay)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is GiftWrapEvent -> {
|
||||||
|
// Avoid decrypting over and over again if the event already exist.
|
||||||
|
val note = LocalCache.getNoteIfExists(event.id)
|
||||||
|
val noteEvent = note?.event as? GiftWrapEvent
|
||||||
|
if (noteEvent != null) {
|
||||||
|
if (relay.brief !in note.relays) {
|
||||||
|
LocalCache.justConsume(noteEvent, relay)
|
||||||
|
|
||||||
|
noteEvent.innerEventId?.let {
|
||||||
|
(LocalCache.getNoteIfExists(it)?.event as? Event)?.let {
|
||||||
|
this.consumeAlreadyVerified(it, relay)
|
||||||
|
}
|
||||||
|
} ?: run {
|
||||||
|
event.unwrap(account.signer) {
|
||||||
|
this.consume(it, relay)
|
||||||
|
noteEvent.innerEventId = it.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// new event
|
||||||
|
event.unwrap(account.signer) {
|
||||||
|
LocalCache.justConsume(event, relay)
|
||||||
|
this.consume(it, relay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is SealedGossipEvent -> {
|
||||||
|
// Avoid decrypting over and over again if the event already exist.
|
||||||
|
val note = LocalCache.getNoteIfExists(event.id)
|
||||||
|
val noteEvent = note?.event as? SealedGossipEvent
|
||||||
|
if (noteEvent != null) {
|
||||||
|
if (relay.brief !in note.relays) {
|
||||||
|
LocalCache.justConsume(noteEvent, relay)
|
||||||
|
|
||||||
|
noteEvent.innerEventId?.let {
|
||||||
|
(LocalCache.getNoteIfExists(it)?.event as? Event)?.let {
|
||||||
|
LocalCache.justConsume(it, relay)
|
||||||
|
}
|
||||||
|
} ?: run {
|
||||||
|
event.unseal(account.signer) {
|
||||||
|
LocalCache.justConsume(it, relay)
|
||||||
|
noteEvent.innerEventId = it.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// new event
|
||||||
|
event.unseal(account.signer) {
|
||||||
|
LocalCache.justConsume(event, relay)
|
||||||
|
LocalCache.justConsume(it, relay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is LnZapEvent -> {
|
||||||
|
// Avoid decrypting over and over again if the event already exist.
|
||||||
|
val note = LocalCache.getNoteIfExists(event.id)
|
||||||
|
if (note?.event == null) {
|
||||||
|
event.zapRequest?.let {
|
||||||
|
if (it.isPrivateZap()) {
|
||||||
|
it.decryptPrivateZap(account.signer) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else -> {
|
|
||||||
LocalCache.justConsume(event, relay)
|
LocalCache.justConsume(event, relay)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
LocalCache.justConsume(event, relay)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -409,16 +431,30 @@ object NostrAccountDataSource : AmethystNostrDataSource("AccountData") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun markInnerAsSeenOnRelay(
|
private fun markInnerAsSeenOnRelay(
|
||||||
noteEvent: EventInterface,
|
newNoteEvent: EventInterface,
|
||||||
relay: Relay,
|
relay: Relay,
|
||||||
) {
|
) {
|
||||||
LocalCache.getNoteIfExists(noteEvent.id())?.addRelay(relay)
|
markInnerAsSeenOnRelay(newNoteEvent.id(), relay)
|
||||||
|
}
|
||||||
|
|
||||||
if (noteEvent is GiftWrapEvent) {
|
private fun markInnerAsSeenOnRelay(
|
||||||
noteEvent.cachedGift(account.signer) { gift -> markInnerAsSeenOnRelay(gift, relay) }
|
eventId: HexKey,
|
||||||
} else if (noteEvent is SealedGossipEvent) {
|
relay: Relay,
|
||||||
noteEvent.cachedGossip(account.signer) { rumor ->
|
) {
|
||||||
markInnerAsSeenOnRelay(rumor, relay)
|
val note = LocalCache.getNoteIfExists(eventId)
|
||||||
|
|
||||||
|
if (note != null) {
|
||||||
|
note.addRelay(relay)
|
||||||
|
|
||||||
|
val noteEvent = note.event
|
||||||
|
if (noteEvent is GiftWrapEvent) {
|
||||||
|
noteEvent.innerEventId?.let {
|
||||||
|
markInnerAsSeenOnRelay(it, relay)
|
||||||
|
}
|
||||||
|
} else if (noteEvent is SealedGossipEvent) {
|
||||||
|
noteEvent.innerEventId?.let {
|
||||||
|
markInnerAsSeenOnRelay(it, relay)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,7 +75,8 @@ class EventNotificationConsumer(
|
|||||||
pushWrappedEvent: GiftWrapEvent,
|
pushWrappedEvent: GiftWrapEvent,
|
||||||
account: Account,
|
account: Account,
|
||||||
) {
|
) {
|
||||||
pushWrappedEvent.cachedGift(account.signer) { notificationEvent ->
|
// no need to cache
|
||||||
|
pushWrappedEvent.unwrap(account.signer) { notificationEvent ->
|
||||||
val consumed = LocalCache.hasConsumed(notificationEvent)
|
val consumed = LocalCache.hasConsumed(notificationEvent)
|
||||||
val verified = LocalCache.justVerify(notificationEvent)
|
val verified = LocalCache.justVerify(notificationEvent)
|
||||||
Log.d("EventNotificationConsumer", "New Notification ${notificationEvent.kind} ${notificationEvent.id} Arrived for ${account.userProfile().toBestDisplayName()} consumed= $consumed && verified= $verified")
|
Log.d("EventNotificationConsumer", "New Notification ${notificationEvent.kind} ${notificationEvent.id} Arrived for ${account.userProfile().toBestDisplayName()} consumed= $consumed && verified= $verified")
|
||||||
@@ -111,15 +112,19 @@ class EventNotificationConsumer(
|
|||||||
|
|
||||||
when (event) {
|
when (event) {
|
||||||
is GiftWrapEvent -> {
|
is GiftWrapEvent -> {
|
||||||
event.cachedGift(account.signer) { unwrapAndConsume(it, account, onReady) }
|
event.unwrap(account.signer) {
|
||||||
|
unwrapAndConsume(it, account, onReady)
|
||||||
|
LocalCache.justConsume(event, null)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
is SealedGossipEvent -> {
|
is SealedGossipEvent -> {
|
||||||
event.cachedGossip(account.signer) {
|
event.unseal(account.signer) {
|
||||||
if (!LocalCache.hasConsumed(it)) {
|
if (!LocalCache.hasConsumed(it)) {
|
||||||
// this is not verifiable
|
// this is not verifiable
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
onReady(it)
|
onReady(it)
|
||||||
}
|
}
|
||||||
|
LocalCache.justConsume(event, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
|
|||||||
@@ -1364,25 +1364,50 @@ class AccountViewModel(
|
|||||||
) {
|
) {
|
||||||
when (event) {
|
when (event) {
|
||||||
is GiftWrapEvent -> {
|
is GiftWrapEvent -> {
|
||||||
event.cachedGift(account.signer) {
|
event.innerEventId?.let {
|
||||||
val existingNote = LocalCache.getNoteIfExists(it.id)
|
val existingNote = LocalCache.getNoteIfExists(it)
|
||||||
if (existingNote != null) {
|
if (existingNote != null) {
|
||||||
unwrapIfNeeded(existingNote.event, onReady)
|
unwrapIfNeeded(existingNote.event, onReady)
|
||||||
} else {
|
} else {
|
||||||
LocalCache.verifyAndConsume(it, null)
|
event.unwrap(account.signer) {
|
||||||
unwrapIfNeeded(it, onReady)
|
LocalCache.verifyAndConsume(it, null)
|
||||||
|
unwrapIfNeeded(it, onReady)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} ?: run {
|
||||||
|
event.unwrap(account.signer) {
|
||||||
|
val existingNote = LocalCache.getNoteIfExists(it.id)
|
||||||
|
if (existingNote != null) {
|
||||||
|
unwrapIfNeeded(existingNote.event, onReady)
|
||||||
|
} else {
|
||||||
|
LocalCache.verifyAndConsume(it, null)
|
||||||
|
unwrapIfNeeded(it, onReady)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is SealedGossipEvent -> {
|
is SealedGossipEvent -> {
|
||||||
event.cachedGossip(account.signer) {
|
event.innerEventId?.let {
|
||||||
val existingNote = LocalCache.getNoteIfExists(it.id)
|
val existingNote = LocalCache.getNoteIfExists(it)
|
||||||
if (existingNote != null) {
|
if (existingNote != null) {
|
||||||
unwrapIfNeeded(existingNote.event, onReady)
|
unwrapIfNeeded(existingNote.event, onReady)
|
||||||
} else {
|
} else {
|
||||||
// this is not verifiable
|
event.unseal(account.signer) {
|
||||||
LocalCache.justConsume(it, null)
|
// this is not verifiable
|
||||||
unwrapIfNeeded(it, onReady)
|
LocalCache.justConsume(it, null)
|
||||||
|
unwrapIfNeeded(it, onReady)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} ?: run {
|
||||||
|
event.unseal(account.signer) {
|
||||||
|
val existingNote = LocalCache.getNoteIfExists(it.id)
|
||||||
|
if (existingNote != null) {
|
||||||
|
unwrapIfNeeded(existingNote.event, onReady)
|
||||||
|
} else {
|
||||||
|
// this is not verifiable
|
||||||
|
LocalCache.justConsume(it, null)
|
||||||
|
unwrapIfNeeded(it, onReady)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,10 +43,15 @@ import com.vitorpamplona.amethyst.model.LocalCache
|
|||||||
import com.vitorpamplona.amethyst.model.Note
|
import com.vitorpamplona.amethyst.model.Note
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.Route
|
import com.vitorpamplona.amethyst.ui.navigation.Route
|
||||||
import com.vitorpamplona.amethyst.ui.stringRes
|
import com.vitorpamplona.amethyst.ui.stringRes
|
||||||
|
import com.vitorpamplona.quartz.encoders.HexKey
|
||||||
import com.vitorpamplona.quartz.events.ChannelCreateEvent
|
import com.vitorpamplona.quartz.events.ChannelCreateEvent
|
||||||
|
import com.vitorpamplona.quartz.events.ChannelMessageEvent
|
||||||
|
import com.vitorpamplona.quartz.events.ChannelMetadataEvent
|
||||||
import com.vitorpamplona.quartz.events.ChatroomKeyable
|
import com.vitorpamplona.quartz.events.ChatroomKeyable
|
||||||
import com.vitorpamplona.quartz.events.EventInterface
|
import com.vitorpamplona.quartz.events.EventInterface
|
||||||
import com.vitorpamplona.quartz.events.GiftWrapEvent
|
import com.vitorpamplona.quartz.events.GiftWrapEvent
|
||||||
|
import com.vitorpamplona.quartz.events.LiveActivitiesChatMessageEvent
|
||||||
|
import com.vitorpamplona.quartz.events.LiveActivitiesEvent
|
||||||
import com.vitorpamplona.quartz.events.SealedGossipEvent
|
import com.vitorpamplona.quartz.events.SealedGossipEvent
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -104,7 +109,7 @@ fun LoadRedirectScreen(
|
|||||||
val event = note.event
|
val event = note.event
|
||||||
|
|
||||||
if (event != null) {
|
if (event != null) {
|
||||||
withContext(Dispatchers.IO) { redirect(event, note, accountViewModel, nav) }
|
withContext(Dispatchers.IO) { redirect(event, accountViewModel, nav) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,35 +123,60 @@ fun LoadRedirectScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun redirect(
|
fun redirect(
|
||||||
event: EventInterface,
|
eventId: HexKey,
|
||||||
note: Note,
|
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: (String) -> Unit,
|
nav: (String) -> Unit,
|
||||||
) {
|
) {
|
||||||
val channelHex = note.channelHex()
|
LocalCache.getNoteIfExists(eventId)?.event?.let {
|
||||||
|
redirect(it, accountViewModel, nav)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun redirect(
|
||||||
|
event: EventInterface,
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
|
nav: (String) -> Unit,
|
||||||
|
) {
|
||||||
|
val channelHex =
|
||||||
|
if (
|
||||||
|
event is ChannelMessageEvent ||
|
||||||
|
event is ChannelMetadataEvent ||
|
||||||
|
event is ChannelCreateEvent ||
|
||||||
|
event is LiveActivitiesChatMessageEvent ||
|
||||||
|
event is LiveActivitiesEvent
|
||||||
|
) {
|
||||||
|
(event as? ChannelMessageEvent)?.channel()
|
||||||
|
?: (event as? ChannelMetadataEvent)?.channel()
|
||||||
|
?: (event as? ChannelCreateEvent)?.id
|
||||||
|
?: (event as? LiveActivitiesChatMessageEvent)?.activity()?.toTag()
|
||||||
|
?: (event as? LiveActivitiesEvent)?.address()?.toTag()
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
if (event is GiftWrapEvent) {
|
if (event is GiftWrapEvent) {
|
||||||
accountViewModel.unwrap(event) { redirect(it, note, accountViewModel, nav) }
|
event.innerEventId?.let {
|
||||||
|
redirect(it, accountViewModel, nav)
|
||||||
|
} ?: run {
|
||||||
|
accountViewModel.unwrap(event) { redirect(it, accountViewModel, nav) }
|
||||||
|
}
|
||||||
} else if (event is SealedGossipEvent) {
|
} else if (event is SealedGossipEvent) {
|
||||||
accountViewModel.unseal(event) { redirect(it, note, accountViewModel, nav) }
|
event.innerEventId?.let {
|
||||||
|
redirect(it, accountViewModel, nav)
|
||||||
|
} ?: run {
|
||||||
|
accountViewModel.unseal(event) { redirect(it, accountViewModel, nav) }
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (event == null) {
|
if (event is ChannelCreateEvent) {
|
||||||
// stay here, loading
|
nav("Channel/${event.id()}")
|
||||||
} else if (event is ChannelCreateEvent) {
|
|
||||||
nav("Channel/${note.idHex}")
|
|
||||||
} else if (event is ChatroomKeyable) {
|
} else if (event is ChatroomKeyable) {
|
||||||
note.author?.let {
|
val withKey = event.chatroomKey(accountViewModel.userProfile().pubkeyHex)
|
||||||
val withKey =
|
accountViewModel.userProfile().createChatroom(withKey)
|
||||||
(event as ChatroomKeyable).chatroomKey(accountViewModel.userProfile().pubkeyHex)
|
nav("Room/${withKey.hashCode()}")
|
||||||
|
|
||||||
accountViewModel.userProfile().createChatroom(withKey)
|
|
||||||
|
|
||||||
nav("Room/${withKey.hashCode()}")
|
|
||||||
}
|
|
||||||
} else if (channelHex != null) {
|
} else if (channelHex != null) {
|
||||||
nav("Channel/$channelHex")
|
nav("Channel/$channelHex")
|
||||||
} else {
|
} else {
|
||||||
nav("Note/${note.idHex}")
|
nav("Note/${event.id()}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,12 +20,14 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.quartz.events
|
package com.vitorpamplona.quartz.events
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import com.vitorpamplona.quartz.crypto.KeyPair
|
import com.vitorpamplona.quartz.crypto.KeyPair
|
||||||
import com.vitorpamplona.quartz.encoders.HexKey
|
import com.vitorpamplona.quartz.encoders.HexKey
|
||||||
import com.vitorpamplona.quartz.signers.NostrSigner
|
import com.vitorpamplona.quartz.signers.NostrSigner
|
||||||
import com.vitorpamplona.quartz.signers.NostrSignerInternal
|
import com.vitorpamplona.quartz.signers.NostrSignerInternal
|
||||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
|
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
|
||||||
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
|
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@@ -37,11 +39,11 @@ class GiftWrapEvent(
|
|||||||
content: String,
|
content: String,
|
||||||
sig: HexKey,
|
sig: HexKey,
|
||||||
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
|
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||||
@Transient private var cachedInnerEvent: Map<HexKey, Event?> = mapOf()
|
@Transient var innerEventId: HexKey? = null
|
||||||
|
|
||||||
override fun countMemory(): Long =
|
override fun countMemory(): Long =
|
||||||
super.countMemory() +
|
super.countMemory() +
|
||||||
32 + (cachedInnerEvent.values.sumOf { pointerSizeInBytes + (it?.countMemory() ?: 0) }) // rough calculation
|
pointerSizeInBytes + (innerEventId?.bytesUsedInMemory() ?: 0)
|
||||||
|
|
||||||
fun copyNoContent(): GiftWrapEvent {
|
fun copyNoContent(): GiftWrapEvent {
|
||||||
val copy =
|
val copy =
|
||||||
@@ -54,48 +56,38 @@ class GiftWrapEvent(
|
|||||||
sig,
|
sig,
|
||||||
)
|
)
|
||||||
|
|
||||||
copy.cachedInnerEvent = cachedInnerEvent
|
copy.innerEventId = innerEventId
|
||||||
|
|
||||||
return copy
|
return copy
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isContentEncoded() = true
|
override fun isContentEncoded() = true
|
||||||
|
|
||||||
fun preCachedGift(signer: NostrSigner): Event? = cachedInnerEvent[signer.pubKey]
|
@Deprecated(
|
||||||
|
message = "Heavy caching was removed from this class due to high memory use. Cache it separatedly",
|
||||||
fun addToCache(
|
replaceWith = ReplaceWith("unwrap"),
|
||||||
pubKey: HexKey,
|
)
|
||||||
gift: Event,
|
|
||||||
) {
|
|
||||||
cachedInnerEvent = cachedInnerEvent + Pair(pubKey, gift)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun cachedGift(
|
fun cachedGift(
|
||||||
signer: NostrSigner,
|
signer: NostrSigner,
|
||||||
onReady: (Event) -> Unit,
|
onReady: (Event) -> Unit,
|
||||||
) {
|
) = unwrap(signer, onReady)
|
||||||
cachedInnerEvent[signer.pubKey]?.let {
|
|
||||||
onReady(it)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
unwrap(signer) { gift ->
|
|
||||||
if (gift is WrappedEvent) {
|
|
||||||
gift.host = HostStub(this.id, this.pubKey, this.kind)
|
|
||||||
}
|
|
||||||
addToCache(signer.pubKey, gift)
|
|
||||||
|
|
||||||
onReady(gift)
|
fun unwrap(
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun unwrap(
|
|
||||||
signer: NostrSigner,
|
signer: NostrSigner,
|
||||||
onReady: (Event) -> Unit,
|
onReady: (Event) -> Unit,
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
plainContent(signer) { onReady(fromJson(it)) }
|
plainContent(signer) { giftStr ->
|
||||||
|
val gift = fromJson(giftStr)
|
||||||
|
if (gift is WrappedEvent) {
|
||||||
|
gift.host = HostStub(this.id, this.pubKey, this.kind)
|
||||||
|
}
|
||||||
|
innerEventId = gift.id
|
||||||
|
|
||||||
|
onReady(gift)
|
||||||
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
// Log.e("UnwrapError", "Couldn't Decrypt the content", e)
|
Log.w("GiftWrapEvent", "Couldn't Decrypt the content", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import com.vitorpamplona.quartz.encoders.HexKey
|
|||||||
import com.vitorpamplona.quartz.encoders.toHexKey
|
import com.vitorpamplona.quartz.encoders.toHexKey
|
||||||
import com.vitorpamplona.quartz.signers.NostrSigner
|
import com.vitorpamplona.quartz.signers.NostrSigner
|
||||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
|
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
|
||||||
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
|
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@@ -38,11 +39,11 @@ class SealedGossipEvent(
|
|||||||
content: String,
|
content: String,
|
||||||
sig: HexKey,
|
sig: HexKey,
|
||||||
) : WrappedEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
|
) : WrappedEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||||
@Transient private var cachedInnerEvent: Map<HexKey, Event?> = mapOf()
|
@Transient var innerEventId: HexKey? = null
|
||||||
|
|
||||||
override fun countMemory(): Long =
|
override fun countMemory(): Long =
|
||||||
super.countMemory() +
|
super.countMemory() +
|
||||||
pointerSizeInBytes + cachedInnerEvent.values.sumOf { pointerSizeInBytes + (it?.countMemory() ?: 0) }
|
pointerSizeInBytes + (innerEventId?.bytesUsedInMemory() ?: 0)
|
||||||
|
|
||||||
fun copyNoContent(): SealedGossipEvent {
|
fun copyNoContent(): SealedGossipEvent {
|
||||||
val copy =
|
val copy =
|
||||||
@@ -55,51 +56,38 @@ class SealedGossipEvent(
|
|||||||
sig,
|
sig,
|
||||||
)
|
)
|
||||||
|
|
||||||
copy.cachedInnerEvent = cachedInnerEvent
|
|
||||||
copy.host = host
|
copy.host = host
|
||||||
|
copy.innerEventId = innerEventId
|
||||||
|
|
||||||
return copy
|
return copy
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isContentEncoded() = true
|
override fun isContentEncoded() = true
|
||||||
|
|
||||||
fun preCachedGossip(signer: NostrSigner): Event? = cachedInnerEvent[signer.pubKey]
|
@Deprecated(
|
||||||
|
message = "Heavy caching was removed from this class due to high memory use. Cache it separatedly",
|
||||||
fun addToCache(
|
replaceWith = ReplaceWith("unseal"),
|
||||||
pubKey: HexKey,
|
)
|
||||||
gift: Event,
|
|
||||||
) {
|
|
||||||
cachedInnerEvent = cachedInnerEvent + Pair(pubKey, gift)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun cachedGossip(
|
fun cachedGossip(
|
||||||
signer: NostrSigner,
|
signer: NostrSigner,
|
||||||
onReady: (Event) -> Unit,
|
onReady: (Event) -> Unit,
|
||||||
) {
|
) = unseal(signer, onReady)
|
||||||
cachedInnerEvent[signer.pubKey]?.let {
|
|
||||||
onReady(it)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
unseal(signer) { gossip ->
|
fun unseal(
|
||||||
val event = gossip.mergeWith(this)
|
|
||||||
if (event is WrappedEvent) {
|
|
||||||
event.host = host ?: HostStub(this.id, this.pubKey, this.kind)
|
|
||||||
}
|
|
||||||
addToCache(signer.pubKey, event)
|
|
||||||
|
|
||||||
onReady(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun unseal(
|
|
||||||
signer: NostrSigner,
|
signer: NostrSigner,
|
||||||
onReady: (Gossip) -> Unit,
|
onReady: (Event) -> Unit,
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
plainContent(signer) {
|
plainContent(signer) {
|
||||||
try {
|
try {
|
||||||
onReady(Gossip.fromJson(it))
|
val gossip = Gossip.fromJson(it)
|
||||||
|
val event = gossip.mergeWith(this)
|
||||||
|
if (event is WrappedEvent) {
|
||||||
|
event.host = host ?: HostStub(this.id, this.pubKey, this.kind)
|
||||||
|
}
|
||||||
|
innerEventId = event.id
|
||||||
|
|
||||||
|
onReady(event)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.w("GossipEvent", "Fail to decrypt or parse Gossip", e)
|
Log.w("GossipEvent", "Fail to decrypt or parse Gossip", e)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user