Moves to a fork model via interfaces to start creating specific screens for each event type

This commit is contained in:
Vitor Pamplona
2026-01-10 11:36:11 -05:00
parent 1c26ccecc0
commit 1ead2983ac
11 changed files with 169 additions and 59 deletions
@@ -805,7 +805,6 @@ object LocalCache : ILocalCache {
fun computeReplyTo(event: Event): List<Note> = fun computeReplyTo(event: Event): List<Note> =
when (event) { when (event) {
is PollNoteEvent -> event.tagsWithoutCitations().mapNotNull { checkGetOrCreateNote(it) } is PollNoteEvent -> event.tagsWithoutCitations().mapNotNull { checkGetOrCreateNote(it) }
is WikiNoteEvent -> event.tagsWithoutCitations().mapNotNull { checkGetOrCreateNote(it) }
is LongTextNoteEvent -> 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 GitReplyEvent -> event.tagsWithoutCitations().filter { it != event.repository()?.toTag() }.mapNotNull { checkGetOrCreateNote(it) }
is TextNoteEvent -> event.tagsWithoutCitations().mapNotNull { checkGetOrCreateNote(it) } is TextNoteEvent -> event.tagsWithoutCitations().mapNotNull { checkGetOrCreateNote(it) }
@@ -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.audio.track.AudioTrackEvent
import com.vitorpamplona.quartz.experimental.bounties.bountyBaseReward import com.vitorpamplona.quartz.experimental.bounties.bountyBaseReward
import com.vitorpamplona.quartz.experimental.edits.TextNoteModificationEvent 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.interactiveStories.InteractiveStoryBaseEvent
import com.vitorpamplona.quartz.experimental.medical.FhirResourceEvent import com.vitorpamplona.quartz.experimental.medical.FhirResourceEvent
import com.vitorpamplona.quartz.experimental.nip95.header.FileStorageHeaderEvent 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.experimental.zapPolls.PollNoteEvent
import com.vitorpamplona.quartz.nip01Core.tags.geohash.geoHashOrScope import com.vitorpamplona.quartz.nip01Core.tags.geohash.geoHashOrScope
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
import com.vitorpamplona.quartz.nip13Pow.strongPoWOrNull import com.vitorpamplona.quartz.nip13Pow.strongPoWOrNull
import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent
@@ -1088,7 +1087,7 @@ fun SecondUserInfoRow(
modifier = UserNameMaxRowHeight, modifier = UserNameMaxRowHeight,
) { ) {
Column(modifier = remember(noteEvent) { Modifier.weight(1f) }) { 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) ShowForkInformation(noteEvent, remember(noteEvent) { Modifier.weight(1f) }, accountViewModel, nav)
} else { } else {
ObserveDisplayNip05Status(noteAuthor, accountViewModel, nav) ObserveDisplayNip05Status(noteAuthor, accountViewModel, nav)
@@ -159,6 +159,7 @@ import com.vitorpamplona.amethyst.ui.theme.reactionBox
import com.vitorpamplona.amethyst.ui.theme.ripple24dp import com.vitorpamplona.amethyst.ui.theme.ripple24dp
import com.vitorpamplona.amethyst.ui.theme.selectedReactionBoxModifier import com.vitorpamplona.amethyst.ui.theme.selectedReactionBoxModifier
import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKeyable import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKeyable
import com.vitorpamplona.quartz.nip30CustomEmoji.CustomEmoji import com.vitorpamplona.quartz.nip30CustomEmoji.CustomEmoji
import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiserAmount 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) Text(stringRes(R.string.quote), color = Color.White, textAlign = TextAlign.Center)
} }
Button( // removes the option to fork for now because we do not have screens for
modifier = Modifier.padding(horizontal = 3.dp), // LongForm, Wiki and NIP posting.
onClick = onFork, if (baseNote.event is TextNoteEvent) {
shape = ButtonBorder, Button(
colors = modifier = Modifier.padding(horizontal = 3.dp),
ButtonDefaults.buttonColors( onClick = onFork,
containerColor = MaterialTheme.colorScheme.primary, shape = ButtonBorder,
), colors =
) { ButtonDefaults.buttonColors(
Text(stringRes(R.string.fork), color = Color.White, textAlign = TextAlign.Center) containerColor = MaterialTheme.colorScheme.primary,
),
) {
Text(stringRes(R.string.fork), color = Color.White, textAlign = TextAlign.Center)
}
} }
} }
} }
@@ -47,13 +47,11 @@ import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.Font14SP import com.vitorpamplona.amethyst.ui.theme.Font14SP
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.nip05 import com.vitorpamplona.amethyst.ui.theme.nip05
import com.vitorpamplona.quartz.experimental.forks.forkFromAddress import com.vitorpamplona.quartz.experimental.forks.IForkableEvent
import com.vitorpamplona.quartz.experimental.forks.forkFromVersion
import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent
@Composable @Composable
fun ShowForkInformation( fun ShowForkInformation(
noteEvent: BaseThreadedEvent, noteEvent: IForkableEvent,
modifier: Modifier, modifier: Modifier,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
@@ -67,7 +65,7 @@ fun ShowForkInformation(
} }
} }
} else if (forkedEvent != null) { } else if (forkedEvent != null) {
LoadNote(forkedEvent.eventId, accountViewModel) { event -> LoadNote(forkedEvent, accountViewModel) { event ->
if (event != null) { if (event != null) {
ForkInformationRowLightColor(event, modifier, accountViewModel, nav) ForkInformationRowLightColor(event, modifier, accountViewModel, nav)
} }
@@ -29,14 +29,13 @@ import com.vitorpamplona.amethyst.ui.dal.AdditiveFeedFilter
import com.vitorpamplona.amethyst.ui.dal.DefaultFeedOrder import com.vitorpamplona.amethyst.ui.dal.DefaultFeedOrder
import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.dal.FilterByListParams
import com.vitorpamplona.quartz.experimental.audio.track.AudioTrackEvent import com.vitorpamplona.quartz.experimental.audio.track.AudioTrackEvent
import com.vitorpamplona.quartz.experimental.forks.forkFromVersion import com.vitorpamplona.quartz.experimental.forks.IForkableEvent
import com.vitorpamplona.quartz.experimental.forks.isForkFromAddressWithPubkey
import com.vitorpamplona.quartz.experimental.nipsOnNostr.NipTextEvent import com.vitorpamplona.quartz.experimental.nipsOnNostr.NipTextEvent
import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.tags.people.isTaggedUser import com.vitorpamplona.quartz.nip01Core.tags.people.isTaggedUser
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent 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.nip10Notes.TextNoteEvent
import com.vitorpamplona.quartz.nip18Reposts.GenericRepostEvent import com.vitorpamplona.quartz.nip18Reposts.GenericRepostEvent
import com.vitorpamplona.quartz.nip18Reposts.RepostEvent import com.vitorpamplona.quartz.nip18Reposts.RepostEvent
@@ -187,7 +186,7 @@ class NotificationFeedFilter(
return true return true
} }
if (event is BaseThreadedEvent) { if (event is BaseNoteEvent) {
if (note.replyTo?.any { it.author?.pubkeyHex == authorHex } == true) { if (note.replyTo?.any { it.author?.pubkeyHex == authorHex } == true) {
return true return true
} }
@@ -210,11 +209,23 @@ class NotificationFeedFilter(
} }
val isAuthoredPostCited = event.findCitations().any { LocalCache.getNoteIfExists(it)?.author?.pubkeyHex == authorHex } 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) { if (event is ReactionEvent) {
@@ -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 package com.vitorpamplona.quartz.experimental.forks
class IForkableEvent { 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?
}
@@ -20,6 +20,7 @@
*/ */
package com.vitorpamplona.quartz.experimental.forks 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.nip01Core.relay.normalizer.RelayUrlNormalizer
import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag
import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag.MARKER import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag.MARKER
@@ -37,3 +38,10 @@ fun MarkedETag.Companion.parseFork(tag: Array<String>): MarkedETag? {
), ),
) )
} }
fun MarkedETag.Companion.parseForkedEventId(tag: Array<String>): 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]
}
@@ -21,7 +21,8 @@
package com.vitorpamplona.quartz.experimental.nipsOnNostr package com.vitorpamplona.quartz.experimental.nipsOnNostr
import androidx.compose.runtime.Immutable 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.experimental.nipsOnNostr.tags.ForkTag
import com.vitorpamplona.quartz.nip01Core.core.Address import com.vitorpamplona.quartz.nip01Core.core.Address
import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
@@ -64,6 +65,7 @@ class NipTextEvent(
RootScope, RootScope,
EventHintProvider, EventHintProvider,
AddressHintProvider, AddressHintProvider,
IForkableEvent,
SearchableEvent { SearchableEvent {
override fun indexableContent() = "title: " + title() + "\n" + content override fun indexableContent() = "title: " + title() + "\n" + content
@@ -105,11 +107,11 @@ class NipTextEvent(
return aHints + qHints + nip19Hints 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) fun title() = tags.firstNotNullOfOrNull(TitleTag::parse)
@@ -21,6 +21,8 @@
package com.vitorpamplona.quartz.nip10Notes package com.vitorpamplona.quartz.nip10Notes
import androidx.compose.runtime.Immutable 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.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.hints.AddressHintProvider 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.nip31Alts.alt
import com.vitorpamplona.quartz.nip50Search.SearchableEvent import com.vitorpamplona.quartz.nip50Search.SearchableEvent
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import kotlinx.serialization.json.JsonNull.content
@Immutable @Immutable
class TextNoteEvent( class TextNoteEvent(
@@ -60,6 +63,7 @@ class TextNoteEvent(
EventHintProvider, EventHintProvider,
AddressHintProvider, AddressHintProvider,
PubKeyHintProvider, PubKeyHintProvider,
IForkableEvent,
SearchableEvent { SearchableEvent {
override fun indexableContent() = "Subject: " + subject() + "\n" + content override fun indexableContent() = "Subject: " + subject() + "\n" + content
@@ -109,6 +113,12 @@ class TextNoteEvent(
return pHints + nip19Hints 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 { companion object {
const val KIND = 1 const val KIND = 1
const val ALT = "A short note: " const val ALT = "A short note: "
@@ -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 package com.vitorpamplona.quartz.nip54Wiki
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder 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.PublishedAtTag
import com.vitorpamplona.quartz.nip23LongContent.tags.SummaryTag
import com.vitorpamplona.quartz.nip23LongContent.tags.TitleTag
fun TagArrayBuilder<LongTextNoteEvent>.title(title: String) = addUnique(TitleTag.assemble(title)) fun TagArrayBuilder<WikiNoteEvent>.title(title: String) = addUnique(TitleTag.assemble(title))
fun TagArrayBuilder<LongTextNoteEvent>.summary(summary: String) = addUnique(SummaryTag.assemble(summary)) fun TagArrayBuilder<WikiNoteEvent>.summary(summary: String) = addUnique(SummaryTag.assemble(summary))
fun TagArrayBuilder<LongTextNoteEvent>.image(imageUrl: String) = addUnique(ImageTag.assemble(imageUrl)) fun TagArrayBuilder<WikiNoteEvent>.image(imageUrl: String) = addUnique(ImageTag.assemble(imageUrl))
fun TagArrayBuilder<LongTextNoteEvent>.publishedAt(publishedAt: Long) = addUnique(PublishedAtTag.assemble(publishedAt)) fun TagArrayBuilder<WikiNoteEvent>.publishedAt(publishedAt: Long) = addUnique(PublishedAtTag.assemble(publishedAt))
@@ -21,9 +21,13 @@
package com.vitorpamplona.quartz.nip54Wiki package com.vitorpamplona.quartz.nip54Wiki
import androidx.compose.runtime.Immutable 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.Address
import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.HexKey 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.AddressHintProvider
import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider import com.vitorpamplona.quartz.nip01Core.hints.EventHintProvider
import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider 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.EventIdHint
import com.vitorpamplona.quartz.nip01Core.hints.types.PubKeyHint import com.vitorpamplona.quartz.nip01Core.hints.types.PubKeyHint
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl 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.aTag.ATag
import com.vitorpamplona.quartz.nip01Core.tags.dTag.dTag import com.vitorpamplona.quartz.nip01Core.tags.dTag.dTag
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip01Core.tags.publishedAt.PublishedAtProvider 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.nip10Notes.tags.MarkedETag
import com.vitorpamplona.quartz.nip18Reposts.quotes.QTag import com.vitorpamplona.quartz.nip18Reposts.quotes.QTag
import com.vitorpamplona.quartz.nip19Bech32.addressHints 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.eventIds
import com.vitorpamplona.quartz.nip19Bech32.pubKeyHints import com.vitorpamplona.quartz.nip19Bech32.pubKeyHints
import com.vitorpamplona.quartz.nip19Bech32.pubKeys 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.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.nip50Search.SearchableEvent
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import kotlin.uuid.ExperimentalUuidApi
import kotlin.uuid.Uuid
@Immutable @Immutable
class WikiNoteEvent( class WikiNoteEvent(
@@ -59,12 +70,14 @@ class WikiNoteEvent(
tags: Array<Array<String>>, tags: Array<Array<String>>,
content: String, content: String,
sig: HexKey, sig: HexKey,
) : BaseThreadedEvent(id, pubKey, createdAt, KIND, tags, content, sig), ) : BaseNoteEvent(id, pubKey, createdAt, KIND, tags, content, sig),
AddressableEvent, AddressableEvent,
EventHintProvider, EventHintProvider,
AddressHintProvider, AddressHintProvider,
PubKeyHintProvider, PubKeyHintProvider,
PublishedAtProvider, PublishedAtProvider,
IForkableEvent,
RootScope,
SearchableEvent { SearchableEvent {
override fun indexableContent() = "title: " + title() + "\nsummary: " + summary() + "\n" + content override fun indexableContent() = "title: " + title() + "\nsummary: " + summary() + "\n" + content
@@ -124,11 +137,17 @@ class WikiNoteEvent(
fun topics() = hashtags() 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? { override fun publishedAt(): Long? {
val publishedAt = tags.firstNotNullOfOrNull(PublishedAtTag::parse) ?: return null val publishedAt = tags.firstNotNullOfOrNull(PublishedAtTag::parse) ?: return null
@@ -144,20 +163,27 @@ class WikiNoteEvent(
companion object { companion object {
const val KIND = 30818 const val KIND = 30818
suspend fun create( @OptIn(ExperimentalUuidApi::class)
msg: String, fun build(
title: String?, description: String,
replyTos: List<String>?, title: String,
mentions: List<String>?, summary: String? = null,
signer: NostrSigner, image: String? = null,
publishedAt: Long? = null,
dTag: String = Uuid.random().toString(),
createdAt: Long = TimeUtils.now(), createdAt: Long = TimeUtils.now(),
): WikiNoteEvent { initializer: TagArrayBuilder<WikiNoteEvent>.() -> Unit = {},
val tags = mutableListOf<Array<String>>() ): EventTemplate<WikiNoteEvent> =
replyTos?.forEach { tags.add(arrayOf("e", it)) } eventTemplate(KIND, description, createdAt) {
mentions?.forEach { tags.add(arrayOf("p", it)) } dTag(dTag)
title?.let { tags.add(arrayOf("title", it)) } alt("Wiki entry: $title")
tags.add(AltTag.assemble("Wiki Post: $title"))
return signer.sign(createdAt, KIND, tags.toTypedArray(), msg) title(title)
} summary?.let { summary(it) }
image?.let { image(it) }
publishedAt?.let { publishedAt(it) }
initializer()
}
} }
} }