- 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
@@ -74,17 +74,18 @@ import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.taggedAddresses 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.geohash.geohashes
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags
import com.vitorpamplona.quartz.nip01Core.tags.people.hasAnyTaggedUser import com.vitorpamplona.quartz.nip01Core.tags.people.hasAnyTaggedUser
import com.vitorpamplona.quartz.nip01Core.tags.people.isTaggedUser 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.Contact
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
import com.vitorpamplona.quartz.nip03Timestamp.OtsEvent import com.vitorpamplona.quartz.nip03Timestamp.OtsEvent
import com.vitorpamplona.quartz.nip04Dm.PrivateDmEvent import com.vitorpamplona.quartz.nip04Dm.PrivateDmEvent
import com.vitorpamplona.quartz.nip09Deletions.DeletionEvent import com.vitorpamplona.quartz.nip09Deletions.DeletionEvent
import com.vitorpamplona.quartz.nip10Notes.ETag
import com.vitorpamplona.quartz.nip10Notes.PTag import com.vitorpamplona.quartz.nip10Notes.PTag
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
import com.vitorpamplona.quartz.nip17Dm.ChatMessageRelayListEvent import com.vitorpamplona.quartz.nip17Dm.ChatMessageRelayListEvent
@@ -954,7 +955,7 @@ class Account(
onReady: (LiveFollowList) -> Unit, onReady: (LiveFollowList) -> Unit,
) { ) {
listEvent.privateTags(signer) { privateTagList -> listEvent.privateTags(signer) { privateTagList ->
val users = (listEvent.bookmarkedPeople() + listEvent.filterUsers(privateTagList)).toSet() val users = (listEvent.taggedUsers() + listEvent.filterUsers(privateTagList)).toSet()
onReady( onReady(
LiveFollowList( LiveFollowList(
authors = users, 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? = suspend fun waitToDecrypt(peopleListFollows: GeneralListEvent): LiveFollowList? =
tryAndWait { continuation -> tryAndWait { continuation ->
decryptLiveFollows(peopleListFollows) { decryptLiveFollows(peopleListFollows) {
@@ -2445,7 +2451,7 @@ class Account(
directMentionsNotes directMentionsNotes
.mapNotNullTo(HashSet(directMentionsNotes.size)) { note -> .mapNotNullTo(HashSet(directMentionsNotes.size)) { note ->
if (note !is AddressableNote) { if (note !is AddressableNote) {
ETag(note.idHex, note.author?.pubkeyHex, note.relayHintUrl()) ETag(note.idHex, note.relayHintUrl(), note.author?.pubkeyHex)
} else { } else {
null null
} }
@@ -2556,7 +2562,7 @@ class Account(
directMentionsNotes directMentionsNotes
.mapNotNullTo(HashSet(directMentionsNotes.size)) { note -> .mapNotNullTo(HashSet(directMentionsNotes.size)) { note ->
if (note !is AddressableNote) { if (note !is AddressableNote) {
ETag(note.idHex, note.author?.pubkeyHex, note.relayHintUrl()) ETag(note.idHex, note.relayHintUrl(), note.author?.pubkeyHex)
} else { } else {
null null
} }
@@ -3520,7 +3526,7 @@ class Account(
if (note is AddressableNote) { if (note is AddressableNote) {
return userProfile().latestBookmarkList?.taggedAddresses()?.contains(note.address) == true return userProfile().latestBookmarkList?.taggedAddresses()?.contains(note.address) == true
} else { } 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> { fun selectedChatsFollowList(): Set<String> {
val contactList = userProfile().latestContactList val contactList = userProfile().latestContactList
return contactList?.taggedEvents()?.toSet() ?: DefaultChannels return contactList?.taggedEventIds()?.toSet() ?: DefaultChannels
} }
fun sendChangeChannel( fun sendChangeChannel(
@@ -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.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.mapTaggedAddress import com.vitorpamplona.quartz.nip01Core.tags.addressables.mapTaggedAddress
import com.vitorpamplona.quartz.nip01Core.tags.addressables.taggedAddresses 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.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.events.taggedEvents
import com.vitorpamplona.quartz.nip01Core.tags.people.isTaggedUsers import com.vitorpamplona.quartz.nip01Core.tags.people.isTaggedUsers
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent 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.decodeEventIdAsHexOrNull
import com.vitorpamplona.quartz.nip19Bech32.decodePublicKeyAsHexOrNull import com.vitorpamplona.quartz.nip19Bech32.decodePublicKeyAsHexOrNull
import com.vitorpamplona.quartz.nip19Bech32.isATag import com.vitorpamplona.quartz.nip19Bech32.isATag
import com.vitorpamplona.quartz.nip19Bech32.parse
import com.vitorpamplona.quartz.nip22Comments.CommentEvent import com.vitorpamplona.quartz.nip22Comments.CommentEvent
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent
import com.vitorpamplona.quartz.nip25Reactions.ReactionEvent import com.vitorpamplona.quartz.nip25Reactions.ReactionEvent
@@ -239,7 +239,7 @@ object LocalCache {
private fun updateObservables(event: Event) { private fun updateObservables(event: Event) {
observablesByKindAndETag[event.kind]?.let { observablesOfKind -> observablesByKindAndETag[event.kind]?.let { observablesOfKind ->
event.forEachTaggedEvent { event.forEachTaggedEventId {
observablesOfKind[it]?.updateIfMatches(event) observablesOfKind[it]?.updateIfMatches(event)
} }
} }
@@ -274,6 +274,8 @@ object LocalCache {
fun getNoteIfExists(key: String): Note? = addressables.get(key) ?: notes.get(key) 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 getChannelIfExists(key: String): Channel? = channels.get(key)
fun getNoteIfExists(event: Event): Note? = fun getNoteIfExists(event: Event): Note? =
@@ -290,6 +292,15 @@ object LocalCache {
getOrCreateNote(event.id) getOrCreateNote(event.id)
} }
fun checkGetOrCreateNote(etag: ETag): Note? {
checkNotInMainThread()
if (isValidHex(etag.eventId)) {
return getOrCreateNote(etag)
}
return null
}
fun checkGetOrCreateNote(key: String): Note? { fun checkGetOrCreateNote(key: String): Note? {
checkNotInMainThread() checkNotInMainThread()
@@ -401,6 +412,16 @@ object LocalCache {
return note 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( fun consume(
event: MetadataEvent, event: MetadataEvent,
relay: Relay?, relay: Relay?,
@@ -681,7 +702,7 @@ object LocalCache {
event.tagsWithoutCitations().mapNotNull { checkGetOrCreateNote(it) } event.tagsWithoutCitations().mapNotNull { checkGetOrCreateNote(it) }
is DraftEvent -> { is DraftEvent -> {
event.mapTaggedEvent { checkGetOrCreateNote(it) } + event.mapTaggedAddress { checkGetOrCreateAddressableNote(it) } event.mapTaggedEventId { checkGetOrCreateNote(it) } + event.mapTaggedAddress { checkGetOrCreateAddressableNote(it) }
} }
else -> emptyList<Note>() else -> emptyList<Note>()
@@ -1562,7 +1583,7 @@ object LocalCache {
note.loadEvent(event, author, emptyList()) note.loadEvent(event, author, emptyList())
event.editedNote()?.let { event.editedNote()?.let {
checkGetOrCreateNote(it)?.let { editedNote -> checkGetOrCreateNote(it.eventId)?.let { editedNote ->
modificationCache.remove(editedNote.idHex) modificationCache.remove(editedNote.idHex)
// must update list of Notes to quickly update the user. // must update list of Notes to quickly update the user.
editedNote.liveSet?.innerModifications?.invalidateData() editedNote.liveSet?.innerModifications?.invalidateData()
@@ -24,7 +24,7 @@ import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.ui.actions.relays.updated 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.ChatroomKey
import com.vitorpamplona.quartz.nip17Dm.ChatroomKeyable import com.vitorpamplona.quartz.nip17Dm.ChatroomKeyable
import com.vitorpamplona.quartz.nip28PublicChat.ChannelMessageEvent import com.vitorpamplona.quartz.nip28PublicChat.ChannelMessageEvent
@@ -141,7 +141,7 @@ class ChatroomListKnownFeedFilter(
account account
.userProfile() .userProfile()
.latestContactList .latestContactList
?.taggedEvents() ?.taggedEventIds()
?.toSet() ?: emptySet() ?.toSet() ?: emptySet()
val newRelevantPublicMessages = mutableMapOf<String, Note>() val newRelevantPublicMessages = mutableMapOf<String, Note>()
newItems newItems
@@ -120,7 +120,7 @@ class FeedContentState(
if (deletionEvents.isEmpty()) { if (deletionEvents.isEmpty()) {
oldNotesState.feed.value.list oldNotesState.feed.value.list
} else { } else {
val deletedEventIds = deletionEvents.flatMapTo(HashSet()) { it.deleteEvents() } val deletedEventIds = deletionEvents.flatMapTo(HashSet()) { it.deleteEventIds() }
val deletedEventAddresses = deletionEvents.flatMapTo(HashSet()) { it.deleteAddresses() } val deletedEventAddresses = deletionEvents.flatMapTo(HashSet()) { it.deleteAddresses() }
oldNotesState.feed.value.list oldNotesState.feed.value.list
.filter { !it.wasOrShouldBeDeletedBy(deletedEventIds, deletedEventAddresses) } .filter { !it.wasOrShouldBeDeletedBy(deletedEventIds, deletedEventAddresses) }
@@ -75,6 +75,7 @@ import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.quartz.nip01Core.UserMetadata import com.vitorpamplona.quartz.nip01Core.UserMetadata
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.firstTaggedAddress 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.nip01Core.tags.events.firstTaggedEvent
import com.vitorpamplona.quartz.nip38UserStatus.StatusEvent import com.vitorpamplona.quartz.nip38UserStatus.StatusEvent
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
@@ -264,7 +265,7 @@ fun DisplayStatus(
event.dTag(), event.dTag(),
event.firstTaggedUrl()?.ifBlank { null }, event.firstTaggedUrl()?.ifBlank { null },
event.firstTaggedAddress(), event.firstTaggedAddress(),
event.firstTaggedEvent()?.ifBlank { null }, event.firstTaggedEvent(),
accountViewModel, accountViewModel,
nav, nav,
) )
@@ -276,7 +277,7 @@ fun DisplayStatusInner(
type: String, type: String,
url: String?, url: String?,
nostrATag: ATag?, nostrATag: ATag?,
nostrHexID: String?, nostrETag: ETag?,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
@@ -335,8 +336,8 @@ fun DisplayStatusInner(
} }
} }
} }
} else if (nostrHexID != null) { } else if (nostrETag != null) {
LoadNote(baseNoteHex = nostrHexID, accountViewModel) { LoadNote(baseNoteHex = nostrETag.eventId, accountViewModel) {
if (it != null) { if (it != null) {
Spacer(modifier = StdHorzSpacer) Spacer(modifier = StdHorzSpacer)
IconButton( IconButton(
@@ -53,9 +53,9 @@ import com.vitorpamplona.amethyst.ui.components.measureSpaceWidth
import com.vitorpamplona.amethyst.ui.navigation.INav import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.navigation.routeFor import com.vitorpamplona.amethyst.ui.navigation.routeFor
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel 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.core.firstTagValueFor
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag 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.nip02FollowList.EmptyTagList
import com.vitorpamplona.quartz.nip10Notes.BaseTextNoteEvent import com.vitorpamplona.quartz.nip10Notes.BaseTextNoteEvent
import com.vitorpamplona.quartz.nip19Bech32.toNIP19 import com.vitorpamplona.quartz.nip19Bech32.toNIP19
@@ -100,7 +100,7 @@ fun DisplayHighlight(
authorHex: String?, authorHex: String?,
url: String?, url: String?,
postAddress: ATag?, postAddress: ATag?,
postVersion: HexKey?, postVersion: ETag?,
makeItShort: Boolean, makeItShort: Boolean,
canPreview: Boolean, canPreview: Boolean,
quotesLeft: Int, quotesLeft: Int,
@@ -150,7 +150,7 @@ private fun DisplayQuoteAuthor(
authorHex: String?, authorHex: String?,
baseUrl: String?, baseUrl: String?,
postAddress: ATag?, postAddress: ATag?,
postVersion: HexKey?, postVersion: ETag?,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
@@ -181,14 +181,14 @@ private fun DisplayQuoteAuthor(
} }
var version by remember { var version by remember {
mutableStateOf<Note?>(postVersion?.let { accountViewModel.getNoteIfExists(it) }) mutableStateOf<Note?>(postVersion?.let { accountViewModel.getNoteIfExists(it.eventId) })
} }
if (version == null && postVersion != null) { if (version == null && postVersion != null) {
LaunchedEffect(key1 = postVersion) { LaunchedEffect(key1 = postVersion) {
val newNote = val newNote =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
accountViewModel.getOrCreateNote(postVersion) accountViewModel.getOrCreateNote(postVersion.eventId)
} }
if (version != newNote) { if (version != newNote) {
version = newNote version = newNote
@@ -84,13 +84,13 @@ fun RenderTextModificationEvent(
val isAuthorTheLoggedUser = val isAuthorTheLoggedUser =
remember { remember {
val authorOfTheOriginalNote = val authorOfTheOriginalNote =
noteEvent.editedNote()?.let { accountViewModel.getNoteIfExists(it)?.author } noteEvent.editedNote()?.let { accountViewModel.getNoteIfExists(it.eventId)?.author?.pubkeyHex ?: it.authorPubKeyHex }
mutableStateOf(accountViewModel.isLoggedUser(authorOfTheOriginalNote)) mutableStateOf(accountViewModel.isLoggedUser(authorOfTheOriginalNote))
} }
noteEvent.editedNote()?.let { noteEvent.editedNote()?.let {
LoadNote(baseNoteHex = it, accountViewModel = accountViewModel) { baseOriginalNote -> LoadNote(baseNoteHex = it.eventId, accountViewModel = accountViewModel) { baseOriginalNote ->
baseOriginalNote?.let { baseOriginalNote?.let {
} }
} }
@@ -128,7 +128,7 @@ fun RenderTextModificationEvent(
} }
noteEvent.editedNote()?.let { noteEvent.editedNote()?.let {
LoadNote(baseNoteHex = it, accountViewModel = accountViewModel) { baseNote -> LoadNote(baseNoteHex = it.eventId, accountViewModel = accountViewModel) { baseNote ->
baseNote?.let { baseNote?.let {
val noteState by baseNote.live().metadata.observeAsState() val noteState by baseNote.live().metadata.observeAsState()
@@ -140,7 +140,7 @@ class FollowListState(
( (
noteEvent is DeletionEvent && noteEvent is DeletionEvent &&
( (
noteEvent.deleteEvents().any { noteEvent.deleteEventIds().any {
LocalCache.getNoteIfExists(it)?.event is PeopleListEvent LocalCache.getNoteIfExists(it)?.event is PeopleListEvent
} || } ||
noteEvent.deleteAddresses().any { noteEvent.deleteAddresses().any {
@@ -82,6 +82,7 @@ import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.people.isTaggedUser 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.nip11RelayInfo.Nip11RelayInformation
import com.vitorpamplona.quartz.nip17Dm.ChatMessageRelayListEvent import com.vitorpamplona.quartz.nip17Dm.ChatMessageRelayListEvent
import com.vitorpamplona.quartz.nip17Dm.ChatroomKey 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.nip37Drafts.DraftEvent
import com.vitorpamplona.quartz.nip47WalletConnect.Response import com.vitorpamplona.quartz.nip47WalletConnect.Response
import com.vitorpamplona.quartz.nip50Search.SearchRelayListEvent import com.vitorpamplona.quartz.nip50Search.SearchRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.GeneralListEvent
import com.vitorpamplona.quartz.nip56Reports.ReportEvent import com.vitorpamplona.quartz.nip56Reports.ReportEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent
@@ -1071,7 +1073,7 @@ class AccountViewModel(
viewModelScope.launch(Dispatchers.IO) { runOnIO() } 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) override suspend fun getOrCreateUser(key: HexKey): User = LocalCache.getOrCreateUser(key)
@@ -1201,7 +1203,7 @@ class AccountViewModel(
hexList: List<String>, hexList: List<String>,
onReady: (ImmutableList<User>) -> Unit, onReady: (ImmutableList<User>) -> Unit,
) { ) {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.Default) {
onReady( onReady(
hexList hexList
.mapNotNull { hex -> checkGetOrCreateUser(hex) } .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( fun checkVideoIsOnline(
videoUrl: String, videoUrl: String,
onDone: (Boolean) -> Unit, onDone: (Boolean) -> Unit,
@@ -177,6 +177,7 @@ import com.vitorpamplona.amethyst.ui.theme.ZeroPadding
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.amethyst.ui.theme.userProfileBorderModifier import com.vitorpamplona.amethyst.ui.theme.userProfileBorderModifier
import com.vitorpamplona.quartz.nip01Core.tags.addressables.taggedAddresses 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.nip01Core.tags.events.taggedEvents
import com.vitorpamplona.quartz.nip02FollowList.EmptyTagList import com.vitorpamplona.quartz.nip02FollowList.EmptyTagList
import com.vitorpamplona.quartz.nip39ExtIdentities.GitHubIdentity import com.vitorpamplona.quartz.nip39ExtIdentities.GitHubIdentity
@@ -1383,7 +1384,7 @@ private fun WatchAndRenderBadgeList(
@Composable @Composable
@OptIn(ExperimentalLayoutApi::class) @OptIn(ExperimentalLayoutApi::class)
private fun RenderBadgeList( private fun RenderBadgeList(
list: ImmutableList<String>, list: ImmutableList<ETag>,
loadProfilePicture: Boolean, loadProfilePicture: Boolean,
loadRobohash: Boolean, loadRobohash: Boolean,
nav: INav, nav: INav,
@@ -1398,17 +1399,17 @@ private fun RenderBadgeList(
@Composable @Composable
private fun LoadAndRenderBadge( private fun LoadAndRenderBadge(
badgeAwardEventHex: String, badgeAwardEvent: ETag,
loadProfilePicture: Boolean, loadProfilePicture: Boolean,
loadRobohash: Boolean, loadRobohash: Boolean,
nav: INav, 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) { if (baseNote == null) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
baseNote = LocalCache.checkGetOrCreateNote(badgeAwardEventHex) baseNote = LocalCache.checkGetOrCreateNote(badgeAwardEvent)
} }
} }
} }
@@ -22,7 +22,7 @@ package com.vitorpamplona.quartz.blossom
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey 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.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
@@ -36,9 +36,7 @@ class BlossomServersEvent(
tags: Array<Array<String>>, tags: Array<Array<String>>,
content: String, content: String,
sig: HexKey, sig: HexKey,
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { ) : BaseReplaceableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
override fun dTag() = FIXED_D_TAG
fun servers(): List<String> = fun servers(): List<String> =
tags.mapNotNull { tags.mapNotNull {
if (it.size > 1 && it[0] == "server") { if (it.size > 1 && it[0] == "server") {
@@ -50,12 +48,11 @@ class BlossomServersEvent(
companion object { companion object {
const val KIND = 10063 const val KIND = 10063
const val FIXED_D_TAG = ""
const val ALT = "File servers used by the author" const val ALT = "File servers used by the author"
fun createAddressATag(pubKey: HexKey): ATag = ATag(KIND, pubKey, FIXED_D_TAG, null) 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>> = fun createTagArray(servers: List<String>): Array<Array<String>> =
servers servers
@@ -24,7 +24,7 @@ import android.util.Log
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import com.fasterxml.jackson.module.kotlin.readValue import com.fasterxml.jackson.module.kotlin.readValue
import com.vitorpamplona.quartz.nip01Core.HexKey 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.core.TagArray
import com.vitorpamplona.quartz.nip01Core.jackson.EventMapper import com.vitorpamplona.quartz.nip01Core.jackson.EventMapper
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
@@ -43,15 +43,13 @@ class PrivateOutboxRelayListEvent(
tags: Array<Array<String>>, tags: Array<Array<String>>,
content: String, content: String,
sig: HexKey, 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 @Transient private var privateTagsCache: Array<Array<String>>? = null
override fun countMemory(): Long = override fun countMemory(): Long =
super.countMemory() + super.countMemory() +
pointerSizeInBytes + (privateTagsCache?.sumOf { pointerSizeInBytes + it.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() } } ?: 0) pointerSizeInBytes + (privateTagsCache?.sumOf { pointerSizeInBytes + it.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() } } ?: 0)
override fun dTag() = FIXED_D_TAG
fun relays(): List<String>? = fun relays(): List<String>? =
tags tags
.mapNotNull { .mapNotNull {
@@ -102,12 +100,11 @@ class PrivateOutboxRelayListEvent(
companion object { companion object {
const val KIND = 10013 const val KIND = 10013
const val FIXED_D_TAG = ""
val TAGS = arrayOf(AltTagSerializer.toTagArray("Relay list to store private content from this author")) 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 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( fun encryptTags(
privateTags: Array<Array<String>>? = null, privateTags: Array<Array<String>>? = null,
@@ -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.buildUrlRefs
import com.vitorpamplona.quartz.nip10Notes.content.findHashtags import com.vitorpamplona.quartz.nip10Notes.content.findHashtags
import com.vitorpamplona.quartz.nip10Notes.content.findURLs import com.vitorpamplona.quartz.nip10Notes.content.findURLs
import com.vitorpamplona.quartz.nip19Bech32.parse
import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrl import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrl
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
import com.vitorpamplona.quartz.nip36SensitiveContent.ContentWarningSerializer import com.vitorpamplona.quartz.nip36SensitiveContent.ContentWarningSerializer
@@ -49,7 +49,7 @@ class InteractiveStoryPrologueEvent(
fun createAddressTag( fun createAddressTag(
pubKey: HexKey, pubKey: HexKey,
dtag: String, dtag: String,
): String = ATag.assembleATag(KIND, pubKey, dtag) ): String = ATag.assembleATagId(KIND, pubKey, dtag)
fun create( fun create(
baseId: String, baseId: String,
@@ -27,7 +27,6 @@ import com.vitorpamplona.quartz.nip01Core.core.firstTag
import com.vitorpamplona.quartz.nip01Core.core.firstTagValue import com.vitorpamplona.quartz.nip01Core.core.firstTagValue
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip19Bech32.parse
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.utils.removeTrailingNullsAndEmptyOthers import com.vitorpamplona.quartz.utils.removeTrailingNullsAndEmptyOthers
@@ -66,7 +65,7 @@ class InteractiveStoryReadingStateEvent(
fun createAddressTag( fun createAddressTag(
pubKey: HexKey, pubKey: HexKey,
dtag: String, dtag: String,
): String = ATag.assembleATag(KIND, pubKey, dtag) ): String = ATag.assembleATagId(KIND, pubKey, dtag)
fun update( fun update(
base: InteractiveStoryReadingStateEvent, base: InteractiveStoryReadingStateEvent,
@@ -49,7 +49,7 @@ class InteractiveStorySceneEvent(
fun createAddressTag( fun createAddressTag(
pubKey: HexKey, pubKey: HexKey,
dtag: String, dtag: String,
): String = ATag.assembleATag(KIND, pubKey, dtag) ): String = ATag.assembleATagId(KIND, pubKey, dtag)
fun create( fun create(
baseId: String, baseId: String,
@@ -23,7 +23,7 @@ package com.vitorpamplona.quartz.nip01Core.core
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag 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 @Immutable
open class BaseAddressableEvent( open class BaseAddressableEvent(
@@ -43,5 +43,5 @@ open class BaseAddressableEvent(
/** /**
* Creates the tag in a memory efficient way (without creating the ATag class * 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())
} }
@@ -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 = ""
}
}
@@ -20,7 +20,11 @@
*/ */
package com.vitorpamplona.quartz.nip01Core.tags.addressables package com.vitorpamplona.quartz.nip01Core.tags.addressables
import android.util.Log
import androidx.compose.runtime.Immutable 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.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.pointerSizeInBytes import com.vitorpamplona.quartz.utils.pointerSizeInBytes
import com.vitorpamplona.quartz.utils.removeTrailingNullsAndEmptyOthers import com.vitorpamplona.quartz.utils.removeTrailingNullsAndEmptyOthers
@@ -35,7 +39,7 @@ data class ATag(
constructor( constructor(
kind: Int, kind: Int,
pubKeyHex: String, pubKeyHex: HexKey,
dTag: String, dTag: String,
relayHint: String?, relayHint: String?,
) : this(kind, pubKeyHex, dTag) { ) : this(kind, pubKeyHex, dTag) {
@@ -49,17 +53,56 @@ data class ATag(
dTag.bytesUsedInMemory() + dTag.bytesUsedInMemory() +
(relay?.bytesUsedInMemory() ?: 0) (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) fun toQTagArray() = removeTrailingNullsAndEmptyOthers("q", toTag(), relay)
companion object { 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, kind: Int,
pubKeyHex: String, pubKeyHex: String,
dTag: String, dTag: String,
) = "$kind:$pubKeyHex:$dTag" relay: String?,
) = arrayOfNotNull(TAG_NAME, assembleATagId(kind, pubKeyHex, dTag), relay)
} }
} }
@@ -28,35 +28,33 @@ import com.vitorpamplona.quartz.nip01Core.core.mapTagged
import com.vitorpamplona.quartz.nip01Core.core.mapValueTagged import com.vitorpamplona.quartz.nip01Core.core.mapValueTagged
import com.vitorpamplona.quartz.nip19Bech32.parse 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(ATag.TAG_NAME, map)
fun <R> TagArray.mapTaggedAddress(map: (address: String) -> R) = this.mapValueTagged("a", map)
fun TagArray.firstIsTaggedAddressableNote(addressableNotes: Set<String>) = fun TagArray.firstIsTaggedAddressableNote(addressableNotes: Set<String>) =
this 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) ?.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 { fun TagArray.isTaggedAddressableKind(kind: Int): Boolean {
val kindStr = kind.toString() 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? { fun TagArray.getTagOfAddressableKind(kind: Int): ATag? {
val kindStr = kind.toString() val kindStr = kind.toString()
val aTag = val aTag =
this 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) ?.getOrNull(1)
?: return null ?: return null
return ATag.parse(aTag, 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) }
@@ -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)
}
}
@@ -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()
@@ -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))
@@ -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) ?: ""
@@ -18,14 +18,14 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * 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. * 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 androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip19Bech32.entities.NEvent import com.vitorpamplona.quartz.nip19Bech32.entities.NEvent
import com.vitorpamplona.quartz.utils.arrayOfNotNull
import com.vitorpamplona.quartz.utils.bytesUsedInMemory import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.pointerSizeInBytes import com.vitorpamplona.quartz.utils.pointerSizeInBytes
import com.vitorpamplona.quartz.utils.removeTrailingNullsAndEmptyOthers
@Immutable @Immutable
data class ETag( data class ETag(
@@ -40,13 +40,31 @@ data class ETag(
} }
fun countMemory(): Long = fun countMemory(): Long =
2 * pointerSizeInBytes + // 2 fields, 4 bytes each reference (32bit) 3 * pointerSizeInBytes + // 3 fields, 4 bytes each reference (32bit)
eventId.bytesUsedInMemory() + eventId.bytesUsedInMemory() +
(relay?.bytesUsedInMemory() ?: 0) (relay?.bytesUsedInMemory() ?: 0) +
(authorPubKeyHex?.bytesUsedInMemory() ?: 0)
fun toNEvent(): String = NEvent.create(eventId, authorPubKeyHex, null, relay) 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)
}
} }
@@ -23,12 +23,14 @@ package com.vitorpamplona.quartz.nip01Core.tags.events
import com.vitorpamplona.quartz.nip01Core.HexKey import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.Event 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.taggedEvents() = tags.taggedEvents()
fun Event.taggedEventIds() = tags.taggedEventIds()
fun Event.firstTaggedEvent() = tags.firstTaggedEvent() fun Event.firstTaggedEvent() = tags.firstTaggedEvent()
fun Event.isTaggedEvent(idHex: String) = tags.isTaggedEvent(idHex) fun Event.isTaggedEvent(idHex: String) = tags.isTaggedEvent(idHex)
@@ -22,18 +22,22 @@ package com.vitorpamplona.quartz.nip01Core.tags.events
import com.vitorpamplona.quartz.nip01Core.HexKey import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArray 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.forEachTagged
import com.vitorpamplona.quartz.nip01Core.core.isTagged 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.mapValueTagged
import com.vitorpamplona.quartz.nip01Core.core.mapValues 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)
@@ -26,6 +26,7 @@ import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.taggedAddresses 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.nip01Core.tags.events.taggedEvents
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
@@ -41,6 +42,8 @@ class DeletionEvent(
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { ) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
fun deleteEvents() = taggedEvents() fun deleteEvents() = taggedEvents()
fun deleteEventIds() = taggedEventIds()
fun deleteAddresses() = taggedAddresses() fun deleteAddresses() = taggedAddresses()
fun deleteAddressTags() = tags.mapNotNull { if (it.size > 1 && it[0] == "a") it[1] else null } fun deleteAddressTags() = tags.mapNotNull { if (it.size > 1 && it[0] == "a") it[1] else null }
@@ -33,7 +33,7 @@ class PoWTag(
fun toTagArray() = assemble(nonce, commitment) fun toTagArray() = assemble(nonce, commitment)
companion object { companion object {
val TAG_NAME = "nonce" const val TAG_NAME = "nonce"
@JvmStatic @JvmStatic
fun parse(tags: Array<String>): PoWTag { fun parse(tags: Array<String>): PoWTag {
@@ -20,6 +20,7 @@
*/ */
package com.vitorpamplona.quartz.nip14Subject package com.vitorpamplona.quartz.nip14Subject
import com.vitorpamplona.quartz.nip01Core.tags.dTags.DTag
import com.vitorpamplona.quartz.utils.bytesUsedInMemory import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.pointerSizeInBytes import com.vitorpamplona.quartz.utils.pointerSizeInBytes
@@ -31,12 +32,12 @@ class SubjectTag(
fun toTagArray() = assemble(subject) fun toTagArray() = assemble(subject)
companion object { companion object {
val TAG_NAME = "subject" const val TAG_NAME = "subject"
@JvmStatic @JvmStatic
fun parse(tags: Array<String>): SubjectTag { fun parse(tags: Array<String>): DTag {
require(tags[0] == TAG_NAME) require(tags[0] == TAG_NAME)
return SubjectTag(tags[1]) return DTag(tags[1])
} }
@JvmStatic @JvmStatic
@@ -22,7 +22,7 @@ package com.vitorpamplona.quartz.nip17Dm
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey 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.NostrSigner
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
@@ -37,9 +37,7 @@ class ChatMessageRelayListEvent(
tags: Array<Array<String>>, tags: Array<Array<String>>,
content: String, content: String,
sig: HexKey, sig: HexKey,
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { ) : BaseReplaceableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
override fun dTag() = FIXED_D_TAG
fun relays(): List<String> = fun relays(): List<String> =
tags.mapNotNull { tags.mapNotNull {
if (it.size > 1 && it[0] == "relay") { if (it.size > 1 && it[0] == "relay") {
@@ -51,11 +49,10 @@ class ChatMessageRelayListEvent(
companion object { companion object {
const val KIND = 10050 const val KIND = 10050
const val FIXED_D_TAG = ""
fun createAddressATag(pubKey: HexKey): ATag = ATag(KIND, pubKey, FIXED_D_TAG, null) 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>> = fun createTagArray(relays: List<String>): Array<Array<String>> =
relays relays
@@ -40,7 +40,7 @@ data class NAddress(
val dTag: String, val dTag: String,
val relay: List<String>, val relay: List<String>,
) : Entity { ) : Entity {
fun aTag(): String = ATag.assembleATag(kind, author, dTag) fun aTag(): String = ATag.assembleATagId(kind, author, dTag)
companion object { companion object {
fun parse(naddr: String): NAddress? { fun parse(naddr: String): NAddress? {
@@ -27,10 +27,10 @@ import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag 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.geohash.geohashMipMap
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.buildHashtagTags import com.vitorpamplona.quartz.nip01Core.tags.hashtags.buildHashtagTags
import com.vitorpamplona.quartz.nip10Notes.BaseTextNoteEvent import com.vitorpamplona.quartz.nip10Notes.BaseTextNoteEvent
import com.vitorpamplona.quartz.nip10Notes.ETag
import com.vitorpamplona.quartz.nip10Notes.PTag import com.vitorpamplona.quartz.nip10Notes.PTag
import com.vitorpamplona.quartz.nip10Notes.content.buildUrlRefs import com.vitorpamplona.quartz.nip10Notes.content.buildUrlRefs
import com.vitorpamplona.quartz.nip10Notes.content.findHashtags import com.vitorpamplona.quartz.nip10Notes.content.findHashtags
@@ -25,7 +25,7 @@ import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag 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.nip01Core.tags.hashtags.hashtags
import com.vitorpamplona.quartz.nip10Notes.BaseTextNoteEvent import com.vitorpamplona.quartz.nip10Notes.BaseTextNoteEvent
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
@@ -45,7 +45,7 @@ class LongTextNoteEvent(
override fun address(relayHint: String?) = ATag(kind, pubKey, dTag(), relayHint) 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() fun topics() = hashtags()
@@ -45,8 +45,6 @@ class ChannelListEvent(
super.countMemory() + super.countMemory() +
32 + (publicAndPrivateEventCache?.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() } ?: 0L) // rough calculation 32 + (publicAndPrivateEventCache?.sumOf { pointerSizeInBytes + it.bytesUsedInMemory() } ?: 0L) // rough calculation
override fun dTag() = FIXED_D_TAG
fun publicAndPrivateEvents( fun publicAndPrivateEvents(
signer: NostrSigner, signer: NostrSigner,
onReady: (ImmutableSet<HexKey>) -> Unit, onReady: (ImmutableSet<HexKey>) -> Unit,
@@ -67,7 +65,6 @@ class ChannelListEvent(
companion object { companion object {
const val KIND = 10005 const val KIND = 10005
const val FIXED_D_TAG = ""
const val ALT = "Public Chat List" const val ALT = "Public Chat List"
fun blockListFor(pubKeyHex: HexKey): String = "$KIND:$pubKeyHex:" fun blockListFor(pubKeyHex: HexKey): String = "$KIND:$pubKeyHex:"
@@ -22,11 +22,10 @@ package com.vitorpamplona.quartz.nip30CustomEmoji
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey 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.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
@Immutable @Immutable
@@ -37,28 +36,14 @@ class EmojiPackSelectionEvent(
tags: Array<Array<String>>, tags: Array<Array<String>>,
content: String, content: String,
sig: HexKey, sig: HexKey,
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { ) : BaseReplaceableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
override fun dTag() = FIXED_D_TAG
companion object { companion object {
const val KIND = 10030 const val KIND = 10030
const val FIXED_D_TAG = ""
const val ALT = "Emoji selection" const val ALT = "Emoji selection"
fun createAddressATag(pubKey: HexKey): ATag = fun createAddressATag(pubKey: HexKey) = ATag(KIND, pubKey, FIXED_D_TAG, null)
ATag(
KIND,
pubKey,
AdvertisedRelayListEvent.FIXED_D_TAG,
null,
)
fun createAddressTag(pubKey: HexKey): String = fun createAddressTag(pubKey: HexKey) = ATag.assembleATagId(KIND, pubKey, FIXED_D_TAG)
ATag.assembleATag(
KIND,
pubKey,
AdvertisedRelayListEvent.FIXED_D_TAG,
)
fun create( fun create(
listOfEmojiPacks: List<ATag>?, listOfEmojiPacks: List<ATag>?,
@@ -125,7 +125,7 @@ class DraftEvent(
fun createAddressTag( fun createAddressTag(
pubKey: HexKey, pubKey: HexKey,
dTag: String, dTag: String,
): String = ATag.assembleATag(KIND, pubKey, dTag) ): String = ATag.assembleATagId(KIND, pubKey, dTag)
fun create( fun create(
dTag: String, dTag: String,
@@ -22,7 +22,7 @@ package com.vitorpamplona.quartz.nip50Search
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey 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.NostrSigner
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
@@ -37,9 +37,7 @@ class SearchRelayListEvent(
tags: Array<Array<String>>, tags: Array<Array<String>>,
content: String, content: String,
sig: HexKey, sig: HexKey,
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { ) : BaseReplaceableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
override fun dTag() = FIXED_D_TAG
fun relays(): List<String> = fun relays(): List<String> =
tags.mapNotNull { tags.mapNotNull {
if (it.size > 1 && it[0] == "relay") { if (it.size > 1 && it[0] == "relay") {
@@ -51,11 +49,10 @@ class SearchRelayListEvent(
companion object { companion object {
const val KIND = 10007 const val KIND = 10007
const val FIXED_D_TAG = ""
fun createAddressATag(pubKey: HexKey): ATag = ATag(KIND, pubKey, FIXED_D_TAG, null) 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>> = fun createTagArray(relays: List<String>): Array<Array<String>> =
relays relays
@@ -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.events.taggedEvents
import com.vitorpamplona.quartz.nip01Core.tags.geohash.geohashes import com.vitorpamplona.quartz.nip01Core.tags.geohash.geohashes
import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUsers import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUsers
import com.vitorpamplona.quartz.nip19Bech32.parse
import kotlinx.collections.immutable.ImmutableSet import kotlinx.collections.immutable.ImmutableSet
import kotlinx.collections.immutable.toImmutableSet import kotlinx.collections.immutable.toImmutableSet
@@ -22,6 +22,7 @@ package com.vitorpamplona.quartz.nip51Lists
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey 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.signers.NostrSigner
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
@@ -76,7 +77,6 @@ class MuteListEvent(
companion object { companion object {
const val KIND = 10000 const val KIND = 10000
const val FIXED_D_TAG = ""
const val ALT = "Mute List" const val ALT = "Mute List"
fun blockListFor(pubKeyHex: HexKey): String = "10000:$pubKeyHex:" fun blockListFor(pubKeyHex: HexKey): String = "10000:$pubKeyHex:"
@@ -44,7 +44,7 @@ class WikiNoteEvent(
override fun address(relayHint: String?) = ATag(kind, pubKey, dTag(), relayHint) 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() fun topics() = hashtags()
@@ -22,7 +22,7 @@ package com.vitorpamplona.quartz.nip65RelayList
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey 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.NostrSigner
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
@@ -37,9 +37,7 @@ class AdvertisedRelayListEvent(
tags: Array<Array<String>>, tags: Array<Array<String>>,
content: String, content: String,
sig: HexKey, sig: HexKey,
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { ) : BaseReplaceableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
override fun dTag() = FIXED_D_TAG
fun relays(): List<AdvertisedRelayInfo> = fun relays(): List<AdvertisedRelayInfo> =
tags.mapNotNull { tags.mapNotNull {
if (it.size > 1 && it[0] == "r") { if (it.size > 1 && it[0] == "r") {
@@ -85,12 +83,11 @@ class AdvertisedRelayListEvent(
companion object { companion object {
const val KIND = 10002 const val KIND = 10002
const val FIXED_D_TAG = ""
const val ALT = "Relay list to discover the user's content" const val ALT = "Relay list to discover the user's content"
fun createAddressATag(pubKey: HexKey): ATag = ATag(KIND, pubKey, FIXED_D_TAG, null) 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( fun updateRelayList(
earlierVersion: AdvertisedRelayListEvent, earlierVersion: AdvertisedRelayListEvent,
@@ -25,9 +25,9 @@ import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag 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.geohash.geohashMipMap
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.buildHashtagTags 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.PTag
import com.vitorpamplona.quartz.nip10Notes.content.buildUrlRefs import com.vitorpamplona.quartz.nip10Notes.content.buildUrlRefs
import com.vitorpamplona.quartz.nip10Notes.content.findHashtags import com.vitorpamplona.quartz.nip10Notes.content.findHashtags
@@ -22,6 +22,7 @@ package com.vitorpamplona.quartz.nip72ModCommunities
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey 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.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip19Bech32.parseAtag import com.vitorpamplona.quartz.nip19Bech32.parseAtag
@@ -72,7 +73,6 @@ class CommunityListEvent(
companion object { companion object {
const val KIND = 10004 const val KIND = 10004
const val FIXED_D_TAG = ""
const val ALT = "Community List" const val ALT = "Community List"
fun blockListFor(pubKeyHex: HexKey): String = "$KIND:$pubKeyHex:" fun blockListFor(pubKeyHex: HexKey): String = "$KIND:$pubKeyHex:"
@@ -25,7 +25,6 @@ import com.vitorpamplona.quartz.nip01Core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip19Bech32.parse
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
@@ -22,7 +22,7 @@ package com.vitorpamplona.quartz.nip96FileStorage
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.HexKey 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.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer import com.vitorpamplona.quartz.nip31Alts.AltTagSerializer
@@ -36,9 +36,7 @@ class FileServersEvent(
tags: Array<Array<String>>, tags: Array<Array<String>>,
content: String, content: String,
sig: HexKey, sig: HexKey,
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { ) : BaseReplaceableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
override fun dTag() = FIXED_D_TAG
fun servers(): List<String> = fun servers(): List<String> =
tags.mapNotNull { tags.mapNotNull {
if (it.size > 1 && it[0] == "server") { if (it.size > 1 && it[0] == "server") {
@@ -50,12 +48,11 @@ class FileServersEvent(
companion object { companion object {
const val KIND = 10096 const val KIND = 10096
const val FIXED_D_TAG = ""
const val ALT = "File servers used by the author" const val ALT = "File servers used by the author"
fun createAddressATag(pubKey: HexKey): ATag = ATag(KIND, pubKey, FIXED_D_TAG, null) 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>> = fun createTagArray(servers: List<String>): Array<Array<String>> =
servers servers