- Normalizes the use of ETag

- Creates a BaseReplaceable event to avoid duplicates of empty d tags.
This commit is contained in:
Vitor Pamplona 2025-01-15 11:48:01 -05:00
parent 5a3c821da4
commit c25d64558e
46 changed files with 393 additions and 145 deletions

View File

@ -74,17 +74,18 @@ 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.taggedAddresses
import com.vitorpamplona.quartz.nip01Core.tags.events.taggedEvents
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.events.taggedEventIds
import com.vitorpamplona.quartz.nip01Core.tags.geohash.geohashes
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags
import com.vitorpamplona.quartz.nip01Core.tags.people.hasAnyTaggedUser
import com.vitorpamplona.quartz.nip01Core.tags.people.isTaggedUser
import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUsers
import com.vitorpamplona.quartz.nip02FollowList.Contact
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
import com.vitorpamplona.quartz.nip03Timestamp.OtsEvent
import com.vitorpamplona.quartz.nip04Dm.PrivateDmEvent
import com.vitorpamplona.quartz.nip09Deletions.DeletionEvent
import com.vitorpamplona.quartz.nip10Notes.ETag
import com.vitorpamplona.quartz.nip10Notes.PTag
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
import com.vitorpamplona.quartz.nip17Dm.ChatMessageRelayListEvent
@ -954,7 +955,7 @@ class Account(
onReady: (LiveFollowList) -> Unit,
) {
listEvent.privateTags(signer) { privateTagList ->
val users = (listEvent.bookmarkedPeople() + listEvent.filterUsers(privateTagList)).toSet()
val users = (listEvent.taggedUsers() + listEvent.filterUsers(privateTagList)).toSet()
onReady(
LiveFollowList(
authors = users,
@ -970,6 +971,11 @@ class Account(
}
}
fun decryptPeopleList(
event: GeneralListEvent,
onReady: (Array<Array<String>>) -> Unit,
) = event.privateTags(signer, onReady)
suspend fun waitToDecrypt(peopleListFollows: GeneralListEvent): LiveFollowList? =
tryAndWait { continuation ->
decryptLiveFollows(peopleListFollows) {
@ -2445,7 +2451,7 @@ class Account(
directMentionsNotes
.mapNotNullTo(HashSet(directMentionsNotes.size)) { note ->
if (note !is AddressableNote) {
ETag(note.idHex, note.author?.pubkeyHex, note.relayHintUrl())
ETag(note.idHex, note.relayHintUrl(), note.author?.pubkeyHex)
} else {
null
}
@ -2556,7 +2562,7 @@ class Account(
directMentionsNotes
.mapNotNullTo(HashSet(directMentionsNotes.size)) { note ->
if (note !is AddressableNote) {
ETag(note.idHex, note.author?.pubkeyHex, note.relayHintUrl())
ETag(note.idHex, note.relayHintUrl(), note.author?.pubkeyHex)
} else {
null
}
@ -3520,7 +3526,7 @@ class Account(
if (note is AddressableNote) {
return userProfile().latestBookmarkList?.taggedAddresses()?.contains(note.address) == true
} else {
return userProfile().latestBookmarkList?.taggedEvents()?.contains(note.idHex) == true
return userProfile().latestBookmarkList?.taggedEventIds()?.contains(note.idHex) == true
}
}
@ -3675,7 +3681,7 @@ class Account(
fun selectedChatsFollowList(): Set<String> {
val contactList = userProfile().latestContactList
return contactList?.taggedEvents()?.toSet() ?: DefaultChannels
return contactList?.taggedEventIds()?.toSet() ?: DefaultChannels
}
fun sendChangeChannel(

View File

@ -57,9 +57,10 @@ import com.vitorpamplona.quartz.nip01Core.hasValidSignature
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.mapTaggedAddress
import com.vitorpamplona.quartz.nip01Core.tags.addressables.taggedAddresses
import com.vitorpamplona.quartz.nip01Core.tags.events.forEachTaggedEvent
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.events.forEachTaggedEventId
import com.vitorpamplona.quartz.nip01Core.tags.events.isTaggedEvent
import com.vitorpamplona.quartz.nip01Core.tags.events.mapTaggedEvent
import com.vitorpamplona.quartz.nip01Core.tags.events.mapTaggedEventId
import com.vitorpamplona.quartz.nip01Core.tags.events.taggedEvents
import com.vitorpamplona.quartz.nip01Core.tags.people.isTaggedUsers
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
@ -78,7 +79,6 @@ import com.vitorpamplona.quartz.nip18Reposts.RepostEvent
import com.vitorpamplona.quartz.nip19Bech32.decodeEventIdAsHexOrNull
import com.vitorpamplona.quartz.nip19Bech32.decodePublicKeyAsHexOrNull
import com.vitorpamplona.quartz.nip19Bech32.isATag
import com.vitorpamplona.quartz.nip19Bech32.parse
import com.vitorpamplona.quartz.nip22Comments.CommentEvent
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent
import com.vitorpamplona.quartz.nip25Reactions.ReactionEvent
@ -239,7 +239,7 @@ object LocalCache {
private fun updateObservables(event: Event) {
observablesByKindAndETag[event.kind]?.let { observablesOfKind ->
event.forEachTaggedEvent {
event.forEachTaggedEventId {
observablesOfKind[it]?.updateIfMatches(event)
}
}
@ -274,6 +274,8 @@ object LocalCache {
fun getNoteIfExists(key: String): Note? = addressables.get(key) ?: notes.get(key)
fun getNoteIfExists(key: ETag): Note? = notes.get(key.eventId)
fun getChannelIfExists(key: String): Channel? = channels.get(key)
fun getNoteIfExists(event: Event): Note? =
@ -290,6 +292,15 @@ object LocalCache {
getOrCreateNote(event.id)
}
fun checkGetOrCreateNote(etag: ETag): Note? {
checkNotInMainThread()
if (isValidHex(etag.eventId)) {
return getOrCreateNote(etag)
}
return null
}
fun checkGetOrCreateNote(key: String): Note? {
checkNotInMainThread()
@ -401,6 +412,16 @@ object LocalCache {
return note
}
fun getOrCreateNote(key: ETag): Note {
val note = getOrCreateNote(key.eventId)
// Loads the user outside a Syncronized block to avoid blocking
val possibleAuthor = key.authorPubKeyHex
if (note.author == null && possibleAuthor != null) {
note.author = checkGetOrCreateUser(possibleAuthor)
}
return note
}
fun consume(
event: MetadataEvent,
relay: Relay?,
@ -681,7 +702,7 @@ object LocalCache {
event.tagsWithoutCitations().mapNotNull { checkGetOrCreateNote(it) }
is DraftEvent -> {
event.mapTaggedEvent { checkGetOrCreateNote(it) } + event.mapTaggedAddress { checkGetOrCreateAddressableNote(it) }
event.mapTaggedEventId { checkGetOrCreateNote(it) } + event.mapTaggedAddress { checkGetOrCreateAddressableNote(it) }
}
else -> emptyList<Note>()
@ -1562,7 +1583,7 @@ object LocalCache {
note.loadEvent(event, author, emptyList())
event.editedNote()?.let {
checkGetOrCreateNote(it)?.let { editedNote ->
checkGetOrCreateNote(it.eventId)?.let { editedNote ->
modificationCache.remove(editedNote.idHex)
// must update list of Notes to quickly update the user.
editedNote.liveSet?.innerModifications?.invalidateData()

View File

@ -24,7 +24,7 @@ import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.ui.actions.relays.updated
import com.vitorpamplona.quartz.nip01Core.tags.events.taggedEvents
import com.vitorpamplona.quartz.nip01Core.tags.events.taggedEventIds
import com.vitorpamplona.quartz.nip17Dm.ChatroomKey
import com.vitorpamplona.quartz.nip17Dm.ChatroomKeyable
import com.vitorpamplona.quartz.nip28PublicChat.ChannelMessageEvent
@ -141,7 +141,7 @@ class ChatroomListKnownFeedFilter(
account
.userProfile()
.latestContactList
?.taggedEvents()
?.taggedEventIds()
?.toSet() ?: emptySet()
val newRelevantPublicMessages = mutableMapOf<String, Note>()
newItems

View File

@ -120,7 +120,7 @@ class FeedContentState(
if (deletionEvents.isEmpty()) {
oldNotesState.feed.value.list
} else {
val deletedEventIds = deletionEvents.flatMapTo(HashSet()) { it.deleteEvents() }
val deletedEventIds = deletionEvents.flatMapTo(HashSet()) { it.deleteEventIds() }
val deletedEventAddresses = deletionEvents.flatMapTo(HashSet()) { it.deleteAddresses() }
oldNotesState.feed.value.list
.filter { !it.wasOrShouldBeDeletedBy(deletedEventIds, deletedEventAddresses) }

View File

@ -75,6 +75,7 @@ import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.quartz.nip01Core.UserMetadata
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.firstTaggedAddress
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.events.firstTaggedEvent
import com.vitorpamplona.quartz.nip38UserStatus.StatusEvent
import com.vitorpamplona.quartz.utils.TimeUtils
@ -264,7 +265,7 @@ fun DisplayStatus(
event.dTag(),
event.firstTaggedUrl()?.ifBlank { null },
event.firstTaggedAddress(),
event.firstTaggedEvent()?.ifBlank { null },
event.firstTaggedEvent(),
accountViewModel,
nav,
)
@ -276,7 +277,7 @@ fun DisplayStatusInner(
type: String,
url: String?,
nostrATag: ATag?,
nostrHexID: String?,
nostrETag: ETag?,
accountViewModel: AccountViewModel,
nav: INav,
) {
@ -335,8 +336,8 @@ fun DisplayStatusInner(
}
}
}
} else if (nostrHexID != null) {
LoadNote(baseNoteHex = nostrHexID, accountViewModel) {
} else if (nostrETag != null) {
LoadNote(baseNoteHex = nostrETag.eventId, accountViewModel) {
if (it != null) {
Spacer(modifier = StdHorzSpacer)
IconButton(

View File

@ -53,9 +53,9 @@ import com.vitorpamplona.amethyst.ui.components.measureSpaceWidth
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.HexKey
import com.vitorpamplona.quartz.nip01Core.core.firstTagValueFor
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip02FollowList.EmptyTagList
import com.vitorpamplona.quartz.nip10Notes.BaseTextNoteEvent
import com.vitorpamplona.quartz.nip19Bech32.toNIP19
@ -100,7 +100,7 @@ fun DisplayHighlight(
authorHex: String?,
url: String?,
postAddress: ATag?,
postVersion: HexKey?,
postVersion: ETag?,
makeItShort: Boolean,
canPreview: Boolean,
quotesLeft: Int,
@ -150,7 +150,7 @@ private fun DisplayQuoteAuthor(
authorHex: String?,
baseUrl: String?,
postAddress: ATag?,
postVersion: HexKey?,
postVersion: ETag?,
accountViewModel: AccountViewModel,
nav: INav,
) {
@ -181,14 +181,14 @@ private fun DisplayQuoteAuthor(
}
var version by remember {
mutableStateOf<Note?>(postVersion?.let { accountViewModel.getNoteIfExists(it) })
mutableStateOf<Note?>(postVersion?.let { accountViewModel.getNoteIfExists(it.eventId) })
}
if (version == null && postVersion != null) {
LaunchedEffect(key1 = postVersion) {
val newNote =
withContext(Dispatchers.IO) {
accountViewModel.getOrCreateNote(postVersion)
accountViewModel.getOrCreateNote(postVersion.eventId)
}
if (version != newNote) {
version = newNote

View File

@ -84,13 +84,13 @@ fun RenderTextModificationEvent(
val isAuthorTheLoggedUser =
remember {
val authorOfTheOriginalNote =
noteEvent.editedNote()?.let { accountViewModel.getNoteIfExists(it)?.author }
noteEvent.editedNote()?.let { accountViewModel.getNoteIfExists(it.eventId)?.author?.pubkeyHex ?: it.authorPubKeyHex }
mutableStateOf(accountViewModel.isLoggedUser(authorOfTheOriginalNote))
}
noteEvent.editedNote()?.let {
LoadNote(baseNoteHex = it, accountViewModel = accountViewModel) { baseOriginalNote ->
LoadNote(baseNoteHex = it.eventId, accountViewModel = accountViewModel) { baseOriginalNote ->
baseOriginalNote?.let {
}
}
@ -128,7 +128,7 @@ fun RenderTextModificationEvent(
}
noteEvent.editedNote()?.let {
LoadNote(baseNoteHex = it, accountViewModel = accountViewModel) { baseNote ->
LoadNote(baseNoteHex = it.eventId, accountViewModel = accountViewModel) { baseNote ->
baseNote?.let {
val noteState by baseNote.live().metadata.observeAsState()

View File

@ -140,7 +140,7 @@ class FollowListState(
(
noteEvent is DeletionEvent &&
(
noteEvent.deleteEvents().any {
noteEvent.deleteEventIds().any {
LocalCache.getNoteIfExists(it)?.event is PeopleListEvent
} ||
noteEvent.deleteAddresses().any {

View File

@ -82,6 +82,7 @@ import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.people.isTaggedUser
import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUsers
import com.vitorpamplona.quartz.nip11RelayInfo.Nip11RelayInformation
import com.vitorpamplona.quartz.nip17Dm.ChatMessageRelayListEvent
import com.vitorpamplona.quartz.nip17Dm.ChatroomKey
@ -99,6 +100,7 @@ import com.vitorpamplona.quartz.nip19Bech32.entities.NSec
import com.vitorpamplona.quartz.nip37Drafts.DraftEvent
import com.vitorpamplona.quartz.nip47WalletConnect.Response
import com.vitorpamplona.quartz.nip50Search.SearchRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.GeneralListEvent
import com.vitorpamplona.quartz.nip56Reports.ReportEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent
@ -1071,7 +1073,7 @@ class AccountViewModel(
viewModelScope.launch(Dispatchers.IO) { runOnIO() }
}
suspend fun checkGetOrCreateUser(key: HexKey): User? = LocalCache.checkGetOrCreateUser(key)
fun checkGetOrCreateUser(key: HexKey): User? = LocalCache.checkGetOrCreateUser(key)
override suspend fun getOrCreateUser(key: HexKey): User = LocalCache.getOrCreateUser(key)
@ -1201,7 +1203,7 @@ class AccountViewModel(
hexList: List<String>,
onReady: (ImmutableList<User>) -> Unit,
) {
viewModelScope.launch(Dispatchers.IO) {
viewModelScope.launch(Dispatchers.Default) {
onReady(
hexList
.mapNotNull { hex -> checkGetOrCreateUser(hex) }
@ -1212,6 +1214,24 @@ class AccountViewModel(
}
}
fun loadUsers(
event: GeneralListEvent,
onReady: (ImmutableList<User>) -> Unit,
) {
viewModelScope.launch(Dispatchers.Default) {
account.decryptPeopleList(event) { privateTagList ->
onReady(
(event.taggedUsers() + event.filterUsers(privateTagList))
.toSet()
.mapNotNull { hex -> checkGetOrCreateUser(hex) }
.sortedBy { account.isFollowing(it) }
.reversed()
.toImmutableList(),
)
}
}
}
fun checkVideoIsOnline(
videoUrl: String,
onDone: (Boolean) -> Unit,

View File

@ -177,6 +177,7 @@ 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.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.events.taggedEvents
import com.vitorpamplona.quartz.nip02FollowList.EmptyTagList
import com.vitorpamplona.quartz.nip39ExtIdentities.GitHubIdentity
@ -1383,7 +1384,7 @@ private fun WatchAndRenderBadgeList(
@Composable
@OptIn(ExperimentalLayoutApi::class)
private fun RenderBadgeList(
list: ImmutableList<String>,
list: ImmutableList<ETag>,
loadProfilePicture: Boolean,
loadRobohash: Boolean,
nav: INav,
@ -1398,17 +1399,17 @@ private fun RenderBadgeList(
@Composable
private fun LoadAndRenderBadge(
badgeAwardEventHex: String,
badgeAwardEvent: ETag,
loadProfilePicture: Boolean,
loadRobohash: Boolean,
nav: INav,
) {
var baseNote by remember(badgeAwardEventHex) { mutableStateOf(LocalCache.getNoteIfExists(badgeAwardEventHex)) }
var baseNote by remember(badgeAwardEvent) { mutableStateOf(LocalCache.getNoteIfExists(badgeAwardEvent)) }
LaunchedEffect(key1 = badgeAwardEventHex) {
LaunchedEffect(key1 = badgeAwardEvent) {
if (baseNote == null) {
withContext(Dispatchers.IO) {
baseNote = LocalCache.checkGetOrCreateNote(badgeAwardEventHex)
baseNote = LocalCache.checkGetOrCreateNote(badgeAwardEvent)
}
}
}

View File

@ -22,7 +22,7 @@ package com.vitorpamplona.quartz.blossom
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
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.nip31Alts.AltTagSerializer
@ -36,9 +36,7 @@ class BlossomServersEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
override fun dTag() = FIXED_D_TAG
) : BaseReplaceableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
fun servers(): List<String> =
tags.mapNotNull {
if (it.size > 1 && it[0] == "server") {
@ -50,12 +48,11 @@ class BlossomServersEvent(
companion object {
const val KIND = 10063
const val FIXED_D_TAG = ""
const val ALT = "File servers used by the author"
fun createAddressATag(pubKey: HexKey): ATag = ATag(KIND, pubKey, FIXED_D_TAG, null)
fun createAddressTag(pubKey: HexKey): String = ATag.assembleATag(KIND, pubKey, FIXED_D_TAG)
fun createAddressTag(pubKey: HexKey): String = ATag.assembleATagId(KIND, pubKey, FIXED_D_TAG)
fun createTagArray(servers: List<String>): Array<Array<String>> =
servers

View File

@ -24,7 +24,7 @@ import android.util.Log
import androidx.compose.runtime.Immutable
import com.fasterxml.jackson.module.kotlin.readValue
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.BaseReplaceableEvent
import com.vitorpamplona.quartz.nip01Core.core.TagArray
import com.vitorpamplona.quartz.nip01Core.jackson.EventMapper
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
@ -43,15 +43,13 @@ class PrivateOutboxRelayListEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
) : BaseReplaceableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
@Transient private var privateTagsCache: Array<Array<String>>? = null
override fun countMemory(): Long =
super.countMemory() +
pointerSizeInBytes + (privateTagsCache?.sumOf { pointerSizeInBytes + it.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() } } ?: 0)
override fun dTag() = FIXED_D_TAG
fun relays(): List<String>? =
tags
.mapNotNull {
@ -102,12 +100,11 @@ class PrivateOutboxRelayListEvent(
companion object {
const val KIND = 10013
const val FIXED_D_TAG = ""
val TAGS = arrayOf(AltTagSerializer.toTagArray("Relay list to store private content from this author"))
fun createAddressATag(pubKey: HexKey): ATag = ATag(KIND, pubKey, FIXED_D_TAG, null)
fun createAddressTag(pubKey: HexKey): String = ATag.assembleATag(KIND, pubKey, FIXED_D_TAG)
fun createAddressTag(pubKey: HexKey): String = ATag.assembleATagId(KIND, pubKey, FIXED_D_TAG)
fun encryptTags(
privateTags: Array<Array<String>>? = null,

View File

@ -29,7 +29,6 @@ import com.vitorpamplona.quartz.nip01Core.tags.hashtags.buildHashtagTags
import com.vitorpamplona.quartz.nip10Notes.content.buildUrlRefs
import com.vitorpamplona.quartz.nip10Notes.content.findHashtags
import com.vitorpamplona.quartz.nip10Notes.content.findURLs
import com.vitorpamplona.quartz.nip19Bech32.parse
import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrl
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
import com.vitorpamplona.quartz.nip36SensitiveContent.ContentWarningSerializer

View File

@ -49,7 +49,7 @@ class InteractiveStoryPrologueEvent(
fun createAddressTag(
pubKey: HexKey,
dtag: String,
): String = ATag.assembleATag(KIND, pubKey, dtag)
): String = ATag.assembleATagId(KIND, pubKey, dtag)
fun create(
baseId: String,

View File

@ -27,7 +27,6 @@ import com.vitorpamplona.quartz.nip01Core.core.firstTag
import com.vitorpamplona.quartz.nip01Core.core.firstTagValue
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip19Bech32.parse
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.utils.removeTrailingNullsAndEmptyOthers
@ -66,7 +65,7 @@ class InteractiveStoryReadingStateEvent(
fun createAddressTag(
pubKey: HexKey,
dtag: String,
): String = ATag.assembleATag(KIND, pubKey, dtag)
): String = ATag.assembleATagId(KIND, pubKey, dtag)
fun update(
base: InteractiveStoryReadingStateEvent,

View File

@ -49,7 +49,7 @@ class InteractiveStorySceneEvent(
fun createAddressTag(
pubKey: HexKey,
dtag: String,
): String = ATag.assembleATag(KIND, pubKey, dtag)
): String = ATag.assembleATagId(KIND, pubKey, dtag)
fun create(
baseId: String,

View File

@ -23,7 +23,7 @@ package com.vitorpamplona.quartz.nip01Core.core
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.dTag
import com.vitorpamplona.quartz.nip01Core.tags.dTags.dTag
@Immutable
open class BaseAddressableEvent(
@ -43,5 +43,5 @@ open class BaseAddressableEvent(
/**
* Creates the tag in a memory efficient way (without creating the ATag class
*/
override fun addressTag() = ATag.assembleATag(kind, pubKey, dTag())
override fun addressTag() = ATag.assembleATagId(kind, pubKey, dTag())
}

View File

@ -0,0 +1,49 @@
/**
* Copyright (c) 2024 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nip01Core.core
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
@Immutable
open class BaseReplaceableEvent(
id: HexKey,
pubKey: HexKey,
createdAt: Long,
kind: Int,
tags: TagArray,
content: String,
sig: HexKey,
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) {
override fun dTag() = FIXED_D_TAG
override fun address(relayHint: String?) = ATag(kind, pubKey, FIXED_D_TAG, relayHint)
/**
* Creates the tag in a memory efficient way (without creating the ATag class
*/
override fun addressTag() = ATag.assembleATagId(kind, pubKey, FIXED_D_TAG)
companion object {
const val FIXED_D_TAG = ""
}
}

View File

@ -20,7 +20,11 @@
*/
package com.vitorpamplona.quartz.nip01Core.tags.addressables
import android.util.Log
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.utils.Hex
import com.vitorpamplona.quartz.utils.arrayOfNotNull
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
import com.vitorpamplona.quartz.utils.removeTrailingNullsAndEmptyOthers
@ -35,7 +39,7 @@ data class ATag(
constructor(
kind: Int,
pubKeyHex: String,
pubKeyHex: HexKey,
dTag: String,
relayHint: String?,
) : this(kind, pubKeyHex, dTag) {
@ -49,17 +53,56 @@ data class ATag(
dTag.bytesUsedInMemory() +
(relay?.bytesUsedInMemory() ?: 0)
fun toTag() = assembleATag(kind, pubKeyHex, dTag)
fun toTag() = assembleATagId(kind, pubKeyHex, dTag)
fun toATagArray() = removeTrailingNullsAndEmptyOthers("a", toTag(), relay)
fun toATagArray() = removeTrailingNullsAndEmptyOthers(TAG_NAME, toTag(), relay)
fun toQTagArray() = removeTrailingNullsAndEmptyOthers("q", toTag(), relay)
companion object {
fun assembleATag(
const val TAG_NAME = "a"
fun assembleATagId(
kind: Int,
pubKeyHex: HexKey,
dTag: String,
) = "$kind:$pubKeyHex:$dTag"
fun parse(
aTagId: String,
relay: String?,
): ATag? =
try {
val parts = aTagId.split(":", limit = 3)
if (Hex.isHex(parts[1])) {
ATag(parts[0].toInt(), parts[1], parts[2], relay)
} else {
Log.w("ATag", "Error parsing A Tag. Pubkey is not hex: $aTagId")
null
}
} catch (t: Throwable) {
Log.w("ATag", "Error parsing A Tag: $aTagId: ${t.message}")
null
}
@JvmStatic
fun parse(tags: Array<String>): ATag? {
require(tags[0] == TAG_NAME)
return parse(tags[1], tags.getOrNull(2))
}
@JvmStatic
fun assemble(
aTagId: HexKey,
relay: String?,
) = arrayOfNotNull(TAG_NAME, aTagId, relay)
@JvmStatic
fun assemble(
kind: Int,
pubKeyHex: String,
dTag: String,
) = "$kind:$pubKeyHex:$dTag"
relay: String?,
) = arrayOfNotNull(TAG_NAME, assembleATagId(kind, pubKeyHex, dTag), relay)
}
}

View File

@ -28,35 +28,33 @@ import com.vitorpamplona.quartz.nip01Core.core.mapTagged
import com.vitorpamplona.quartz.nip01Core.core.mapValueTagged
import com.vitorpamplona.quartz.nip19Bech32.parse
fun TagArray.dTag() = this.firstOrNull { it.size > 1 && it[0] == "d" }?.get(1) ?: ""
fun <R> TagArray.mapTaggedAddress(map: (address: String) -> R) = this.mapValueTagged("a", map)
fun <R> TagArray.mapTaggedAddress(map: (address: String) -> R) = this.mapValueTagged(ATag.TAG_NAME, map)
fun TagArray.firstIsTaggedAddressableNote(addressableNotes: Set<String>) =
this
.firstOrNull { it.size > 1 && it[0] == "a" && it[1] in addressableNotes }
.firstOrNull { it.size > 1 && it[0] == ATag.TAG_NAME && it[1] in addressableNotes }
?.getOrNull(1)
fun TagArray.isTaggedAddressableNote(idHex: String) = this.isTagged("a", idHex)
fun TagArray.isTaggedAddressableNote(idHex: String) = this.isTagged(ATag.TAG_NAME, idHex)
fun TagArray.isTaggedAddressableNotes(idHexes: Set<String>) = this.isAnyTagged("a", idHexes)
fun TagArray.isTaggedAddressableNotes(idHexes: Set<String>) = this.isAnyTagged(ATag.TAG_NAME, idHexes)
fun TagArray.isTaggedAddressableKind(kind: Int): Boolean {
val kindStr = kind.toString()
return this.any { it.size > 1 && it[0] == "a" && it[1].startsWith(kindStr) }
return this.any { it.size > 1 && it[0] == ATag.TAG_NAME && it[1].startsWith(kindStr) }
}
fun TagArray.getTagOfAddressableKind(kind: Int): ATag? {
val kindStr = kind.toString()
val aTag =
this
.firstOrNull { it.size > 1 && it[0] == "a" && it[1].startsWith(kindStr) }
.firstOrNull { it.size > 1 && it[0] == ATag.TAG_NAME && it[1].startsWith(kindStr) }
?.getOrNull(1)
?: return null
return ATag.parse(aTag, null)
}
fun TagArray.taggedAddresses() = this.mapTagged("a") { ATag.parse(it[1], it.getOrNull(2)) }
fun TagArray.taggedAddresses() = this.mapTagged(ATag.TAG_NAME) { ATag.parse(it) }
fun TagArray.firstTaggedAddress() = this.firstMapTagged("a") { ATag.parse(it[1], it.getOrNull(2)) }
fun TagArray.firstTaggedAddress() = this.firstMapTagged(ATag.TAG_NAME) { ATag.parse(it) }

View File

@ -0,0 +1,45 @@
/**
* Copyright (c) 2024 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nip01Core.tags.dTags
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
class DTag(
val dId: String,
) {
fun countMemory(): Long = 1 * pointerSizeInBytes + dId.bytesUsedInMemory()
fun toTagArray() = assemble(dId)
companion object {
const val TAG_NAME = "d"
@JvmStatic
fun parse(tags: Array<String>): DTag {
require(tags[0] == TAG_NAME)
return DTag(tags[1])
}
@JvmStatic
fun assemble(dId: String) = arrayOf(TAG_NAME, dId)
}
}

View File

@ -0,0 +1,25 @@
/**
* Copyright (c) 2024 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nip01Core.tags.dTags
import com.vitorpamplona.quartz.nip01Core.core.Event
fun Event.dTag() = tags.dTag()

View File

@ -0,0 +1,25 @@
/**
* Copyright (c) 2024 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nip01Core.tags.dTags
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
fun TagArrayBuilder.dTag(name: String) = add(DTag.assemble(name))

View File

@ -0,0 +1,26 @@
/**
* Copyright (c) 2024 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nip01Core.tags.dTags
import com.vitorpamplona.quartz.nip01Core.core.TagArray
import com.vitorpamplona.quartz.nip01Core.core.firstTagValue
fun TagArray.dTag() = this.firstTagValue(DTag.TAG_NAME) ?: ""

View File

@ -18,14 +18,14 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nip10Notes
package com.vitorpamplona.quartz.nip01Core.tags.events
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip19Bech32.entities.NEvent
import com.vitorpamplona.quartz.utils.arrayOfNotNull
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
import com.vitorpamplona.quartz.utils.removeTrailingNullsAndEmptyOthers
@Immutable
data class ETag(
@ -40,13 +40,31 @@ data class ETag(
}
fun countMemory(): Long =
2 * pointerSizeInBytes + // 2 fields, 4 bytes each reference (32bit)
3 * pointerSizeInBytes + // 3 fields, 4 bytes each reference (32bit)
eventId.bytesUsedInMemory() +
(relay?.bytesUsedInMemory() ?: 0)
(relay?.bytesUsedInMemory() ?: 0) +
(authorPubKeyHex?.bytesUsedInMemory() ?: 0)
fun toNEvent(): String = NEvent.create(eventId, authorPubKeyHex, null, relay)
fun toETagArray() = removeTrailingNullsAndEmptyOthers("e", eventId, relay, authorPubKeyHex)
fun toETagArray() = arrayOfNotNull(TAG_NAME, eventId, relay, authorPubKeyHex)
fun toQTagArray() = removeTrailingNullsAndEmptyOthers("q", eventId, relay, authorPubKeyHex)
fun toQTagArray() = arrayOfNotNull("q", eventId, relay, authorPubKeyHex)
companion object {
const val TAG_NAME = "e"
@JvmStatic
fun parse(tags: Array<String>): ETag {
require(tags[0] == TAG_NAME)
return ETag(tags[1], tags.getOrNull(2), tags.getOrNull(3))
}
@JvmStatic
fun assemble(
eventId: HexKey,
relay: String?,
author: HexKey?,
) = arrayOfNotNull(TAG_NAME, eventId, relay, author)
}
}

View File

@ -23,12 +23,14 @@ package com.vitorpamplona.quartz.nip01Core.tags.events
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.Event
fun Event.forEachTaggedEvent(onEach: (eventId: HexKey) -> Unit) = tags.forEachTaggedEvent(onEach)
fun Event.forEachTaggedEventId(onEach: (eventId: HexKey) -> Unit) = tags.forEachTaggedEventId(onEach)
fun <R> Event.mapTaggedEvent(map: (eventId: HexKey) -> R) = tags.mapTaggedEvent(map)
fun <R> Event.mapTaggedEventId(map: (eventId: HexKey) -> R) = tags.mapTaggedEventId(map)
fun Event.taggedEvents() = tags.taggedEvents()
fun Event.taggedEventIds() = tags.taggedEventIds()
fun Event.firstTaggedEvent() = tags.firstTaggedEvent()
fun Event.isTaggedEvent(idHex: String) = tags.isTaggedEvent(idHex)

View File

@ -22,18 +22,22 @@ package com.vitorpamplona.quartz.nip01Core.tags.events
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArray
import com.vitorpamplona.quartz.nip01Core.core.firstTagValue
import com.vitorpamplona.quartz.nip01Core.core.firstMapTagged
import com.vitorpamplona.quartz.nip01Core.core.forEachTagged
import com.vitorpamplona.quartz.nip01Core.core.isTagged
import com.vitorpamplona.quartz.nip01Core.core.mapTagged
import com.vitorpamplona.quartz.nip01Core.core.mapValueTagged
import com.vitorpamplona.quartz.nip01Core.core.mapValues
import com.vitorpamplona.quartz.nip19Bech32.parse
fun TagArray.forEachTaggedEvent(onEach: (eventId: HexKey) -> Unit) = this.forEachTagged("e", onEach)
fun TagArray.forEachTaggedEventId(onEach: (eventId: HexKey) -> Unit) = this.forEachTagged(ETag.TAG_NAME, onEach)
fun <R> TagArray.mapTaggedEvent(map: (eventId: HexKey) -> R) = this.mapValueTagged("e", map)
fun <R> TagArray.mapTaggedEventId(map: (eventId: HexKey) -> R) = this.mapValueTagged(ETag.TAG_NAME, map)
fun TagArray.taggedEvents() = this.mapValues("e")
fun TagArray.taggedEvents() = this.mapTagged(ETag.TAG_NAME) { ETag.parse(it) }
fun TagArray.firstTaggedEvent() = this.firstTagValue("e")
fun TagArray.taggedEventIds() = this.mapValues(ETag.TAG_NAME)
fun TagArray.isTaggedEvent(idHex: String) = this.isTagged("e", idHex)
fun TagArray.firstTaggedEvent() = this.firstMapTagged(ETag.TAG_NAME) { ETag.parse(it) }
fun TagArray.isTaggedEvent(idHex: String) = this.isTagged(ETag.TAG_NAME, idHex)

View File

@ -26,6 +26,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.taggedAddresses
import com.vitorpamplona.quartz.nip01Core.tags.events.taggedEventIds
import com.vitorpamplona.quartz.nip01Core.tags.events.taggedEvents
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
import com.vitorpamplona.quartz.utils.TimeUtils
@ -41,6 +42,8 @@ class DeletionEvent(
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
fun deleteEvents() = taggedEvents()
fun deleteEventIds() = taggedEventIds()
fun deleteAddresses() = taggedAddresses()
fun deleteAddressTags() = tags.mapNotNull { if (it.size > 1 && it[0] == "a") it[1] else null }

View File

@ -33,7 +33,7 @@ class PoWTag(
fun toTagArray() = assemble(nonce, commitment)
companion object {
val TAG_NAME = "nonce"
const val TAG_NAME = "nonce"
@JvmStatic
fun parse(tags: Array<String>): PoWTag {

View File

@ -20,6 +20,7 @@
*/
package com.vitorpamplona.quartz.nip14Subject
import com.vitorpamplona.quartz.nip01Core.tags.dTags.DTag
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
@ -31,12 +32,12 @@ class SubjectTag(
fun toTagArray() = assemble(subject)
companion object {
val TAG_NAME = "subject"
const val TAG_NAME = "subject"
@JvmStatic
fun parse(tags: Array<String>): SubjectTag {
fun parse(tags: Array<String>): DTag {
require(tags[0] == TAG_NAME)
return SubjectTag(tags[1])
return DTag(tags[1])
}
@JvmStatic

View File

@ -22,7 +22,7 @@ package com.vitorpamplona.quartz.nip17Dm
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
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
@ -37,9 +37,7 @@ class ChatMessageRelayListEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
override fun dTag() = FIXED_D_TAG
) : BaseReplaceableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
fun relays(): List<String> =
tags.mapNotNull {
if (it.size > 1 && it[0] == "relay") {
@ -51,11 +49,10 @@ class ChatMessageRelayListEvent(
companion object {
const val KIND = 10050
const val FIXED_D_TAG = ""
fun createAddressATag(pubKey: HexKey): ATag = ATag(KIND, pubKey, FIXED_D_TAG, null)
fun createAddressTag(pubKey: HexKey): String = ATag.assembleATag(KIND, pubKey, FIXED_D_TAG)
fun createAddressTag(pubKey: HexKey): String = ATag.assembleATagId(KIND, pubKey, FIXED_D_TAG)
fun createTagArray(relays: List<String>): Array<Array<String>> =
relays

View File

@ -40,7 +40,7 @@ data class NAddress(
val dTag: String,
val relay: List<String>,
) : Entity {
fun aTag(): String = ATag.assembleATag(kind, author, dTag)
fun aTag(): String = ATag.assembleATagId(kind, author, dTag)
companion object {
fun parse(naddr: String): NAddress? {

View File

@ -27,10 +27,10 @@ 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.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.geohash.geohashMipMap
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.buildHashtagTags
import com.vitorpamplona.quartz.nip10Notes.BaseTextNoteEvent
import com.vitorpamplona.quartz.nip10Notes.ETag
import com.vitorpamplona.quartz.nip10Notes.PTag
import com.vitorpamplona.quartz.nip10Notes.content.buildUrlRefs
import com.vitorpamplona.quartz.nip10Notes.content.findHashtags

View File

@ -25,7 +25,7 @@ import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.dTag
import com.vitorpamplona.quartz.nip01Core.tags.dTags.dTag
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags
import com.vitorpamplona.quartz.nip10Notes.BaseTextNoteEvent
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
@ -45,7 +45,7 @@ class LongTextNoteEvent(
override fun address(relayHint: String?) = ATag(kind, pubKey, dTag(), relayHint)
override fun addressTag() = ATag.assembleATag(kind, pubKey, dTag())
override fun addressTag() = ATag.assembleATagId(kind, pubKey, dTag())
fun topics() = hashtags()

View File

@ -45,8 +45,6 @@ class ChannelListEvent(
super.countMemory() +
32 + (publicAndPrivateEventCache?.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() } ?: 0L) // rough calculation
override fun dTag() = FIXED_D_TAG
fun publicAndPrivateEvents(
signer: NostrSigner,
onReady: (ImmutableSet<HexKey>) -> Unit,
@ -67,7 +65,6 @@ class ChannelListEvent(
companion object {
const val KIND = 10005
const val FIXED_D_TAG = ""
const val ALT = "Public Chat List"
fun blockListFor(pubKeyHex: HexKey): String = "$KIND:$pubKeyHex:"

View File

@ -22,11 +22,10 @@ package com.vitorpamplona.quartz.nip30CustomEmoji
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
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.nip31Alts.AltTagSerializer
import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
import com.vitorpamplona.quartz.utils.TimeUtils
@Immutable
@ -37,28 +36,14 @@ class EmojiPackSelectionEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
override fun dTag() = FIXED_D_TAG
) : BaseReplaceableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
companion object {
const val KIND = 10030
const val FIXED_D_TAG = ""
const val ALT = "Emoji selection"
fun createAddressATag(pubKey: HexKey): ATag =
ATag(
KIND,
pubKey,
AdvertisedRelayListEvent.FIXED_D_TAG,
null,
)
fun createAddressATag(pubKey: HexKey) = ATag(KIND, pubKey, FIXED_D_TAG, null)
fun createAddressTag(pubKey: HexKey): String =
ATag.assembleATag(
KIND,
pubKey,
AdvertisedRelayListEvent.FIXED_D_TAG,
)
fun createAddressTag(pubKey: HexKey) = ATag.assembleATagId(KIND, pubKey, FIXED_D_TAG)
fun create(
listOfEmojiPacks: List<ATag>?,

View File

@ -125,7 +125,7 @@ class DraftEvent(
fun createAddressTag(
pubKey: HexKey,
dTag: String,
): String = ATag.assembleATag(KIND, pubKey, dTag)
): String = ATag.assembleATagId(KIND, pubKey, dTag)
fun create(
dTag: String,

View File

@ -22,7 +22,7 @@ package com.vitorpamplona.quartz.nip50Search
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
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
@ -37,9 +37,7 @@ class SearchRelayListEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
override fun dTag() = FIXED_D_TAG
) : BaseReplaceableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
fun relays(): List<String> =
tags.mapNotNull {
if (it.size > 1 && it[0] == "relay") {
@ -51,11 +49,10 @@ class SearchRelayListEvent(
companion object {
const val KIND = 10007
const val FIXED_D_TAG = ""
fun createAddressATag(pubKey: HexKey): ATag = ATag(KIND, pubKey, FIXED_D_TAG, null)
fun createAddressTag(pubKey: HexKey): String = ATag.assembleATag(KIND, pubKey, FIXED_D_TAG)
fun createAddressTag(pubKey: HexKey): String = ATag.assembleATagId(KIND, pubKey, FIXED_D_TAG)
fun createTagArray(relays: List<String>): Array<Array<String>> =
relays

View File

@ -29,7 +29,6 @@ import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.events.taggedEvents
import com.vitorpamplona.quartz.nip01Core.tags.geohash.geohashes
import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUsers
import com.vitorpamplona.quartz.nip19Bech32.parse
import kotlinx.collections.immutable.ImmutableSet
import kotlinx.collections.immutable.toImmutableSet

View File

@ -22,6 +22,7 @@ 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.nip31Alts.AltTagSerializer
import com.vitorpamplona.quartz.utils.TimeUtils
@ -76,7 +77,6 @@ class MuteListEvent(
companion object {
const val KIND = 10000
const val FIXED_D_TAG = ""
const val ALT = "Mute List"
fun blockListFor(pubKeyHex: HexKey): String = "10000:$pubKeyHex:"

View File

@ -44,7 +44,7 @@ class WikiNoteEvent(
override fun address(relayHint: String?) = ATag(kind, pubKey, dTag(), relayHint)
override fun addressTag() = ATag.assembleATag(kind, pubKey, dTag())
override fun addressTag() = ATag.assembleATagId(kind, pubKey, dTag())
fun topics() = hashtags()

View File

@ -22,7 +22,7 @@ package com.vitorpamplona.quartz.nip65RelayList
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
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
@ -37,9 +37,7 @@ class AdvertisedRelayListEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
override fun dTag() = FIXED_D_TAG
) : BaseReplaceableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
fun relays(): List<AdvertisedRelayInfo> =
tags.mapNotNull {
if (it.size > 1 && it[0] == "r") {
@ -85,12 +83,11 @@ class AdvertisedRelayListEvent(
companion object {
const val KIND = 10002
const val FIXED_D_TAG = ""
const val ALT = "Relay list to discover the user's content"
fun createAddressATag(pubKey: HexKey): ATag = ATag(KIND, pubKey, FIXED_D_TAG, null)
fun createAddressTag(pubKey: HexKey): String = ATag.assembleATag(KIND, pubKey, FIXED_D_TAG)
fun createAddressTag(pubKey: HexKey): String = ATag.assembleATagId(KIND, pubKey, FIXED_D_TAG)
fun updateRelayList(
earlierVersion: AdvertisedRelayListEvent,

View File

@ -25,9 +25,9 @@ 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.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.geohash.geohashMipMap
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.buildHashtagTags
import com.vitorpamplona.quartz.nip10Notes.ETag
import com.vitorpamplona.quartz.nip10Notes.PTag
import com.vitorpamplona.quartz.nip10Notes.content.buildUrlRefs
import com.vitorpamplona.quartz.nip10Notes.content.findHashtags

View File

@ -22,6 +22,7 @@ package com.vitorpamplona.quartz.nip72ModCommunities
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.ATag
import com.vitorpamplona.quartz.nip19Bech32.parseAtag
@ -72,7 +73,6 @@ class CommunityListEvent(
companion object {
const val KIND = 10004
const val FIXED_D_TAG = ""
const val ALT = "Community List"
fun blockListFor(pubKeyHex: HexKey): String = "$KIND:$pubKeyHex:"

View File

@ -25,7 +25,6 @@ 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.nip19Bech32.parse
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
import com.vitorpamplona.quartz.utils.TimeUtils

View File

@ -22,7 +22,7 @@ package com.vitorpamplona.quartz.nip96FileStorage
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
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.nip31Alts.AltTagSerializer
@ -36,9 +36,7 @@ class FileServersEvent(
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
override fun dTag() = FIXED_D_TAG
) : BaseReplaceableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
fun servers(): List<String> =
tags.mapNotNull {
if (it.size > 1 && it[0] == "server") {
@ -50,12 +48,11 @@ class FileServersEvent(
companion object {
const val KIND = 10096
const val FIXED_D_TAG = ""
const val ALT = "File servers used by the author"
fun createAddressATag(pubKey: HexKey): ATag = ATag(KIND, pubKey, FIXED_D_TAG, null)
fun createAddressTag(pubKey: HexKey): String = ATag.assembleATag(KIND, pubKey, FIXED_D_TAG)
fun createAddressTag(pubKey: HexKey): String = ATag.assembleATagId(KIND, pubKey, FIXED_D_TAG)
fun createTagArray(servers: List<String>): Array<Array<String>> =
servers