diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt index 0bad1548b..ad2e47c4e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt @@ -805,7 +805,6 @@ object LocalCache : ILocalCache { fun computeReplyTo(event: Event): List = when (event) { is PollNoteEvent -> event.tagsWithoutCitations().mapNotNull { checkGetOrCreateNote(it) } - is WikiNoteEvent -> event.tagsWithoutCitations().mapNotNull { checkGetOrCreateNote(it) } is LongTextNoteEvent -> event.tagsWithoutCitations().mapNotNull { checkGetOrCreateNote(it) } is GitReplyEvent -> event.tagsWithoutCitations().filter { it != event.repository()?.toTag() }.mapNotNull { checkGetOrCreateNote(it) } is TextNoteEvent -> event.tagsWithoutCitations().mapNotNull { checkGetOrCreateNote(it) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt index d707d87fc..7956f2a8d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt @@ -164,7 +164,7 @@ import com.vitorpamplona.quartz.experimental.audio.header.AudioHeaderEvent import com.vitorpamplona.quartz.experimental.audio.track.AudioTrackEvent import com.vitorpamplona.quartz.experimental.bounties.bountyBaseReward import com.vitorpamplona.quartz.experimental.edits.TextNoteModificationEvent -import com.vitorpamplona.quartz.experimental.forks.isAFork +import com.vitorpamplona.quartz.experimental.forks.IForkableEvent import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStoryBaseEvent import com.vitorpamplona.quartz.experimental.medical.FhirResourceEvent import com.vitorpamplona.quartz.experimental.nip95.header.FileStorageHeaderEvent @@ -173,7 +173,6 @@ import com.vitorpamplona.quartz.experimental.publicMessages.PublicMessageEvent import com.vitorpamplona.quartz.experimental.zapPolls.PollNoteEvent import com.vitorpamplona.quartz.nip01Core.tags.geohash.geoHashOrScope import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent -import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent import com.vitorpamplona.quartz.nip13Pow.strongPoWOrNull import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent @@ -1088,7 +1087,7 @@ fun SecondUserInfoRow( modifier = UserNameMaxRowHeight, ) { Column(modifier = remember(noteEvent) { Modifier.weight(1f) }) { - if (noteEvent is BaseThreadedEvent && noteEvent.isAFork()) { + if (noteEvent is IForkableEvent && noteEvent.isAFork()) { ShowForkInformation(noteEvent, remember(noteEvent) { Modifier.weight(1f) }, accountViewModel, nav) } else { ObserveDisplayNip05Status(noteAuthor, accountViewModel, nav) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt index 279a3414f..a2814a464 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt @@ -159,6 +159,7 @@ import com.vitorpamplona.amethyst.ui.theme.reactionBox import com.vitorpamplona.amethyst.ui.theme.ripple24dp import com.vitorpamplona.amethyst.ui.theme.selectedReactionBoxModifier import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent +import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKeyable import com.vitorpamplona.quartz.nip30CustomEmoji.CustomEmoji import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiserAmount @@ -1394,16 +1395,20 @@ private fun BoostTypeChoicePopup( Text(stringRes(R.string.quote), color = Color.White, textAlign = TextAlign.Center) } - Button( - modifier = Modifier.padding(horizontal = 3.dp), - onClick = onFork, - shape = ButtonBorder, - colors = - ButtonDefaults.buttonColors( - containerColor = MaterialTheme.colorScheme.primary, - ), - ) { - Text(stringRes(R.string.fork), color = Color.White, textAlign = TextAlign.Center) + // removes the option to fork for now because we do not have screens for + // LongForm, Wiki and NIP posting. + if (baseNote.event is TextNoteEvent) { + Button( + modifier = Modifier.padding(horizontal = 3.dp), + onClick = onFork, + shape = ButtonBorder, + colors = + ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.primary, + ), + ) { + Text(stringRes(R.string.fork), color = Color.White, textAlign = TextAlign.Center) + } } } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/ForkInfo.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/ForkInfo.kt index 6ad3d4bad..2c52fdaa7 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/ForkInfo.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/ForkInfo.kt @@ -47,13 +47,11 @@ import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.theme.Font14SP import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer import com.vitorpamplona.amethyst.ui.theme.nip05 -import com.vitorpamplona.quartz.experimental.forks.forkFromAddress -import com.vitorpamplona.quartz.experimental.forks.forkFromVersion -import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent +import com.vitorpamplona.quartz.experimental.forks.IForkableEvent @Composable fun ShowForkInformation( - noteEvent: BaseThreadedEvent, + noteEvent: IForkableEvent, modifier: Modifier, accountViewModel: AccountViewModel, nav: INav, @@ -67,7 +65,7 @@ fun ShowForkInformation( } } } else if (forkedEvent != null) { - LoadNote(forkedEvent.eventId, accountViewModel) { event -> + LoadNote(forkedEvent, accountViewModel) { event -> if (event != null) { ForkInformationRowLightColor(event, modifier, accountViewModel, nav) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/dal/NotificationFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/dal/NotificationFeedFilter.kt index d07a6f284..b8a5ba010 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/dal/NotificationFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/dal/NotificationFeedFilter.kt @@ -29,14 +29,13 @@ import com.vitorpamplona.amethyst.ui.dal.AdditiveFeedFilter import com.vitorpamplona.amethyst.ui.dal.DefaultFeedOrder import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.quartz.experimental.audio.track.AudioTrackEvent -import com.vitorpamplona.quartz.experimental.forks.forkFromVersion -import com.vitorpamplona.quartz.experimental.forks.isForkFromAddressWithPubkey +import com.vitorpamplona.quartz.experimental.forks.IForkableEvent import com.vitorpamplona.quartz.experimental.nipsOnNostr.NipTextEvent import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.tags.people.isTaggedUser import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent -import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent +import com.vitorpamplona.quartz.nip10Notes.BaseNoteEvent import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent import com.vitorpamplona.quartz.nip18Reposts.GenericRepostEvent import com.vitorpamplona.quartz.nip18Reposts.RepostEvent @@ -187,7 +186,7 @@ class NotificationFeedFilter( return true } - if (event is BaseThreadedEvent) { + if (event is BaseNoteEvent) { if (note.replyTo?.any { it.author?.pubkeyHex == authorHex } == true) { return true } @@ -210,11 +209,23 @@ class NotificationFeedFilter( } val isAuthoredPostCited = event.findCitations().any { LocalCache.getNoteIfExists(it)?.author?.pubkeyHex == authorHex } - val isAuthorDirectlyCited = event.citedUsers().contains(authorHex) - val isAuthorOfAFork = - event.isForkFromAddressWithPubkey(authorHex) || (event.forkFromVersion()?.let { LocalCache.getNoteIfExists(it.eventId)?.author?.pubkeyHex == authorHex } == true) - return isAuthoredPostCited || isAuthorDirectlyCited || isAuthorOfAFork + if (isAuthoredPostCited) return true + + val isAuthorDirectlyCited = event.citedUsers().contains(authorHex) + + if (isAuthorDirectlyCited) return true + + return if (event is IForkableEvent && event.isAFork()) { + val address = event.forkFromAddress() + val version = event.forkFromVersion() + + // Displays notifications about forks + address?.pubKeyHex == authorHex || + (version?.let { LocalCache.getNoteIfExists(it)?.author?.pubkeyHex == authorHex } == true) + } else { + false + } } if (event is ReactionEvent) { diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/forks/IForkableEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/forks/IForkableEvent.kt index 47fddb817..e614cf866 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/forks/IForkableEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/forks/IForkableEvent.kt @@ -1,4 +1,34 @@ +/** + * Copyright (c) 2025 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.experimental.forks -class IForkableEvent { -} \ No newline at end of file +import androidx.compose.runtime.Stable +import com.vitorpamplona.quartz.nip01Core.core.Address +import com.vitorpamplona.quartz.nip01Core.core.HexKey + +@Stable +interface IForkableEvent { + fun isAFork(): Boolean + + fun forkFromAddress(): Address? + + fun forkFromVersion(): HexKey? +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/forks/MarkedETagExt.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/forks/MarkedETagExt.kt index cc2971896..714404777 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/forks/MarkedETagExt.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/forks/MarkedETagExt.kt @@ -20,6 +20,7 @@ */ package com.vitorpamplona.quartz.experimental.forks +import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag.MARKER @@ -37,3 +38,10 @@ fun MarkedETag.Companion.parseFork(tag: Array): MarkedETag? { ), ) } + +fun MarkedETag.Companion.parseForkedEventId(tag: Array): HexKey? { + if (tag.size < 4 || tag[0] != "e") return null + if (tag[ORDER_MARKER] != MARKER.FORK.code) return null + // ["e", id hex, relay hint, marker, pubkey] + return tag[ORDER_EVT_ID] +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/nipsOnNostr/NipTextEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/nipsOnNostr/NipTextEvent.kt index f00e84c3a..07581a783 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/nipsOnNostr/NipTextEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/experimental/nipsOnNostr/NipTextEvent.kt @@ -21,7 +21,8 @@ package com.vitorpamplona.quartz.experimental.nipsOnNostr import androidx.compose.runtime.Immutable -import com.vitorpamplona.quartz.experimental.forks.parseFork +import com.vitorpamplona.quartz.experimental.forks.IForkableEvent +import com.vitorpamplona.quartz.experimental.forks.parseForkedEventId import com.vitorpamplona.quartz.experimental.nipsOnNostr.tags.ForkTag import com.vitorpamplona.quartz.nip01Core.core.Address import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent @@ -64,6 +65,7 @@ class NipTextEvent( RootScope, EventHintProvider, AddressHintProvider, + IForkableEvent, SearchableEvent { override fun indexableContent() = "title: " + title() + "\n" + content @@ -105,11 +107,11 @@ class NipTextEvent( return aHints + qHints + nip19Hints } - fun isAFork() = tags.any { it.size > 3 && (it[0] == "a" || it[0] == "e") && it[3] == "fork" } + override fun isAFork() = tags.any { it.size > 3 && (it[0] == "a" || it[0] == "e") && it[3] == "fork" } - fun forkFromAddress() = tags.firstNotNullOfOrNull(ForkTag::parseAddress) + override fun forkFromAddress() = tags.firstNotNullOfOrNull(ForkTag::parseAddress) - fun forkFromVersion() = tags.firstNotNullOfOrNull(MarkedETag::parseFork) + override fun forkFromVersion() = tags.firstNotNullOfOrNull(MarkedETag::parseForkedEventId) fun title() = tags.firstNotNullOfOrNull(TitleTag::parse) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip10Notes/TextNoteEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip10Notes/TextNoteEvent.kt index f50a5dd48..b8ac14da8 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip10Notes/TextNoteEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip10Notes/TextNoteEvent.kt @@ -21,6 +21,8 @@ package com.vitorpamplona.quartz.nip10Notes import androidx.compose.runtime.Immutable +import com.vitorpamplona.quartz.experimental.forks.IForkableEvent +import com.vitorpamplona.quartz.experimental.forks.parseForkedEventId import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider @@ -47,6 +49,7 @@ import com.vitorpamplona.quartz.nip19Bech32.pubKeys import com.vitorpamplona.quartz.nip31Alts.alt import com.vitorpamplona.quartz.nip50Search.SearchableEvent import com.vitorpamplona.quartz.utils.TimeUtils +import kotlinx.serialization.json.JsonNull.content @Immutable class TextNoteEvent( @@ -60,6 +63,7 @@ class TextNoteEvent( EventHintProvider, AddressHintProvider, PubKeyHintProvider, + IForkableEvent, SearchableEvent { override fun indexableContent() = "Subject: " + subject() + "\n" + content @@ -109,6 +113,12 @@ class TextNoteEvent( return pHints + nip19Hints } + override fun isAFork() = tags.any { it.size > 3 && (it[0] == "a" || it[0] == "e") && it[3] == "fork" } + + override fun forkFromAddress() = tags.firstNotNullOfOrNull(ATag::parseAddress) + + override fun forkFromVersion() = tags.firstNotNullOfOrNull(MarkedETag::parseForkedEventId) + companion object { const val KIND = 1 const val ALT = "A short note: " diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip54Wiki/TagArrayBuilderExt.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip54Wiki/TagArrayBuilderExt.kt index f460e981e..f6715d8fd 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip54Wiki/TagArrayBuilderExt.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip54Wiki/TagArrayBuilderExt.kt @@ -1,13 +1,35 @@ +/** + * Copyright (c) 2025 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.nip54Wiki import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder -import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent +import com.vitorpamplona.quartz.nip23LongContent.tags.ImageTag import com.vitorpamplona.quartz.nip23LongContent.tags.PublishedAtTag +import com.vitorpamplona.quartz.nip23LongContent.tags.SummaryTag +import com.vitorpamplona.quartz.nip23LongContent.tags.TitleTag -fun TagArrayBuilder.title(title: String) = addUnique(TitleTag.assemble(title)) +fun TagArrayBuilder.title(title: String) = addUnique(TitleTag.assemble(title)) -fun TagArrayBuilder.summary(summary: String) = addUnique(SummaryTag.assemble(summary)) +fun TagArrayBuilder.summary(summary: String) = addUnique(SummaryTag.assemble(summary)) -fun TagArrayBuilder.image(imageUrl: String) = addUnique(ImageTag.assemble(imageUrl)) +fun TagArrayBuilder.image(imageUrl: String) = addUnique(ImageTag.assemble(imageUrl)) -fun TagArrayBuilder.publishedAt(publishedAt: Long) = addUnique(PublishedAtTag.assemble(publishedAt)) \ No newline at end of file +fun TagArrayBuilder.publishedAt(publishedAt: Long) = addUnique(PublishedAtTag.assemble(publishedAt)) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip54Wiki/WikiNoteEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip54Wiki/WikiNoteEvent.kt index e2908f2b4..e81c56fbe 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip54Wiki/WikiNoteEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip54Wiki/WikiNoteEvent.kt @@ -21,9 +21,13 @@ package com.vitorpamplona.quartz.nip54Wiki import androidx.compose.runtime.Immutable +import com.vitorpamplona.quartz.experimental.forks.IForkableEvent +import com.vitorpamplona.quartz.experimental.forks.parseForkedEventId +import com.vitorpamplona.quartz.experimental.nipsOnNostr.tags.ForkTag import com.vitorpamplona.quartz.nip01Core.core.Address import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent import com.vitorpamplona.quartz.nip01Core.core.HexKey +import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider @@ -31,13 +35,14 @@ import com.vitorpamplona.quartz.nip01Core.hints.types.AddressHint import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint import com.vitorpamplona.quartz.nip01Core.hints.types.PubKeyHint import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl -import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner +import com.vitorpamplona.quartz.nip01Core.signers.EventTemplate +import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip01Core.tags.aTag.ATag import com.vitorpamplona.quartz.nip01Core.tags.dTag.dTag import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags import com.vitorpamplona.quartz.nip01Core.tags.people.PTag import com.vitorpamplona.quartz.nip01Core.tags.publishedAt.PublishedAtProvider -import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent +import com.vitorpamplona.quartz.nip10Notes.BaseNoteEvent import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag import com.vitorpamplona.quartz.nip18Reposts.quotes.QTag import com.vitorpamplona.quartz.nip19Bech32.addressHints @@ -46,10 +51,16 @@ import com.vitorpamplona.quartz.nip19Bech32.eventHints import com.vitorpamplona.quartz.nip19Bech32.eventIds import com.vitorpamplona.quartz.nip19Bech32.pubKeyHints import com.vitorpamplona.quartz.nip19Bech32.pubKeys +import com.vitorpamplona.quartz.nip22Comments.RootScope +import com.vitorpamplona.quartz.nip23LongContent.tags.ImageTag import com.vitorpamplona.quartz.nip23LongContent.tags.PublishedAtTag -import com.vitorpamplona.quartz.nip31Alts.AltTag +import com.vitorpamplona.quartz.nip23LongContent.tags.SummaryTag +import com.vitorpamplona.quartz.nip23LongContent.tags.TitleTag +import com.vitorpamplona.quartz.nip31Alts.alt import com.vitorpamplona.quartz.nip50Search.SearchableEvent import com.vitorpamplona.quartz.utils.TimeUtils +import kotlin.uuid.ExperimentalUuidApi +import kotlin.uuid.Uuid @Immutable class WikiNoteEvent( @@ -59,12 +70,14 @@ class WikiNoteEvent( tags: Array>, content: String, sig: HexKey, -) : BaseThreadedEvent(id, pubKey, createdAt, KIND, tags, content, sig), +) : BaseNoteEvent(id, pubKey, createdAt, KIND, tags, content, sig), AddressableEvent, EventHintProvider, AddressHintProvider, PubKeyHintProvider, PublishedAtProvider, + IForkableEvent, + RootScope, SearchableEvent { override fun indexableContent() = "title: " + title() + "\nsummary: " + summary() + "\n" + content @@ -124,11 +137,17 @@ class WikiNoteEvent( fun topics() = hashtags() - fun title() = tags.firstOrNull { it.size > 1 && it[0] == "title" }?.get(1) + fun title() = tags.firstNotNullOfOrNull(TitleTag::parse) - fun summary() = tags.firstOrNull { it.size > 1 && it[0] == "summary" }?.get(1) + fun image() = tags.firstNotNullOfOrNull(ImageTag::parse) - fun image() = tags.firstOrNull { it.size > 1 && it[0] == "image" }?.get(1) + fun summary() = tags.firstNotNullOfOrNull(SummaryTag::parse) + + override fun isAFork() = tags.any { it.size > 3 && (it[0] == "a" || it[0] == "e") && it[3] == "fork" } + + override fun forkFromAddress() = tags.firstNotNullOfOrNull(ForkTag::parseAddress) + + override fun forkFromVersion() = tags.firstNotNullOfOrNull(MarkedETag::parseForkedEventId) override fun publishedAt(): Long? { val publishedAt = tags.firstNotNullOfOrNull(PublishedAtTag::parse) ?: return null @@ -144,20 +163,27 @@ class WikiNoteEvent( companion object { const val KIND = 30818 - suspend fun create( - msg: String, - title: String?, - replyTos: List?, - mentions: List?, - signer: NostrSigner, + @OptIn(ExperimentalUuidApi::class) + fun build( + description: String, + title: String, + summary: String? = null, + image: String? = null, + publishedAt: Long? = null, + dTag: String = Uuid.random().toString(), createdAt: Long = TimeUtils.now(), - ): WikiNoteEvent { - val tags = mutableListOf>() - replyTos?.forEach { tags.add(arrayOf("e", it)) } - mentions?.forEach { tags.add(arrayOf("p", it)) } - title?.let { tags.add(arrayOf("title", it)) } - tags.add(AltTag.assemble("Wiki Post: $title")) - return signer.sign(createdAt, KIND, tags.toTypedArray(), msg) - } + initializer: TagArrayBuilder.() -> Unit = {}, + ): EventTemplate = + eventTemplate(KIND, description, createdAt) { + dTag(dTag) + alt("Wiki entry: $title") + + title(title) + summary?.let { summary(it) } + image?.let { image(it) } + publishedAt?.let { publishedAt(it) } + + initializer() + } } }