Migrates LocalCache from being based on ATags to Addresses, removing the need to use memory space to store relay hints

This commit is contained in:
Vitor Pamplona
2025-02-17 14:12:54 -05:00
parent 94ffe783e9
commit 7ad8b2ce46
62 changed files with 307 additions and 266 deletions
@@ -85,8 +85,10 @@ import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
import com.vitorpamplona.quartz.nip01Core.signers.EventTemplate
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.isTaggedAddressableNote
import com.vitorpamplona.quartz.nip01Core.tags.addressables.taggedATags
import com.vitorpamplona.quartz.nip01Core.tags.addressables.taggedAddresses
import com.vitorpamplona.quartz.nip01Core.tags.events.isTaggedEvent
import com.vitorpamplona.quartz.nip01Core.tags.events.taggedEventIds
import com.vitorpamplona.quartz.nip01Core.tags.geohash.geohash
import com.vitorpamplona.quartz.nip01Core.tags.geohash.geohashes
@@ -1001,7 +1003,7 @@ class Account(
hashtags = (listEvent.hashtags() + listEvent.filterHashtags(privateTagList)).toSet(),
geotags = (listEvent.geohashes() + listEvent.filterGeohashes(privateTagList)).toSet(),
addresses =
(listEvent.taggedAddresses() + listEvent.filterAddresses(privateTagList))
(listEvent.taggedATags() + listEvent.filterATags(privateTagList))
.map { it.toTag() }
.toSet(),
),
@@ -1124,7 +1126,9 @@ class Account(
fun getEmojiPackSelectionFlow(): StateFlow<NoteState> = getEmojiPackSelectionNote().flow().metadata.stateFlow
fun getEmojiPackSelectionNote(): AddressableNote = LocalCache.getOrCreateAddressableNote(EmojiPackSelectionEvent.createAddressATag(userProfile().pubkeyHex))
fun getEmojiPackSelectionAddress() = EmojiPackSelectionEvent.createAddress(userProfile().pubkeyHex)
fun getEmojiPackSelectionNote(): AddressableNote = LocalCache.getOrCreateAddressableNote(getEmojiPackSelectionAddress())
fun convertEmojiSelectionPack(selection: EmojiPackSelectionEvent?): List<StateFlow<NoteState>>? =
selection?.taggedAddresses()?.map {
@@ -1849,7 +1853,7 @@ class Account(
val contactList = userProfile().latestContactList
if (contactList != null) {
ContactListEvent.followAddressableEvent(contactList, community.address, signer) {
ContactListEvent.followAddressableEvent(contactList, community.toATag(), signer) {
Amethyst.instance.client.send(it)
LocalCache.justConsume(it, null)
}
@@ -1862,7 +1866,7 @@ class Account(
followUsers = emptyList(),
followTags = emptyList(),
followGeohashes = emptyList(),
followCommunities = listOf(community.address),
followCommunities = listOf(community.toATag()),
followEvents = DefaultChannels.toList(),
relayUse = relays,
signer = signer,
@@ -2008,7 +2012,7 @@ class Account(
if (contactList != null && contactList.tags.isNotEmpty()) {
ContactListEvent.unfollowAddressableEvent(
contactList,
community.address,
community.toATag(),
signer,
onReady = this::onNewEventCreated,
)
@@ -2860,7 +2864,7 @@ class Account(
if (note is AddressableNote) {
BookmarkListEvent.addReplaceable(
userProfile().latestBookmarkList,
note.address,
note.toATag(),
isPrivate,
signer,
) {
@@ -2891,7 +2895,7 @@ class Account(
if (note is AddressableNote) {
BookmarkListEvent.removeReplaceable(
bookmarks,
note.address,
note.toATag(),
isPrivate,
signer,
) {
@@ -2954,7 +2958,7 @@ class Account(
}
if (note is AddressableNote) {
userProfile().latestBookmarkList?.privateTaggedAddresses(signer) {
userProfile().latestBookmarkList?.privateAddress(signer) {
onReady(it.contains(note.address))
}
} else {
@@ -2968,40 +2972,19 @@ class Account(
if (!isWriteable()) return false
if (note is AddressableNote) {
return userProfile().latestBookmarkList?.taggedAddresses()?.contains(note.address) == true
return userProfile().latestBookmarkList?.isTaggedAddressableNote(note.idHex) == true
} else {
return userProfile().latestBookmarkList?.taggedEventIds()?.contains(note.idHex) == true
return userProfile().latestBookmarkList?.isTaggedEvent(note.idHex) == true
}
}
fun getAppSpecificDataNote(): AddressableNote {
val aTag = AppSpecificDataEvent.createTag(userProfile().pubkeyHex, APP_SPECIFIC_DATA_D_TAG)
return LocalCache.getOrCreateAddressableNote(aTag)
}
fun getAppSpecificDataNote() = LocalCache.getOrCreateAddressableNote(AppSpecificDataEvent.createAddress(userProfile().pubkeyHex, APP_SPECIFIC_DATA_D_TAG))
fun getAppSpecificDataFlow(): StateFlow<NoteState> = getAppSpecificDataNote().flow().metadata.stateFlow
fun getBlockListNote(): AddressableNote {
val aTag =
ATag(
PeopleListEvent.KIND,
userProfile().pubkeyHex,
PeopleListEvent.BLOCK_LIST_D_TAG,
null,
)
return LocalCache.getOrCreateAddressableNote(aTag)
}
fun getBlockListNote() = LocalCache.getOrCreateAddressableNote(PeopleListEvent.createBlockAddress(userProfile().pubkeyHex))
fun getMuteListNote(): AddressableNote {
val aTag =
ATag(
MuteListEvent.KIND,
userProfile().pubkeyHex,
"",
null,
)
return LocalCache.getOrCreateAddressableNote(aTag)
}
fun getMuteListNote() = LocalCache.getOrCreateAddressableNote(MuteListEvent.createAddress(userProfile().pubkeyHex))
fun getMuteListFlow(): StateFlow<NoteState> = getMuteListNote().flow().metadata.stateFlow
@@ -3345,10 +3328,7 @@ class Account(
)
}
fun getDMRelayListNote(): AddressableNote =
LocalCache.getOrCreateAddressableNote(
ChatMessageRelayListEvent.createAddressATag(signer.pubKey),
)
fun getDMRelayListNote(): AddressableNote = LocalCache.getOrCreateAddressableNote(ChatMessageRelayListEvent.createAddress(signer.pubKey))
fun getDMRelayListFlow(): StateFlow<NoteState> = getDMRelayListNote().flow().metadata.stateFlow
@@ -3380,7 +3360,7 @@ class Account(
fun getPrivateOutboxRelayListNote(): AddressableNote =
LocalCache.getOrCreateAddressableNote(
PrivateOutboxRelayListEvent.createAddressATag(signer.pubKey),
PrivateOutboxRelayListEvent.createAddress(signer.pubKey),
)
fun getPrivateOutboxRelayListFlow(): StateFlow<NoteState> = getPrivateOutboxRelayListNote().flow().metadata.stateFlow
@@ -3414,7 +3394,7 @@ class Account(
fun getSearchRelayListNote(): AddressableNote =
LocalCache.getOrCreateAddressableNote(
SearchRelayListEvent.createAddressATag(signer.pubKey),
SearchRelayListEvent.createAddress(signer.pubKey),
)
fun getSearchRelayListFlow(): StateFlow<NoteState> = getSearchRelayListNote().flow().metadata.stateFlow
@@ -3448,7 +3428,7 @@ class Account(
fun getNIP65RelayListNote(pubkey: HexKey = signer.pubKey): AddressableNote =
LocalCache.getOrCreateAddressableNote(
AdvertisedRelayListEvent.createAddressATag(pubkey),
AdvertisedRelayListEvent.createAddress(pubkey),
)
fun getNIP65RelayListFlow(pubkey: HexKey = signer.pubKey): StateFlow<NoteState> = getNIP65RelayListNote(pubkey).flow().metadata.stateFlow
@@ -3484,13 +3464,13 @@ class Account(
fun getFileServersListFlow(): StateFlow<NoteState> = getFileServersNote().flow().metadata.stateFlow
fun getFileServersNote(): AddressableNote = LocalCache.getOrCreateAddressableNote(FileServersEvent.createAddressATag(userProfile().pubkeyHex))
fun getFileServersNote(): AddressableNote = LocalCache.getOrCreateAddressableNote(FileServersEvent.createAddress(userProfile().pubkeyHex))
fun getBlossomServersList(): BlossomServersEvent? = getBlossomServersNote().event as? BlossomServersEvent
fun getBlossomServersListFlow(): StateFlow<NoteState> = getBlossomServersNote().flow().metadata.stateFlow
fun getBlossomServersNote(): AddressableNote = LocalCache.getOrCreateAddressableNote(BlossomServersEvent.createAddressATag(userProfile().pubkeyHex))
fun getBlossomServersNote(): AddressableNote = LocalCache.getOrCreateAddressableNote(BlossomServersEvent.createAddress(userProfile().pubkeyHex))
fun host(url: String): String =
try {
@@ -32,6 +32,8 @@ import com.vitorpamplona.ammolite.relays.Relay
import com.vitorpamplona.ammolite.relays.RelayBriefInfoCache
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip19Bech32.entities.NAddress
import com.vitorpamplona.quartz.nip19Bech32.toNAddr
import com.vitorpamplona.quartz.nip19Bech32.toNEvent
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
@@ -81,11 +83,11 @@ class PublicChatChannel(
@Stable
class LiveActivitiesChannel(
val address: ATag,
) : Channel(address.toTag()) {
val address: Address,
) : Channel(address.toValue()) {
var info: LiveActivitiesEvent? = null
override fun idNote() = address.toNAddr()
override fun idNote() = toNAddr()
override fun idDisplayNote() = idNote().toShortenHex()
@@ -93,6 +95,8 @@ class LiveActivitiesChannel(
override fun relays() = info?.allRelayUrls() ?: super.relays()
fun relayHintUrl() = relays().firstOrNull()
fun updateChannelInfo(
creator: User,
channelInfo: LiveActivitiesEvent,
@@ -112,6 +116,10 @@ class LiveActivitiesChannel(
listOfNotNull(info?.title(), info?.summary())
.filter { it.contains(prefix, true) }
.isNotEmpty()
fun toNAddr() = NAddress.create(address.kind, address.pubKeyHex, address.dTag, relayHintUrl())
fun toATag() = ATag(address, relayHintUrl())
}
data class Counter(
@@ -167,6 +175,15 @@ abstract class Channel(
}
}
fun addRelay(relay: Relay) {
val counter = relays[relay.brief]
if (counter != null) {
counter.number++
} else {
addRelaySync(relay.brief)
}
}
fun addNote(
note: Note,
relay: Relay? = null,
@@ -178,12 +195,7 @@ abstract class Channel(
}
if (relay != null) {
val counter = relays[relay.brief]
if (counter != null) {
counter.number++
} else {
addRelaySync(relay.brief)
}
addRelay(relay)
}
}
@@ -55,6 +55,7 @@ import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.tagValueContains
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip01Core.tags.addressables.mapTaggedAddress
import com.vitorpamplona.quartz.nip01Core.tags.addressables.taggedAddresses
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
@@ -275,6 +276,8 @@ object LocalCache {
fun getAddressableNoteIfExists(key: String): AddressableNote? = addressables.get(key)
fun getAddressableNoteIfExists(address: Address): AddressableNote? = getAddressableNoteIfExists(address.toValue())
fun getNoteIfExists(key: String): Note? = addressables.get(key) ?: notes.get(key)
fun getNoteIfExists(key: ETag): Note? = notes.get(key.eventId)
@@ -290,7 +293,7 @@ object LocalCache {
fun getOrCreateNote(event: Event): Note =
if (event is AddressableEvent) {
getOrCreateAddressableNote(event.aTag())
getOrCreateAddressableNote(event.address())
} else {
getOrCreateNote(event.id)
}
@@ -304,8 +307,6 @@ object LocalCache {
return null
}
fun checkGetOrCreateNote(atag: ATag) = getOrCreateAddressableNote(atag)
fun checkGetOrCreateNote(key: String): Note? {
checkNotInMainThread()
@@ -371,9 +372,10 @@ object LocalCache {
if (isValidHex(key)) {
return channels.getOrCreate(key) { PublicChatChannel(key) }
}
val aTag = ATag.parse(key, null)
if (aTag != null) {
return channels.getOrCreate(aTag.toTag()) { LiveActivitiesChannel(aTag) }
val address = Address.parse(key)
if (address != null) {
return channels.getOrCreate(address.toValue()) { LiveActivitiesChannel(address) }
}
return null
}
@@ -387,7 +389,7 @@ object LocalCache {
fun checkGetOrCreateAddressableNote(key: String): AddressableNote? =
try {
val addr = ATag.parse(key, null) // relay doesn't matter for the index.
val addr = Address.parse(key)
if (addr != null) {
getOrCreateAddressableNote(addr)
} else {
@@ -398,17 +400,12 @@ object LocalCache {
null
}
fun getOrCreateAddressableNoteInternal(key: ATag): AddressableNote {
// checkNotInMainThread()
// we can't use naddr here because naddr might include relay info and
// the preferred relay should not be part of the index.
return addressables.getOrCreate(key.toTag()) {
fun getOrCreateAddressableNoteInternal(key: Address): AddressableNote =
addressables.getOrCreate(key.toValue()) {
AddressableNote(key)
}
}
fun getOrCreateAddressableNote(key: ATag): AddressableNote {
fun getOrCreateAddressableNote(key: Address): AddressableNote {
val note = getOrCreateAddressableNoteInternal(key)
// Loads the user outside a Syncronized block to avoid blocking
if (note.author == null) {
@@ -598,7 +595,7 @@ object LocalCache {
relay: Relay?,
) {
val version = getOrCreateNote(event.id)
val note = getOrCreateAddressableNote(event.aTag())
val note = getOrCreateAddressableNote(event.address())
val author = getOrCreateUser(event.pubKey)
if (version.event == null) {
@@ -632,7 +629,7 @@ object LocalCache {
relay: Relay?,
) {
val version = getOrCreateNote(event.id)
val note = getOrCreateAddressableNote(event.aTag())
val note = getOrCreateAddressableNote(event.address())
val author = getOrCreateUser(event.pubKey)
if (version.event == null) {
@@ -693,7 +690,7 @@ object LocalCache {
event.taggedAddresses().map { getOrCreateAddressableNote(it) }
is CommunityPostApprovalEvent ->
event.approvedEvents().mapNotNull { checkGetOrCreateNote(it) } +
event.approvedAddresses().map { checkGetOrCreateNote(it) }
event.approvedAddresses().map { getOrCreateAddressableNote(it) }
is ReactionEvent ->
event.originalPost().mapNotNull { checkGetOrCreateNote(it) } +
event.taggedAddresses().map { getOrCreateAddressableNote(it) }
@@ -730,7 +727,7 @@ object LocalCache {
relay: Relay?,
) {
val version = getOrCreateNote(event.id)
val note = getOrCreateAddressableNote(event.aTag())
val note = getOrCreateAddressableNote(event.address())
val author = getOrCreateUser(event.pubKey)
if (version.event == null) {
@@ -743,9 +740,11 @@ object LocalCache {
if (event.createdAt > (note.createdAt() ?: 0)) {
note.loadEvent(event, author, emptyList())
val channel =
getOrCreateChannel(note.idHex) { LiveActivitiesChannel(note.address) }
as? LiveActivitiesChannel
val channel = getOrCreateChannel(note.idHex) { LiveActivitiesChannel(note.address) } as? LiveActivitiesChannel
if (relay != null) {
channel?.addRelay(relay)
}
val creator = event.host()?.let { checkGetOrCreateUser(it.pubKey) } ?: author
@@ -900,7 +899,7 @@ object LocalCache {
relay: Relay?,
) {
val version = getOrCreateNote(event.id)
val note = getOrCreateAddressableNote(event.aTag())
val note = getOrCreateAddressableNote(event.address())
val author = getOrCreateUser(event.pubKey)
if (version.event == null) {
@@ -957,7 +956,7 @@ object LocalCache {
fun consume(event: BadgeProfilesEvent) {
val version = getOrCreateNote(event.id)
val note = getOrCreateAddressableNote(event.aTag())
val note = getOrCreateAddressableNote(event.address())
val author = getOrCreateUser(event.pubKey)
if (version.event == null) {
@@ -1029,7 +1028,7 @@ object LocalCache {
relay: Relay?,
) {
val version = getOrCreateNote(event.id)
val note = getOrCreateAddressableNote(event.aTag())
val note = getOrCreateAddressableNote(event.address())
val author = getOrCreateUser(event.pubKey)
val replyTos = computeReplyTo(event)
@@ -1131,7 +1130,7 @@ object LocalCache {
}
}
val addressList = event.deleteAddressTags()
val addressList = event.deleteAddressIds()
val addressSet = addressList.toSet()
addressList
@@ -1296,7 +1295,7 @@ object LocalCache {
val author = getOrCreateUser(event.pubKey)
val communities = event.communities()
val communities = event.communityAddresses()
val eventsApproved = computeReplyTo(event)
val repliesTo = communities.map { getOrCreateAddressableNote(it) }
@@ -1470,9 +1469,9 @@ object LocalCache {
event: LiveActivitiesChatMessageEvent,
relay: Relay?,
) {
val activityId = event.activity() ?: return
val activityAddress = event.activityAddress() ?: return
val channel = getOrCreateChannel(activityId.toTag()) { LiveActivitiesChannel(activityId) }
val channel = getOrCreateChannel(activityAddress.toValue()) { LiveActivitiesChannel(activityAddress) }
val note = getOrCreateNote(event.id)
channel.addNote(note, relay)
@@ -2348,7 +2347,7 @@ object LocalCache {
draftWrap: DraftEvent,
draft: Event,
) {
val note = getOrCreateAddressableNote(draftWrap.aTag())
val note = getOrCreateAddressableNote(draftWrap.address())
val author = getOrCreateUser(draftWrap.pubKey)
when (draft) {
@@ -46,6 +46,7 @@ import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.events.EventReference
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.anyHashTag
@@ -54,6 +55,7 @@ import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent
import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag
import com.vitorpamplona.quartz.nip18Reposts.GenericRepostEvent
import com.vitorpamplona.quartz.nip18Reposts.RepostEvent
import com.vitorpamplona.quartz.nip19Bech32.entities.NAddress
import com.vitorpamplona.quartz.nip19Bech32.entities.NEvent
import com.vitorpamplona.quartz.nip19Bech32.toNAddr
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent
@@ -85,11 +87,11 @@ import kotlin.coroutines.resume
@Stable
class AddressableNote(
val address: ATag,
) : Note(address.toTag()) {
override fun idNote() = address.toNAddr(relayHintUrl())
val address: Address,
) : Note(address.toValue()) {
override fun idNote() = toNAddr()
override fun toNEvent() = address.toNAddr(relayHintUrl())
override fun toNEvent() = toNAddr()
override fun idDisplayNote() = idNote().toShortenHex()
@@ -108,13 +110,15 @@ class AddressableNote(
override fun wasOrShouldBeDeletedBy(
deletionEvents: Set<HexKey>,
deletionAddressables: Set<ATag>,
deletionAddressables: Set<Address>,
): Boolean {
val thisEvent = event
return deletionAddressables.contains(address) || (thisEvent != null && deletionEvents.contains(thisEvent.id))
}
override fun toATag() = ATag.parse(idHex, relayHintUrl())
fun toNAddr() = NAddress.create(address.kind, address.pubKeyHex, address.dTag, relayHintUrl())
fun toATag() = ATag(address, relayHintUrl())
}
@Stable
@@ -211,7 +215,7 @@ open class Note(
null
}
open fun address(): ATag? = null
open fun address(): Address? = null
open fun createdAt() = event?.createdAt
@@ -915,10 +919,10 @@ open class Note(
open fun wasOrShouldBeDeletedBy(
deletionEvents: Set<HexKey>,
deletionAddressables: Set<ATag>,
deletionAddressables: Set<Address>,
): Boolean {
val thisEvent = event
return deletionEvents.contains(idHex) || (thisEvent is AddressableEvent && deletionAddressables.contains(thisEvent.aTag()))
return deletionEvents.contains(idHex) || (thisEvent is AddressableEvent && deletionAddressables.contains(thisEvent.address()))
}
fun toETag(): ETag {
@@ -950,15 +954,6 @@ open class Note(
MarkedETag(idHex, relayHintUrl(), marker, author?.pubkeyHex)
}
}
open fun toATag(): ATag? {
val noteEvent = event
return if (noteEvent is AddressableEvent) {
ATag(noteEvent.kind, noteEvent.pubKey, noteEvent.dTag(), relayHintUrl())
} else {
null
}
}
}
@Stable
@@ -23,7 +23,7 @@ package com.vitorpamplona.amethyst.model
import androidx.compose.runtime.Stable
import com.vitorpamplona.amethyst.service.checkNotInMainThread
import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip18Reposts.GenericRepostEvent
import com.vitorpamplona.quartz.nip18Reposts.RepostEvent
import kotlinx.collections.immutable.ImmutableSet
@@ -132,7 +132,7 @@ class ThreadAssembler {
}
class OnlyLatestVersionSet : MutableSet<Note> {
val map = hashMapOf<ATag, Long>()
val map = hashMapOf<Address, Long>()
val set = hashSetOf<Note>()
override fun add(element: Note): Boolean {
@@ -142,14 +142,14 @@ class OnlyLatestVersionSet : MutableSet<Note> {
return if (element is AddressableNote && loadedCreatedAt != null) {
innerAdd(element.address, element, loadedCreatedAt)
} else if (noteEvent is AddressableEvent && loadedCreatedAt != null) {
innerAdd(noteEvent.aTag(), element, loadedCreatedAt)
innerAdd(noteEvent.address(), element, loadedCreatedAt)
} else {
set.add(element)
}
}
private fun innerAdd(
address: ATag,
address: Address,
element: Note,
loadedCreatedAt: Long,
): Boolean {
@@ -193,7 +193,7 @@ class OnlyLatestVersionSet : MutableSet<Note> {
element.address()?.let {
map.remove(it)
}
(element.event as? AddressableEvent)?.aTag()?.let {
(element.event as? AddressableEvent)?.address()?.let {
map.remove(it)
}
@@ -50,7 +50,7 @@ object NostrCommunityDataSource : AmethystNostrDataSource("SingleCommunityFeed")
authors = authors,
tags =
mapOf(
"a" to listOf(myCommunityToWatch.address.toTag()),
"a" to listOf(myCommunityToWatch.address.toValue()),
),
kinds = listOf(CommunityPostApprovalEvent.KIND),
limit = 500,
@@ -81,7 +81,7 @@ object NostrSingleEventDataSource : AmethystNostrDataSource("SingleEventFeed") {
CommunityPostApprovalEvent.KIND,
LiveActivitiesChatMessageEvent.KIND,
),
tags = mapOf("a" to it.mapNotNull { it.address()?.toTag() }),
tags = mapOf("a" to it.mapNotNull { it.address()?.toValue() }),
since = findMinimumEOSEs(it),
// Max amount of "replies" to download on a specific event.
limit = 1000,
@@ -95,7 +95,7 @@ object NostrSingleEventDataSource : AmethystNostrDataSource("SingleEventFeed") {
listOf(
DeletionEvent.KIND,
),
tags = mapOf("a" to it.mapNotNull { it.address()?.toTag() }),
tags = mapOf("a" to it.mapNotNull { it.address()?.toValue() }),
since = findMinimumEOSEs(it),
// Max amount of "replies" to download on a specific event.
limit = 10,
@@ -792,11 +792,7 @@ open class NewPostViewModel : ViewModel() {
imetas(usedAttachments)
}
} else {
if (channel.address.relay == null) {
channel.address.relay = channelRelays.firstOrNull() ?: replyingToEvent?.relay
}
LiveActivitiesChatMessageEvent.message(tagger.message, channel.address) {
LiveActivitiesChatMessageEvent.message(tagger.message, channel.toATag()) {
tagger.pTags?.let { notify(it.map { it.toPTag() }) }
hashtags(findHashtags(tagger.message))
@@ -23,8 +23,7 @@ package com.vitorpamplona.amethyst.ui.dal
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip19Bech32.parseAtagUnckecked
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip51Lists.MuteListEvent
import com.vitorpamplona.quartz.nip51Lists.PeopleListEvent
import com.vitorpamplona.quartz.nip72ModCommunities.approval.CommunityPostApprovalEvent
@@ -54,7 +53,7 @@ open class DiscoverCommunityFeedFilter(
val notes =
LocalCache.addressables.mapNotNullIntoSet { key, note ->
val noteEvent = note.event
if (noteEvent == null && shouldInclude(ATag.parseAtagUnckecked(key), filterParams)) {
if (noteEvent == null && shouldInclude(Address.parse(key), filterParams)) {
// send unloaded communities to the screen
note
} else if (noteEvent is CommunityDefinitionEvent && filterParams.match(noteEvent)) {
@@ -86,7 +85,7 @@ open class DiscoverCommunityFeedFilter(
if (noteEvent is CommunityDefinitionEvent && filterParams.match(noteEvent)) {
listOf(note)
} else if (noteEvent is CommunityPostApprovalEvent) {
noteEvent.communities().mapNotNull {
noteEvent.communityAddresses().mapNotNull {
val definitionNote = LocalCache.getOrCreateAddressableNote(it)
val definitionEvent = definitionNote.event
@@ -106,7 +105,7 @@ open class DiscoverCommunityFeedFilter(
}
private fun shouldInclude(
aTag: ATag?,
aTag: Address?,
params: FilterByListParams,
) = aTag != null && aTag.kind == CommunityDefinitionEvent.KIND && params.match(aTag)
@@ -24,7 +24,7 @@ import com.vitorpamplona.amethyst.model.AROUND_ME
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.GLOBAL_FOLLOWS
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip01Core.tags.addressables.isTaggedAddressableNotes
import com.vitorpamplona.quartz.nip01Core.tags.geohash.isTaggedGeoHashes
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.isTaggedHashes
@@ -68,10 +68,10 @@ class FilterByListParams(
}
}
fun isATagInList(aTag: ATag): Boolean {
fun isAuthorInFollows(address: Address): Boolean {
if (followLists == null) return false
return aTag.pubKeyHex in followLists.authors
return address.pubKeyHex in followLists.authors
}
fun match(
@@ -81,10 +81,10 @@ class FilterByListParams(
(isHiddenList || isNotHidden(noteEvent.pubKey)) &&
isNotInTheFuture(noteEvent)
fun match(aTag: ATag?) =
aTag != null &&
(isGlobal || isATagInList(aTag)) &&
(isHiddenList || isNotHidden(aTag.pubKeyHex))
fun match(address: Address?) =
address != null &&
(isGlobal || isAuthorInFollows(address)) &&
(isHiddenList || isNotHidden(address.pubKeyHex))
companion object {
fun showHiddenKey(
@@ -23,7 +23,6 @@ package com.vitorpamplona.amethyst.ui.dal
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip89AppHandlers.recommendation.AppRecommendationEvent
class UserProfileAppRecommendationsFeedFilter(
@@ -48,7 +47,7 @@ class UserProfileAppRecommendationsFeedFilter(
val noteEvent = it.event
if (noteEvent is AppRecommendationEvent) {
if (noteEvent.pubKey == user.pubkeyHex) {
return noteEvent.recommendations().map { LocalCache.getOrCreateAddressableNote(ATag(it.address)) }
return noteEvent.recommendations().map { LocalCache.getOrCreateAddressableNote(it.address) }
}
}
@@ -113,7 +113,7 @@ import com.vitorpamplona.amethyst.ui.theme.profileContentHeaderModifier
import com.vitorpamplona.amethyst.ui.tor.ConnectTorDialog
import com.vitorpamplona.ammolite.relays.RelayPoolStatus
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip02FollowList.ImmutableListOfLists
@Composable
@@ -278,7 +278,7 @@ private fun EditStatusBoxes(
@Composable
fun StatusEditBar(
savedStatus: String? = null,
tag: ATag? = null,
address: Address? = null,
accountViewModel: AccountViewModel,
nav: INav,
) {
@@ -311,10 +311,10 @@ fun StatusEditBar(
keyboardActions =
KeyboardActions(
onSend = {
if (tag == null) {
if (address == null) {
accountViewModel.createStatus(currentStatus.value)
} else {
accountViewModel.updateStatus(tag, currentStatus.value)
accountViewModel.updateStatus(address, currentStatus.value)
}
focusManager.clearFocus(true)
@@ -324,17 +324,17 @@ fun StatusEditBar(
trailingIcon = {
if (hasChanged.value) {
SendButton {
if (tag == null) {
if (address == null) {
accountViewModel.createStatus(currentStatus.value)
} else {
accountViewModel.updateStatus(tag, currentStatus.value)
accountViewModel.updateStatus(address, currentStatus.value)
}
focusManager.clearFocus(true)
}
} else {
if (tag != null) {
if (address != null) {
UserStatusDeleteButton {
accountViewModel.deleteStatus(tag)
accountViewModel.deleteStatus(address)
focusManager.clearFocus(true)
}
}
@@ -39,7 +39,7 @@ import com.vitorpamplona.amethyst.service.CachedGeoLocations
import com.vitorpamplona.amethyst.ui.components.GenericLoadable
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.equalImmutableLists
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.Dispatchers
@@ -112,20 +112,20 @@ fun LoadAddressableNote(
@Composable
fun LoadAddressableNote(
aTag: ATag,
address: Address,
accountViewModel: AccountViewModel,
content: @Composable (AddressableNote?) -> Unit,
) {
var note by
remember(aTag) {
mutableStateOf<AddressableNote?>(accountViewModel.getAddressableNoteIfExists(aTag.toTag()))
remember(address) {
mutableStateOf(accountViewModel.getAddressableNoteIfExists(address.toValue()))
}
if (note == null) {
LaunchedEffect(key1 = aTag) {
LaunchedEffect(key1 = address) {
val newNote =
withContext(Dispatchers.IO) {
accountViewModel.getOrCreateAddressableNote(aTag)
accountViewModel.getOrCreateAddressableNote(address)
}
if (note != newNote) {
note = newNote
@@ -73,7 +73,7 @@ import com.vitorpamplona.amethyst.ui.theme.lessImportantLink
import com.vitorpamplona.amethyst.ui.theme.nip05
import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.quartz.nip01Core.metadata.UserMetadata
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip01Core.tags.addressables.firstTaggedAddress
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.events.firstTaggedEvent
@@ -276,7 +276,7 @@ fun DisplayStatusInner(
content: String,
type: String,
url: String?,
nostrATag: ATag?,
nostrATag: Address?,
nostrETag: ETag?,
accountViewModel: AccountViewModel,
nav: INav,
@@ -125,13 +125,13 @@ val njumpLink = { nip19BechAddress: String ->
val externalLinkForNote = { note: Note ->
if (note is AddressableNote) {
if (note.event?.bountyBaseReward() != null) {
"https://nostrbounties.com/b/${note.address().toNAddr()}"
"https://nostrbounties.com/b/${note.toNAddr()}"
} else if (note.event is PeopleListEvent) {
"https://listr.lol/a/${note.address().toNAddr()}"
"https://listr.lol/a/${note.toNAddr()}"
} else if (note.event is AudioTrackEvent) {
"https://zapstr.live/?track=${note.address().toNAddr()}"
"https://zapstr.live/?track=${note.toNAddr()}"
} else {
njumpLink(note.address().toNAddr())
njumpLink(note.toNAddr())
}
} else {
if (note.event is FileHeaderEvent) {
@@ -61,7 +61,7 @@ import kotlinx.coroutines.flow.Flow
@Composable
fun WatchAndLoadMyEmojiList(accountViewModel: AccountViewModel) {
LoadAddressableNote(
EmojiPackSelectionEvent.createAddressATag(accountViewModel.userProfile().pubkeyHex),
EmojiPackSelectionEvent.createAddress(accountViewModel.userProfile().pubkeyHex),
accountViewModel,
) { emptyNote ->
emptyNote?.let { usersEmojiList ->
@@ -73,7 +73,7 @@ fun WatchAndLoadMyEmojiList(accountViewModel: AccountViewModel) {
.observeAsState((usersEmojiList.event as? EmojiPackSelectionEvent)?.taggedAddresses()?.toImmutableList())
collections?.forEach {
LoadAddressableNote(aTag = it, accountViewModel) {
LoadAddressableNote(it, accountViewModel) {
it?.live()?.metadata?.observeAsState()
}
}
@@ -86,7 +86,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.SaveButton
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip01Core.tags.addressables.taggedAddresses
import com.vitorpamplona.quartz.nip30CustomEmoji.CustomEmoji
import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrlTag
@@ -340,13 +340,7 @@ private fun EmojiSelector(
onClick: ((EmojiUrlTag) -> Unit)? = null,
) {
LoadAddressableNote(
aTag =
ATag(
EmojiPackSelectionEvent.KIND,
accountViewModel.userProfile().pubkeyHex,
"",
null,
),
accountViewModel.account.getEmojiPackSelectionAddress(),
accountViewModel,
) { emptyNote ->
emptyNote?.let { usersEmojiList ->
@@ -369,7 +363,7 @@ private fun EmojiSelector(
@Composable
fun EmojiCollectionGallery(
emojiCollections: ImmutableList<ATag>,
emojiCollections: ImmutableList<Address>,
accountViewModel: AccountViewModel,
nav: INav,
onClick: ((EmojiUrlTag) -> Unit)? = null,
@@ -382,8 +376,8 @@ fun EmojiCollectionGallery(
LazyColumn(
state = listState,
) {
itemsIndexed(emojiCollections, key = { _, item -> item.toTag() }) { _, item ->
LoadAddressableNote(aTag = item, accountViewModel) {
itemsIndexed(emojiCollections, key = { _, item -> item }) { _, item ->
LoadAddressableNote(item, accountViewModel) {
it?.let { WatchAndRenderNote(it, bgColor, accountViewModel, nav, onClick) }
}
}
@@ -59,16 +59,13 @@ fun ShowForkInformation(
val forkedAddress = remember(noteEvent) { noteEvent.forkFromAddress() }
val forkedEvent = remember(noteEvent) { noteEvent.forkFromVersion() }
if (forkedAddress != null) {
LoadAddressableNote(
aTag = forkedAddress,
accountViewModel = accountViewModel,
) { addressableNote ->
LoadAddressableNote(forkedAddress, accountViewModel) { addressableNote ->
if (addressableNote != null) {
ForkInformationRowLightColor(addressableNote, modifier, accountViewModel, nav)
}
}
} else if (forkedEvent != null) {
LoadNote(forkedEvent.eventId, accountViewModel = accountViewModel) { event ->
LoadNote(forkedEvent.eventId, accountViewModel) { event ->
if (event != null) {
ForkInformationRowLightColor(event, modifier, accountViewModel, nav)
}
@@ -56,11 +56,9 @@ import com.vitorpamplona.amethyst.ui.note.elements.RemoveButton
import com.vitorpamplona.amethyst.ui.note.getGradient
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.theme.Size35Modifier
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.isTaggedAddressableNote
import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrlTag
import com.vitorpamplona.quartz.nip30CustomEmoji.pack.EmojiPackEvent
import com.vitorpamplona.quartz.nip30CustomEmoji.selection.EmojiPackSelectionEvent
import com.vitorpamplona.quartz.nip30CustomEmoji.taggedEmojis
@Composable
@@ -180,13 +178,7 @@ private fun EmojiListOptions(
emojiPackNote: Note,
) {
LoadAddressableNote(
aTag =
ATag(
EmojiPackSelectionEvent.KIND,
accountViewModel.userProfile().pubkeyHex,
"",
null,
),
accountViewModel.account.getEmojiPackSelectionAddress(),
accountViewModel,
) {
it?.let { usersEmojiList ->
@@ -137,10 +137,10 @@ private fun RenderGitPatchEvent(
accountViewModel: AccountViewModel,
nav: INav,
) {
val repository = remember(noteEvent) { noteEvent.repository() }
val repository = remember(noteEvent) { noteEvent.repositoryAddress() }
if (repository != null) {
LoadAddressableNote(aTag = repository, accountViewModel = accountViewModel) {
LoadAddressableNote(repository, accountViewModel) {
if (it != null) {
RenderShortRepositoryHeader(it, accountViewModel, nav)
Spacer(modifier = DoubleVertSpacer)
@@ -242,10 +242,10 @@ private fun RenderGitIssueEvent(
accountViewModel: AccountViewModel,
nav: INav,
) {
val repository = remember(noteEvent) { noteEvent.repository() }
val repository = remember(noteEvent) { noteEvent.repositoryAddress() }
if (repository != null) {
LoadAddressableNote(aTag = repository, accountViewModel = accountViewModel) {
LoadAddressableNote(repository, accountViewModel) {
if (it != null) {
RenderShortRepositoryHeader(it, accountViewModel, nav)
Spacer(modifier = DoubleVertSpacer)
@@ -54,7 +54,7 @@ import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.navigation.routeFor
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip01Core.core.firstTagValueFor
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip02FollowList.EmptyTagList
import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent
@@ -81,7 +81,7 @@ fun RenderHighlight(
context = noteEvent.context(),
authorHex = noteEvent.pubKey,
url = noteEvent.inUrl(),
postAddress = noteEvent.inPost(),
postAddress = noteEvent.inPostAddress(),
postVersion = noteEvent.inPostVersion(),
makeItShort = makeItShort,
canPreview = canPreview,
@@ -99,7 +99,7 @@ fun DisplayHighlight(
context: String?,
authorHex: String?,
url: String?,
postAddress: ATag?,
postAddress: Address?,
postVersion: ETag?,
makeItShort: Boolean,
canPreview: Boolean,
@@ -149,7 +149,7 @@ private fun DisplayQuoteAuthor(
highlightQuote: String,
authorHex: String?,
baseUrl: String?,
postAddress: ATag?,
postAddress: Address?,
postVersion: ETag?,
accountViewModel: AccountViewModel,
nav: INav,
@@ -165,7 +165,7 @@ private fun DisplayQuoteAuthor(
}
var addressable by remember {
mutableStateOf<AddressableNote?>(postAddress?.let { accountViewModel.getAddressableNoteIfExists(it.toTag()) })
mutableStateOf<AddressableNote?>(postAddress?.let { accountViewModel.getAddressableNoteIfExists(it) })
}
if (addressable == null && postAddress != null) {
@@ -66,13 +66,13 @@ fun RenderInteractiveStory(
val rootEvent = note.value?.note?.event as? InteractiveStoryBaseEvent ?: return
// keep updating the reading state event with new versions
val readingStateNote = accountViewModel.getInteractiveStoryReadingState(address.toTag())
val readingStateNote = accountViewModel.getInteractiveStoryReadingState(address.toValue())
val latestReadingNoteState = readingStateNote.live().metadata.observeAsState()
val readingState = latestReadingNoteState.value?.note?.event as? InteractiveStoryReadingStateEvent
val currentScene = readingState?.currentScene()
if (currentScene != null && currentScene != rootEvent.aTag()) {
if (currentScene != null && currentScene != rootEvent.address()) {
LoadAddressableNote(currentScene, accountViewModel) { currentSceneBaseNote ->
val currentScene = currentSceneBaseNote?.live()?.metadata?.observeAsState()
val currentSceneEvent = currentScene?.value?.note?.event as? InteractiveStoryBaseEvent
@@ -56,7 +56,7 @@ fun RenderPostApproval(
val noteEvent = note.event as? CommunityPostApprovalEvent ?: return
Column(Modifier.fillMaxWidth()) {
noteEvent.communities().forEach { tag ->
noteEvent.communityAddresses().forEach { tag ->
LoadAddressableNote(tag, accountViewModel) { baseNote ->
baseNote?.let {
RenderCommunity(
@@ -127,7 +127,7 @@ class FollowListState(
checkNotInMainThread()
val hasNewList =
newNotes.any {
newNotes.any { it ->
val noteEvent = it.event
noteEvent?.pubKey == account.userProfile().pubkeyHex &&
@@ -79,7 +79,7 @@ import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
import com.vitorpamplona.quartz.nip01Core.metadata.UserMetadata
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip01Core.tags.people.PubKeyReferenceTag
import com.vitorpamplona.quartz.nip01Core.tags.people.isTaggedUser
import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUserIds
@@ -987,17 +987,17 @@ class AccountViewModel(
}
fun updateStatus(
it: ATag,
address: Address,
newStatus: String,
) {
viewModelScope.launch(Dispatchers.IO) {
account.updateStatus(LocalCache.getOrCreateAddressableNote(it), newStatus)
account.updateStatus(LocalCache.getOrCreateAddressableNote(address), newStatus)
}
}
fun deleteStatus(it: ATag) {
fun deleteStatus(address: Address) {
viewModelScope.launch(Dispatchers.IO) {
account.deleteStatus(LocalCache.getOrCreateAddressableNote(it))
account.deleteStatus(LocalCache.getOrCreateAddressableNote(address))
}
}
@@ -1124,10 +1124,10 @@ class AccountViewModel(
viewModelScope.launch(Dispatchers.IO) { onResult(checkGetOrCreateAddressableNote(key)) }
}
suspend fun getOrCreateAddressableNote(key: ATag): AddressableNote = LocalCache.getOrCreateAddressableNote(key)
suspend fun getOrCreateAddressableNote(key: Address): AddressableNote = LocalCache.getOrCreateAddressableNote(key)
fun getOrCreateAddressableNote(
key: ATag,
key: Address,
onResult: (AddressableNote?) -> Unit,
) {
viewModelScope.launch(Dispatchers.IO) { onResult(getOrCreateAddressableNote(key)) }
@@ -1135,6 +1135,8 @@ class AccountViewModel(
fun getAddressableNoteIfExists(key: String): AddressableNote? = LocalCache.getAddressableNoteIfExists(key)
fun getAddressableNoteIfExists(key: Address): AddressableNote? = LocalCache.getAddressableNoteIfExists(key)
suspend fun findStatusesForUser(
myUser: User,
onResult: (ImmutableList<AddressableNote>) -> Unit,
@@ -1539,7 +1541,7 @@ class AccountViewModel(
): Note {
val note =
if (innerEvent is AddressableEvent) {
AddressableNote(innerEvent.aTag())
AddressableNote(innerEvent.address())
} else {
Note(innerEvent.id)
}
@@ -1598,14 +1600,14 @@ class AccountViewModel(
AdvertisedRelayListEvent.createAddressTag(user.pubkeyHex),
)
fun getInteractiveStoryReadingState(dATag: String): AddressableNote = LocalCache.getOrCreateAddressableNote(InteractiveStoryReadingStateEvent.createAddressATag(account.signer.pubKey, dATag))
fun getInteractiveStoryReadingState(dATag: String): AddressableNote = LocalCache.getOrCreateAddressableNote(InteractiveStoryReadingStateEvent.createAddress(account.signer.pubKey, dATag))
fun updateInteractiveStoryReadingState(
root: InteractiveStoryBaseEvent,
readingScene: InteractiveStoryBaseEvent,
) {
viewModelScope.launch(Dispatchers.IO) {
val sceneNoteRelayHint = LocalCache.getOrCreateAddressableNote(readingScene.aTag()).relayHintUrl()
val sceneNoteRelayHint = LocalCache.getOrCreateAddressableNote(readingScene.address()).relayHintUrl()
val readingState = getInteractiveStoryReadingState(root.addressTag())
val readingStateEvent = readingState.event as? InteractiveStoryReadingStateEvent
@@ -1613,7 +1615,7 @@ class AccountViewModel(
if (readingStateEvent != null) {
account.updateInteractiveStoryReadingState(readingStateEvent, readingScene, sceneNoteRelayHint)
} else {
val rootNoteRelayHint = LocalCache.getOrCreateAddressableNote(root.aTag()).relayHintUrl()
val rootNoteRelayHint = LocalCache.getOrCreateAddressableNote(root.address()).relayHintUrl()
account.createInteractiveStoryReadingState(root, rootNoteRelayHint, readingScene, sceneNoteRelayHint)
}
@@ -486,11 +486,7 @@ private suspend fun innerSendPost(
imetas(usedAttachments)
}
} else {
if (channel.address.relay == null) {
channel.address.relay = channelRelays.firstOrNull() ?: replyingToEvent?.relay
}
LiveActivitiesChatMessageEvent.message(tagger.message, channel.address) {
LiveActivitiesChatMessageEvent.message(tagger.message, channel.toATag()) {
hashtags(findHashtags(tagger.message))
references(findURLs(tagger.message))
quotes(findNostrUris(tagger.message))
@@ -176,7 +176,7 @@ import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.ZeroPadding
import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.amethyst.ui.theme.userProfileBorderModifier
import com.vitorpamplona.quartz.nip01Core.tags.addressables.taggedAddresses
import com.vitorpamplona.quartz.nip01Core.tags.addressables.taggedATags
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.events.taggedEvents
import com.vitorpamplona.quartz.nip02FollowList.EmptyTagList
@@ -704,7 +704,7 @@ private fun BookmarkTabHeader(baseUser: User) {
(
bookmarkList?.taggedEvents()?.count()
?: 0
) + (bookmarkList?.taggedAddresses()?.count() ?: 0)
) + (bookmarkList?.taggedATags()?.count() ?: 0)
if (newBookmarks != userBookmarks) {
userBookmarks = newBookmarks
@@ -1349,8 +1349,8 @@ private fun DisplayBadges(
nav: INav,
) {
LoadAddressableNote(
aTag = BadgeProfilesEvent.createAddressTag(baseUser.pubkeyHex),
accountViewModel = accountViewModel,
BadgeProfilesEvent.createAddress(baseUser.pubkeyHex),
accountViewModel,
) { note ->
if (note != null) {
WatchAndRenderBadgeList(
@@ -1004,7 +1004,7 @@ private fun RenderWikiHeaderForThread(
}
forkedAddress?.let {
LoadAddressableNote(aTag = it, accountViewModel = accountViewModel) { originalVersion ->
LoadAddressableNote(it, accountViewModel) { originalVersion ->
if (originalVersion != null) {
ForkInformationRow(originalVersion, Modifier.fillMaxWidth(), accountViewModel, nav)
}
@@ -25,6 +25,7 @@ import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.BaseReplaceableEvent
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip31Alts.AltTag
import com.vitorpamplona.quartz.utils.TimeUtils
@@ -50,6 +51,8 @@ class BlossomServersEvent(
const val KIND = 10063
const val ALT = "File servers used by the author"
fun createAddress(pubKey: HexKey): Address = Address(KIND, pubKey, FIXED_D_TAG)
fun createAddressATag(pubKey: HexKey): ATag = ATag(KIND, pubKey, FIXED_D_TAG, null)
fun createAddressTag(pubKey: HexKey): String = ATag.assembleATagId(KIND, pubKey, FIXED_D_TAG)
@@ -29,6 +29,7 @@ import com.vitorpamplona.quartz.nip01Core.core.TagArray
import com.vitorpamplona.quartz.nip01Core.jackson.EventMapper
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip21UriScheme.toNostrUri
import com.vitorpamplona.quartz.nip31Alts.AltTag
import com.vitorpamplona.quartz.utils.TimeUtils
@@ -102,6 +103,8 @@ class PrivateOutboxRelayListEvent(
const val KIND = 10013
val TAGS = arrayOf(AltTag.assemble("Relay list to store private content from this author"))
fun createAddress(pubKey: HexKey): Address = Address(KIND, pubKey, FIXED_D_TAG)
fun createAddressATag(pubKey: HexKey): ATag = ATag(KIND, pubKey, FIXED_D_TAG, null)
fun createAddressTag(pubKey: HexKey): String = ATag.assembleATagId(KIND, pubKey, FIXED_D_TAG)
@@ -21,7 +21,7 @@
package com.vitorpamplona.quartz.experimental.forks
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent
import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag
@@ -30,9 +30,7 @@ fun BaseThreadedEvent.isAFork() = tags.any { it.size > 3 && (it[0] == "a" || it[
fun BaseThreadedEvent.forkFromAddress() =
tags.firstOrNull { it.size > 3 && it[0] == "a" && it[3] == "fork" }?.let {
val aTagValue = it[1]
val relay = it.getOrNull(2)
ATag.parse(aTagValue, relay)
Address.parse(aTagValue)
}
fun BaseThreadedEvent.forkFromVersion() = tags.firstNotNullOfOrNull(MarkedETag::parseFork)
@@ -30,6 +30,7 @@ import com.vitorpamplona.quartz.nip01Core.core.builder
import com.vitorpamplona.quartz.nip01Core.signers.EventTemplate
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip01Core.tags.dTags.dTag
import com.vitorpamplona.quartz.nip23LongContent.tags.ImageTag
import com.vitorpamplona.quartz.nip23LongContent.tags.TitleTag
@@ -57,13 +58,18 @@ class InteractiveStoryReadingStateEvent(
fun root() = tags.firstNotNullOfOrNull(RootSceneTag::parse)
fun currentScene() = tags.firstNotNullOfOrNull(ATag::parse)
fun currentScene() = tags.firstNotNullOfOrNull(ATag::parseAddress)
companion object {
const val KIND = 30298
const val ALT1 = "Interactive Story Reading state"
const val ALT2 = "The reading state of "
fun createAddress(
pubKey: HexKey,
dtag: String,
): Address = Address(KIND, pubKey, dtag)
fun createAddressATag(
pubKey: HexKey,
dtag: String,
@@ -20,14 +20,16 @@
*/
package com.vitorpamplona.quartz.experimental.interactiveStories.tags
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip46RemoteSigner.getOrNull
import com.vitorpamplona.quartz.utils.arrayOfNotNull
class StoryOptionTag(
val option: String,
val address: ATag,
val address: Address,
val relay: String?,
) {
fun toTagArray() = assemble(option, address)
fun toTagArray() = assemble(option, address, relay)
companion object {
const val TAG_NAME = "option"
@@ -37,13 +39,16 @@ class StoryOptionTag(
fun parse(tag: Array<String>): StoryOptionTag? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
return ATag.parse(tag[2], tag.getOrNull(3))?.let { StoryOptionTag(tag[1], it) }
val address = Address.parse(tag[2]) ?: return null
return StoryOptionTag(tag[1], address, tag.getOrNull(3))
}
@JvmStatic
fun assemble(
title: String,
destination: ATag,
) = arrayOfNotNull(TAG_NAME, title, destination.toTag(), destination.relay)
address: Address,
relay: String?,
) = arrayOfNotNull(TAG_NAME, title, address.toValue(), relay)
}
}
@@ -46,5 +46,5 @@ open class BaseAddressableEvent(
/**
* Creates the tag in a memory efficient way (without creating the ATag class
*/
override fun addressTag() = ATag.assembleATagId(kind, pubKey, dTag())
override fun addressTag() = Address.assemble(kind, pubKey, dTag())
}
@@ -44,7 +44,7 @@ open class BaseReplaceableEvent(
/**
* Creates the tag in a memory efficient way (without creating the ATag class
*/
override fun addressTag() = ATag.assembleATagId(kind, pubKey, FIXED_D_TAG)
override fun addressTag() = Address.assemble(kind, pubKey, dTag())
companion object {
const val FIXED_D_TAG = ""
@@ -34,19 +34,9 @@ data class ATag(
val kind: Int,
val pubKeyHex: String,
val dTag: String,
val relay: String? = null,
) {
var relay: String? = null
constructor(address: Address) : this(address.kind, address.pubKeyHex, address.dTag)
constructor(
kind: Int,
pubKeyHex: HexKey,
dTag: String,
relayHint: String?,
) : this(kind, pubKeyHex, dTag) {
this.relay = relayHint
}
constructor(address: Address, relayHint: String? = null) : this(address.kind, address.pubKeyHex, address.dTag, relayHint)
fun countMemory(): Long =
5 * pointerSizeInBytes + // 7 fields, 4 bytes each reference (32bit)
@@ -129,6 +119,9 @@ data class ATag(
@JvmStatic
fun parseAddress(tag: Array<String>) = ATagParser.parseAddress(TAG_NAME, tag)
@JvmStatic
fun parseAddressId(tag: Array<String>) = ATagParser.parseAddressId(TAG_NAME, tag)
@JvmStatic
fun assemble(
aTagId: HexKey,
@@ -99,6 +99,15 @@ class ATagParser {
fun parseAddress(
tagName: String,
tag: Array<String>,
): Address? {
if (tag.isNotName(tagName, TAG_SIZE)) return null
return Address.parse(tag[1])
}
@JvmStatic
fun parseAddressId(
tagName: String,
tag: Array<String>,
): String? {
if (tag.isNotName(tagName, TAG_SIZE)) return null
return tag[1]
@@ -26,7 +26,7 @@ import com.vitorpamplona.quartz.utils.Hex
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
class Address(
data class Address(
val kind: Int,
val pubKeyHex: HexKey,
val dTag: String,
@@ -34,6 +34,10 @@ fun Event.isTaggedAddressableKind(kind: Int) = tags.isTaggedAddressableKind(kind
fun Event.getTagOfAddressableKind(kind: Int) = tags.getTagOfAddressableKind(kind)
fun Event.taggedATags() = tags.taggedATags()
fun Event.firstTaggedATag() = tags.firstTaggedATag()
fun Event.taggedAddresses() = tags.taggedAddresses()
fun Event.firstTaggedAddress() = tags.firstTaggedAddress()
@@ -29,14 +29,18 @@ fun <R> TagArray.mapTaggedAddress(map: (address: String) -> R) = this.mapValueTa
fun TagArray.firstIsTaggedAddressableNote(addressableNotes: Set<String>) = this.firstNotNullOfOrNull(ATag::parseIfIsIn, addressableNotes)
fun TagArray.isTaggedAddressableNote(idHex: String) = this.any(ATag::isTagged, idHex)
fun TagArray.isTaggedAddressableNote(addressId: String) = this.any(ATag::isTagged, addressId)
fun TagArray.isTaggedAddressableNotes(idHexes: Set<String>) = this.any(ATag::isIn, idHexes)
fun TagArray.isTaggedAddressableNotes(addressIds: Set<String>) = this.any(ATag::isIn, addressIds)
fun TagArray.isTaggedAddressableKind(kind: Int) = this.any(ATag::isTaggedWithKind, kind.toString())
fun TagArray.getTagOfAddressableKind(kind: Int) = this.firstNotNullOfOrNull(ATag::parseIfOfKind, kind.toString())
fun TagArray.taggedAddresses() = this.mapNotNull(ATag::parse)
fun TagArray.taggedATags() = this.mapNotNull(ATag::parse)
fun TagArray.firstTaggedAddress() = this.firstNotNullOfOrNull(ATag::parse)
fun TagArray.firstTaggedATag() = this.firstNotNullOfOrNull(ATag::parse)
fun TagArray.taggedAddresses() = this.mapNotNull(ATag::parseAddress)
fun TagArray.firstTaggedAddress() = this.firstNotNullOfOrNull(ATag::parseAddress)
@@ -55,7 +55,7 @@ class DeletionEvent(
fun deleteAddresses() = taggedAddresses()
fun deleteAddressTags() = tags.mapNotNull(ATag::parseAddress)
fun deleteAddressIds() = tags.mapNotNull(ATag::parseAddressId)
companion object {
const val KIND = 5
@@ -23,7 +23,7 @@ package com.vitorpamplona.quartz.nip10Notes
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.tags.addressables.taggedAddresses
import com.vitorpamplona.quartz.nip01Core.tags.addressables.taggedATags
import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUsers
import com.vitorpamplona.quartz.nip10Notes.content.findIndexTagsWithEventsOrAddresses
import com.vitorpamplona.quartz.nip10Notes.content.findIndexTagsWithPeople
@@ -141,7 +141,7 @@ open class BaseThreadedEvent(
val uncertainRepliesTo = unmarkedReplyTos()
val tagAddresses =
taggedAddresses()
taggedATags()
.filter {
it.kind != CommunityDefinitionEvent.KIND && (kind != WikiNoteEvent.KIND || it.kind != WikiNoteEvent.KIND)
// removes forks from itself.
@@ -26,6 +26,7 @@ import com.vitorpamplona.quartz.nip01Core.core.BaseReplaceableEvent
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip31Alts.AltTag
import com.vitorpamplona.quartz.utils.TimeUtils
@@ -50,6 +51,8 @@ class ChatMessageRelayListEvent(
companion object {
const val KIND = 10050
fun createAddress(pubKey: HexKey): Address = Address(KIND, pubKey, FIXED_D_TAG)
fun createAddressATag(pubKey: HexKey): ATag = ATag(KIND, pubKey, FIXED_D_TAG, null)
fun createAddressTag(pubKey: HexKey): String = ATag.assembleATagId(KIND, pubKey, FIXED_D_TAG)
@@ -75,12 +75,14 @@ data class NAddress(
kind: Int,
pubKeyHex: String,
dTag: String,
relay: String?,
vararg relays: String?,
): String =
TlvBuilder()
.apply {
addString(TlvTypes.SPECIAL, dTag)
addStringIfNotNull(TlvTypes.RELAY, relay)
relays.forEach {
addStringIfNotNull(TlvTypes.RELAY, it)
}
addHex(TlvTypes.AUTHOR, pubKeyHex)
addInt(TlvTypes.KIND, kind)
}.build()
@@ -59,12 +59,14 @@ data class NEvent(
idHex: String,
author: String?,
kind: Int?,
relay: String?,
vararg relays: String?,
): String =
TlvBuilder()
.apply {
addHex(TlvTypes.SPECIAL, idHex)
addStringIfNotNull(TlvTypes.RELAY, relay)
relays.forEach {
addStringIfNotNull(TlvTypes.RELAY, it)
}
addHexIfNotNull(TlvTypes.AUTHOR, author)
addIntIfNotNull(TlvTypes.KIND, kind)
}.build()
@@ -54,7 +54,7 @@ class LongTextNoteEvent(
override fun address() = Address(kind, pubKey, dTag())
override fun addressTag() = ATag.assembleATagId(kind, pubKey, dTag())
override fun addressTag() = Address.assemble(kind, pubKey, dTag())
fun topics() = hashtags()
@@ -28,7 +28,8 @@ import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.signers.eventUpdate
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.taggedAddresses
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip01Core.tags.addressables.taggedATags
import com.vitorpamplona.quartz.nip30CustomEmoji.pack.EmojiPackEvent
import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.utils.TimeUtils
@@ -42,13 +43,14 @@ class EmojiPackSelectionEvent(
content: String,
sig: HexKey,
) : BaseReplaceableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
fun emojiPacks() = taggedAddresses()
fun emojiPacks() = taggedATags()
companion object {
const val KIND = 10030
const val ALT_DESCRIPTION = "Emoji selection"
const val FIXED_D_TAG = ""
fun createAddressATag(pubKey: HexKey) = ATag(KIND, pubKey, FIXED_D_TAG, null)
fun createAddress(pubKey: HexKey) = Address(KIND, pubKey, FIXED_D_TAG)
fun createAddressTag(pubKey: HexKey) = ATag.assembleATagId(KIND, pubKey, FIXED_D_TAG)
@@ -43,7 +43,9 @@ class GitIssueEvent(
content: String,
sig: HexKey,
) : BaseThreadedEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
fun repositoryHex() = tags.firstNotNullOfOrNull(ATag::parseAddress)
fun repositoryHex() = tags.firstNotNullOfOrNull(ATag::parseAddressId)
fun repositoryAddress() = tags.firstNotNullOfOrNull(ATag::parseAddress)
fun repository() = tags.firstNotNullOfOrNull(ATag::parse)
@@ -25,6 +25,7 @@ import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip31Alts.AltTag
import com.vitorpamplona.quartz.utils.TimeUtils
@@ -43,6 +44,15 @@ class GitPatchEvent(
private fun repositoryHex() = innerRepository()?.getOrNull(1)
fun repositoryAddress() =
innerRepository()?.let {
if (it.size > 1) {
Address.parse(it[1])
} else {
null
}
}
fun repository() =
innerRepository()?.let {
if (it.size > 1) {
@@ -44,7 +44,7 @@ class GitReplyEvent(
content: String,
sig: HexKey,
) : BaseThreadedEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
fun repositoryHex() = tags.firstNotNullOfOrNull(ATag::parseAddress)
fun repositoryHex() = tags.firstNotNullOfOrNull(ATag::parseAddressId)
fun repository() = tags.firstNotNullOfOrNull(ATag::parse)
@@ -26,6 +26,7 @@ import com.vitorpamplona.quartz.nip01Core.core.BaseReplaceableEvent
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip31Alts.AltTag
import com.vitorpamplona.quartz.utils.TimeUtils
@@ -50,6 +51,8 @@ class SearchRelayListEvent(
companion object {
const val KIND = 10007
fun createAddress(pubKey: HexKey): Address = Address(KIND, pubKey, FIXED_D_TAG)
fun createAddressATag(pubKey: HexKey): ATag = ATag(KIND, pubKey, FIXED_D_TAG, null)
fun createAddressTag(pubKey: HexKey): String = ATag.assembleATagId(KIND, pubKey, FIXED_D_TAG)
@@ -26,6 +26,7 @@ import com.vitorpamplona.quartz.nip01Core.core.isTagged
import com.vitorpamplona.quartz.nip01Core.jackson.EventMapper
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip01Core.tags.events.taggedEvents
import com.vitorpamplona.quartz.nip01Core.tags.geohash.geohashes
import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUserIds
@@ -113,9 +114,14 @@ abstract class GeneralListEvent(
onReady: (List<String>) -> Unit,
) = privateTags(signer) { onReady(filterEvents(it)) }
fun privateTaggedAddresses(
fun privateATags(
signer: NostrSigner,
onReady: (List<ATag>) -> Unit,
) = privateTags(signer) { onReady(filterATags(it)) }
fun privateAddress(
signer: NostrSigner,
onReady: (List<Address>) -> Unit,
) = privateTags(signer) { onReady(filterAddresses(it)) }
fun filterUsers(tags: Array<Array<String>>): List<String> = tags.filter { it.size > 1 && it[0] == "p" }.map { it[1] }
@@ -126,15 +132,9 @@ abstract class GeneralListEvent(
fun filterEvents(tags: Array<Array<String>>): List<String> = tags.filter { it.size > 1 && it[0] == "e" }.map { it[1] }
fun filterAddresses(tags: Array<Array<String>>): List<ATag> =
tags
.filter { it.firstOrNull() == "a" }
.mapNotNull {
val aTagValue = it.getOrNull(1)
val relay = it.getOrNull(2)
fun filterATags(tags: Array<Array<String>>): List<ATag> = tags.mapNotNull(ATag::parse)
if (aTagValue != null) ATag.parse(aTagValue, relay) else null
}
fun filterAddresses(tags: Array<Array<String>>): List<Address> = tags.mapNotNull(ATag::parseAddress)
companion object {
fun createPrivateTags(
@@ -22,8 +22,8 @@ package com.vitorpamplona.quartz.nip51Lists
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.BaseReplaceableEvent.Companion.FIXED_D_TAG
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip31Alts.AltTag
import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
@@ -77,8 +77,11 @@ class MuteListEvent(
companion object {
const val KIND = 10000
const val FIXED_D_TAG = ""
const val ALT = "Mute List"
fun createAddress(pubKey: HexKey) = Address(KIND, pubKey, FIXED_D_TAG)
fun blockListFor(pubKeyHex: HexKey): String = "10000:$pubKeyHex:"
fun createListWithTag(
@@ -23,6 +23,7 @@ package com.vitorpamplona.quartz.nip51Lists
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip31Alts.AltTag
import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
@@ -127,6 +128,8 @@ class PeopleListEvent(
const val BLOCK_LIST_D_TAG = "mute"
const val ALT = "List of people"
fun createBlockAddress(pubKey: HexKey) = Address(KIND, pubKey, BLOCK_LIST_D_TAG)
fun blockListFor(pubKeyHex: HexKey): String = "30000:$pubKeyHex:$BLOCK_LIST_D_TAG"
fun createListWithTag(
@@ -39,10 +39,12 @@ class LiveActivitiesChatMessageEvent(
content: String,
sig: HexKey,
) : BaseThreadedEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
private fun activityHex() = tags.firstNotNullOfOrNull(ATag::parseAddress)
private fun activityHex() = tags.firstNotNullOfOrNull(ATag::parseAddressId)
fun activity() = tags.firstNotNullOfOrNull(ATag::parse)
fun activityAddress() = tags.firstNotNullOfOrNull(ATag::parseAddress)
override fun markedReplyTos() = super.markedReplyTos().minus(activityHex() ?: "")
override fun unmarkedReplyTos() = super.markedReplyTos().minus(activityHex() ?: "")
@@ -48,7 +48,7 @@ class WikiNoteEvent(
override fun address() = Address(kind, pubKey, dTag())
override fun addressTag() = ATag.assembleATagId(kind, pubKey, dTag())
override fun addressTag() = Address.assemble(kind, pubKey, dTag())
fun topics() = hashtags()
@@ -24,6 +24,7 @@ import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip01Core.tags.addressables.taggedAddresses
import com.vitorpamplona.quartz.nip01Core.tags.events.taggedEvents
@@ -45,6 +46,8 @@ class BadgeProfilesEvent(
private const val STANDARD_D_TAG = "profile_badges"
private const val ALT = "List of accepted badges by the author"
fun createAddress(pubKey: HexKey): Address = Address(KIND, pubKey, STANDARD_D_TAG)
fun createAddressTag(pubKey: HexKey): ATag = ATag(KIND, pubKey, STANDARD_D_TAG, null)
}
}
@@ -26,6 +26,7 @@ import com.vitorpamplona.quartz.nip01Core.core.BaseReplaceableEvent
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip31Alts.AltTag
import com.vitorpamplona.quartz.utils.TimeUtils
@@ -85,6 +86,8 @@ class AdvertisedRelayListEvent(
const val KIND = 10002
const val ALT = "Relay list to discover the user's content"
fun createAddress(pubKey: HexKey): Address = Address(KIND, pubKey, FIXED_D_TAG)
fun createAddressATag(pubKey: HexKey): ATag = ATag(KIND, pubKey, FIXED_D_TAG, null)
fun createAddressTag(pubKey: HexKey): String = ATag.assembleATagId(KIND, pubKey, FIXED_D_TAG)
@@ -27,6 +27,7 @@ import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.addressables.taggedATags
import com.vitorpamplona.quartz.nip01Core.tags.addressables.taggedAddresses
import com.vitorpamplona.quartz.nip01Core.tags.events.taggedEvents
import com.vitorpamplona.quartz.nip01Core.tags.kinds.kind
@@ -54,10 +55,14 @@ class CommunityPostApprovalEvent(
null
}
fun communities() = taggedAddresses().filter { it.kind == CommunityDefinitionEvent.KIND }
fun communities() = taggedATags().filter { it.kind == CommunityDefinitionEvent.KIND }
fun communityAddresses() = taggedAddresses().filter { it.kind == CommunityDefinitionEvent.KIND }
fun approvedEvents() = taggedEvents()
fun approvedATags() = taggedATags().filter { it.kind != CommunityDefinitionEvent.KIND }
fun approvedAddresses() = taggedAddresses().filter { it.kind != CommunityDefinitionEvent.KIND }
companion object {
@@ -24,6 +24,7 @@ import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip31Alts.AltTag
import com.vitorpamplona.quartz.utils.TimeUtils
@@ -39,6 +40,11 @@ class AppSpecificDataEvent(
const val KIND = 30078
const val ALT = "Arbitrary app data"
fun createAddress(
pubKey: HexKey,
dTag: String,
) = Address(KIND, pubKey, dTag)
fun createTag(
pubkey: HexKey,
dTag: String,
@@ -23,6 +23,7 @@ package com.vitorpamplona.quartz.nip84Highlights
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.firstTaggedATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.firstTaggedAddress
import com.vitorpamplona.quartz.nip01Core.tags.events.firstTaggedEvent
import com.vitorpamplona.quartz.nip01Core.tags.people.firstTaggedUser
@@ -49,7 +50,9 @@ class HighlightEvent(
fun context() = tags.firstNotNullOfOrNull(ContextTag::parse)
fun inPost() = firstTaggedAddress()
fun inPost() = firstTaggedATag()
fun inPostAddress() = firstTaggedAddress()
fun inPostVersion() = firstTaggedEvent()
@@ -26,6 +26,7 @@ import com.vitorpamplona.quartz.nip01Core.core.BaseReplaceableEvent
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.nip96FileStorage.config.tags.ServerTag
import com.vitorpamplona.quartz.utils.TimeUtils
@@ -45,6 +46,8 @@ class FileServersEvent(
const val KIND = 10096
const val ALT_DESCRIPTOR = "File servers used by the author"
fun createAddress(pubKey: HexKey): Address = Address(KIND, pubKey, FIXED_D_TAG)
fun createAddressATag(pubKey: HexKey): ATag = ATag(KIND, pubKey, FIXED_D_TAG, null)
fun createAddressTag(pubKey: HexKey): String = ATag.assembleATagId(KIND, pubKey, FIXED_D_TAG)