From 3c36f52bafa7fe879c81ff6f4595e561c15cb6c0 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Mon, 26 Feb 2024 15:17:17 -0500 Subject: [PATCH] Visual support for Kind1 forks. --- .../vitorpamplona/amethyst/model/Account.kt | 2 + .../amethyst/ui/actions/NewPostView.kt | 3 +- .../amethyst/ui/actions/NewPostViewModel.kt | 46 +++++++++- .../amethyst/ui/buttons/ChannelFabColumn.kt | 2 +- .../amethyst/ui/components/ClickableRoute.kt | 7 +- .../amethyst/ui/components/SplitItem.kt | 9 ++ .../amethyst/ui/elements/DisplayReward.kt | 1 + .../amethyst/ui/note/NoteCompose.kt | 89 +++++++++++++++++-- .../amethyst/ui/note/ReactionsRow.kt | 53 ++++++++++- .../amethyst/ui/screen/ThreadFeedView.kt | 7 +- .../ui/screen/loggedIn/VideoScreen.kt | 9 +- app/src/main/res/values/strings.xml | 2 + .../quartz/events/BaseTextNoteEvent.kt | 13 +++ .../quartz/events/TextNoteEvent.kt | 5 ++ .../quartz/events/WikiNoteEvent.kt | 10 --- 15 files changed, 226 insertions(+), 32 deletions(-) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt index 21331740e..1cc7b8423 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt @@ -1325,6 +1325,7 @@ class Account( replyingTo: String?, root: String?, directMentions: Set, + forkedFrom: Event?, relayList: List? = null, geohash: String? = null, nip94attachments: List? = null, @@ -1349,6 +1350,7 @@ class Account( directMentions = directMentions, geohash = geohash, nip94attachments = nip94attachments, + forkedFrom = forkedFrom, signer = signer, ) { Client.send(it, relayList = relayList) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt index 5ebf74489..9294f7081 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt @@ -186,6 +186,7 @@ fun NewPostView( onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = null, + fork: Note? = null, enableMessageInterface: Boolean = false, accountViewModel: AccountViewModel, nav: (String) -> Unit, @@ -201,7 +202,7 @@ fun NewPostView( var relayList = remember { accountViewModel.account.activeWriteRelays().toImmutableList() } LaunchedEffect(Unit) { - postViewModel.load(accountViewModel, baseReplyTo, quote) + postViewModel.load(accountViewModel, baseReplyTo, quote, fork) launch(Dispatchers.IO) { postViewModel.imageUploadingError.collect { error -> diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt index f25bdeb94..944f01097 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt @@ -55,6 +55,7 @@ import com.vitorpamplona.quartz.events.BaseTextNoteEvent import com.vitorpamplona.quartz.events.ChatMessageEvent import com.vitorpamplona.quartz.events.ClassifiedsEvent import com.vitorpamplona.quartz.events.CommunityDefinitionEvent +import com.vitorpamplona.quartz.events.Event import com.vitorpamplona.quartz.events.FileHeaderEvent import com.vitorpamplona.quartz.events.FileStorageEvent import com.vitorpamplona.quartz.events.FileStorageHeaderEvent @@ -85,10 +86,10 @@ open class NewPostViewModel() : ViewModel() { var requiresNIP24: Boolean = false var originalNote: Note? = null + var forkedFromNote: Note? = null var pTags by mutableStateOf?>(null) var eTags by mutableStateOf?>(null) - var imetaTags = mutableStateListOf>() var nip94attachments by mutableStateOf>(emptyList()) var nip95attachments by @@ -166,6 +167,7 @@ open class NewPostViewModel() : ViewModel() { accountViewModel: AccountViewModel, replyingTo: Note?, quote: Note?, + fork: Note?, ) { this.accountViewModel = accountViewModel this.account = accountViewModel.account @@ -214,6 +216,37 @@ open class NewPostViewModel() : ViewModel() { zapRaiserAmount = null forwardZapTo = Split() forwardZapToEditting = TextFieldValue("") + + fork?.let { + message = TextFieldValue(it.event?.content() ?: "") + urlPreview = findUrlInMessage() + + it.event?.isSensitive()?.let { + if (it) wantsToMarkAsSensitive = true + } + + it.event?.zapraiserAmount()?.let { + zapRaiserAmount = it + } + + it.event?.zapSplitSetup()?.let { + val totalWeight = it.sumOf { if (it.isLnAddress) 0.0 else it.weight } + + it.forEach { + if (!it.isLnAddress) { + forwardZapTo.addItem(LocalCache.getOrCreateUser(it.lnAddressOrPubKeyHex), (it.weight / totalWeight).toFloat()) + } + } + } + + it.author?.let { + if (this.pTags?.contains(it) != true) { + this.pTags = listOf(it) + (this.pTags ?: emptyList()) + } + } + + forkedFromNote = it + } } fun sendPost(relayList: List? = null) { @@ -404,9 +437,16 @@ open class NewPostViewModel() : ViewModel() { val replyId = originalNote?.idHex + val replyToSet = + if (forkedFromNote != null) { + (listOfNotNull(forkedFromNote) + (tagger.eTags ?: emptyList())).ifEmpty { null } + } else { + tagger.eTags + } + account?.sendPost( message = tagger.message, - replyTo = tagger.eTags, + replyTo = replyToSet, mentions = tagger.pTags, tags = null, zapReceiver = zapReceiver, @@ -415,6 +455,7 @@ open class NewPostViewModel() : ViewModel() { replyingTo = replyId, root = rootId, directMentions = tagger.directMentions, + forkedFrom = forkedFromNote?.event as? Event, relayList = relayList, geohash = geoHash, nip94attachments = usedAttachments, @@ -504,7 +545,6 @@ open class NewPostViewModel() : ViewModel() { urlPreview = null isUploadingImage = false pTags = null - imetaTags.clear() wantsDirectMessage = false diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/buttons/ChannelFabColumn.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/buttons/ChannelFabColumn.kt index d2544a659..018d32834 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/buttons/ChannelFabColumn.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/buttons/ChannelFabColumn.kt @@ -65,7 +65,7 @@ fun ChannelFabColumn( if (wantsToSendNewMessage) { NewPostView( - { wantsToSendNewMessage = false }, + onClose = { wantsToSendNewMessage = false }, enableMessageInterface = true, accountViewModel = accountViewModel, nav = nav, diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRoute.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRoute.kt index 31f5ea043..107f40c7d 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRoute.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRoute.kt @@ -294,6 +294,7 @@ fun CreateClickableText( maxLines: Int = Int.MAX_VALUE, overrideColor: Color? = null, fontWeight: FontWeight? = null, + fontSize: TextUnit = TextUnit.Unspecified, route: String, nav: (String) -> Unit, ) { @@ -304,12 +305,14 @@ fun CreateClickableText( remember(clickablePart, suffix) { val clickablePartStyle = SpanStyle( + fontSize = fontSize, color = overrideColor ?: primaryColor, fontWeight = fontWeight, ) val nonClickablePartStyle = SpanStyle( + fontSize = fontSize, color = overrideColor ?: onBackgroundColor, fontWeight = fontWeight, ) @@ -562,6 +565,7 @@ fun CreateClickableTextWithEmoji( maxLines: Int = Int.MAX_VALUE, overrideColor: Color? = null, fontWeight: FontWeight = FontWeight.Normal, + fontSize: TextUnit = TextUnit.Unspecified, route: String, nav: (String) -> Unit, tags: ImmutableListOfLists?, @@ -570,11 +574,12 @@ fun CreateClickableTextWithEmoji( text = clickablePart, tags = tags, onRegularText = { - CreateClickableText(it, null, maxLines, overrideColor, fontWeight, route, nav) + CreateClickableText(it, null, maxLines, overrideColor, fontWeight, fontSize, route, nav) }, onEmojiText = { val clickablePartStyle = SpanStyle( + fontSize = fontSize, color = overrideColor ?: MaterialTheme.colorScheme.primary, fontWeight = fontWeight, ) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/SplitItem.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/SplitItem.kt index 389cb358a..6fedfb580 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/SplitItem.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/SplitItem.kt @@ -32,6 +32,15 @@ class SplitItem(val key: T) { class Split() { var items: List> by mutableStateOf(emptyList()) + fun addItem( + key: T, + percentage: Float, + ) { + val newItem = SplitItem(key) + newItem.percentage = percentage + this.items = items.plus(newItem) + } + fun addItem(key: T): Int { val wasEqualSplit = isEqualSplit() val newItem = SplitItem(key) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/elements/DisplayReward.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/elements/DisplayReward.kt index ae6e5409a..2c7520103 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/elements/DisplayReward.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/elements/DisplayReward.kt @@ -184,6 +184,7 @@ class AddBountyAmountViewModel : ViewModel() { replyingTo = null, root = null, directMentions = setOf(), + forkedFrom = null, ) nextAmount = TextFieldValue("") diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt index a895af644..935ec7838 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt @@ -80,6 +80,7 @@ import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow @@ -178,6 +179,7 @@ import com.vitorpamplona.amethyst.ui.theme.grayText import com.vitorpamplona.amethyst.ui.theme.imageModifier import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink import com.vitorpamplona.amethyst.ui.theme.newItemBackgroundColor +import com.vitorpamplona.amethyst.ui.theme.nip05 import com.vitorpamplona.amethyst.ui.theme.normalNoteModifier import com.vitorpamplona.amethyst.ui.theme.normalWithTopMarginNoteModifier import com.vitorpamplona.amethyst.ui.theme.placeholderText @@ -2512,28 +2514,32 @@ fun SecondUserInfoRow( accountViewModel: AccountViewModel, nav: (String) -> Unit, ) { - val noteEvent = remember { note.event } ?: return - val noteAuthor = remember { note.author } ?: return + val noteEvent = note.event ?: return + val noteAuthor = note.author ?: return Row( verticalAlignment = CenterVertically, modifier = UserNameMaxRowHeight, ) { - ObserveDisplayNip05Status(noteAuthor, remember { Modifier.weight(1f) }, accountViewModel, nav) + if (noteEvent is BaseTextNoteEvent && noteEvent.isAFork()) { + ShowForkInformation(noteEvent, remember(noteEvent) { Modifier.weight(1f) }, accountViewModel, nav) + } else { + ObserveDisplayNip05Status(noteAuthor, remember(noteEvent) { Modifier.weight(1f) }, accountViewModel, nav) + } - val geo = remember { noteEvent.getGeoHash() } + val geo = remember(noteEvent) { noteEvent.getGeoHash() } if (geo != null) { Spacer(StdHorzSpacer) DisplayLocation(geo, nav) } - val baseReward = remember { noteEvent.getReward()?.let { Reward(it) } } + val baseReward = remember(noteEvent) { noteEvent.getReward()?.let { Reward(it) } } if (baseReward != null) { Spacer(StdHorzSpacer) DisplayReward(baseReward, note, accountViewModel, nav) } - val pow = remember { noteEvent.getPoWRank() } + val pow = remember(noteEvent) { noteEvent.getPoWRank() } if (pow > 20) { Spacer(StdHorzSpacer) DisplayPoW(pow) @@ -2541,6 +2547,76 @@ fun SecondUserInfoRow( } } +@Composable +private fun ShowForkInformation( + noteEvent: BaseTextNoteEvent, + modifier: Modifier, + accountViewModel: AccountViewModel, + nav: (String) -> Unit, +) { + val forkedAddress = remember(noteEvent) { noteEvent.forkFromAddress() } + val forkedEvent = remember(noteEvent) { noteEvent.forkFromVersion() } + if (forkedAddress != null) { + LoadAddressableNote(aTag = forkedAddress, accountViewModel = accountViewModel) { addressableNote -> + if (addressableNote != null) { + ForkInformationRowLightColor(addressableNote, modifier, accountViewModel, nav) + } + } + } else if (forkedEvent != null) { + LoadNote(forkedEvent, accountViewModel = accountViewModel) { event -> + if (event != null) { + ForkInformationRowLightColor(event, modifier, accountViewModel, nav) + } + } + } +} + +@Composable +fun ForkInformationRowLightColor( + originalVersion: Note, + modifier: Modifier = Modifier, + accountViewModel: AccountViewModel, + nav: (String) -> Unit, +) { + val noteState by originalVersion.live().metadata.observeAsState() + val note = noteState?.note ?: return + val author = note.author ?: return + val route = remember(note) { routeFor(note, accountViewModel.userProfile()) } + + if (route != null) { + Row(modifier) { + ClickableText( + text = + buildAnnotatedString { + append(stringResource(id = R.string.forked_from)) + append(" ") + }, + onClick = { nav(route) }, + style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.nip05, fontSize = Font14SP), + maxLines = 1, + overflow = TextOverflow.Visible, + ) + + val userState by author.live().metadata.observeAsState() + val userDisplayName = remember(userState) { userState?.user?.toBestDisplayName() } + val userTags = + remember(userState) { userState?.user?.info?.latestMetadata?.tags?.toImmutableListOfLists() } + + if (userDisplayName != null) { + CreateClickableTextWithEmoji( + clickablePart = userDisplayName, + maxLines = 1, + route = route, + overrideColor = MaterialTheme.colorScheme.nip05, + fontSize = Font14SP, + nav = nav, + tags = userTags, + ) + } + } + } +} + @Composable fun LoadStatuses( user: User, @@ -3928,7 +4004,6 @@ private fun WikiNoteHeader( nav: (String) -> Unit, ) { val title = remember(noteEvent) { noteEvent.title() } - val forkedAddress = remember(noteEvent) { noteEvent.forkFromAddress() } val summary = remember(noteEvent) { noteEvent.summary()?.ifBlank { null } ?: noteEvent.content.take(200).ifBlank { null } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt index 098a56f4d..794a20752 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt @@ -127,6 +127,7 @@ import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderTextColorFilter import com.vitorpamplona.quartz.encoders.Nip30CustomEmoji +import com.vitorpamplona.quartz.events.BaseTextNoteEvent import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableSet import kotlinx.collections.immutable.persistentListOf @@ -499,6 +500,7 @@ private fun BoostWithDialog( nav: (String) -> Unit, ) { var wantsToQuote by remember { mutableStateOf(null) } + var wantsToFork by remember { mutableStateOf(null) } if (wantsToQuote != null) { NewPostView( @@ -510,7 +512,34 @@ private fun BoostWithDialog( ) } - BoostReaction(baseNote, grayTint, accountViewModel) { wantsToQuote = baseNote } + if (wantsToFork != null) { + val replyTo = + remember(wantsToFork) { + val forkEvent = wantsToFork?.event + if (forkEvent is BaseTextNoteEvent) { + val hex = forkEvent.replyingTo() + wantsToFork?.replyTo?.filter { it.event?.id() == hex }?.firstOrNull() + } else { + null + } + } + + NewPostView( + onClose = { wantsToFork = null }, + baseReplyTo = replyTo, + fork = wantsToFork, + accountViewModel = accountViewModel, + nav = nav, + ) + } + + BoostReaction( + baseNote, + grayTint, + accountViewModel, + onQuotePress = { wantsToQuote = baseNote }, + onForkPress = { wantsToFork = baseNote }, + ) } @Composable @@ -650,6 +679,7 @@ fun BoostReaction( iconSizeModifier: Modifier = Size20Modifier, iconSize: Dp = Size20dp, onQuotePress: () -> Unit, + onForkPress: () -> Unit, ) { var wantsToBoost by remember { mutableStateOf(false) } @@ -671,7 +701,13 @@ fun BoostReaction( wantsToBoost = false onQuotePress() }, - onRepost = { accountViewModel.boost(baseNote) }, + onRepost = { + accountViewModel.boost(baseNote) + }, + onFork = { + wantsToBoost = false + onForkPress() + }, ) } } @@ -1149,6 +1185,7 @@ private fun BoostTypeChoicePopup( onDismiss: () -> Unit, onQuote: () -> Unit, onRepost: () -> Unit, + onFork: () -> Unit, ) { val iconSizePx = with(LocalDensity.current) { -iconSize.toPx().toInt() } @@ -1189,6 +1226,18 @@ private fun BoostTypeChoicePopup( ) { Text(stringResource(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(stringResource(R.string.fork), color = Color.White, textAlign = TextAlign.Center) + } } } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ThreadFeedView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ThreadFeedView.kt index abb9572d6..511a8caa4 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ThreadFeedView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ThreadFeedView.kt @@ -83,7 +83,6 @@ import androidx.compose.ui.unit.sp import androidx.lifecycle.compose.collectAsStateWithLifecycle import coil.compose.AsyncImage import com.vitorpamplona.amethyst.R -import com.vitorpamplona.amethyst.model.AddressableNote import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.ui.components.InlineCarrousel @@ -913,7 +912,7 @@ private fun RenderWikiHeaderForThread( forkedAddress?.let { LoadAddressableNote(aTag = it, accountViewModel = accountViewModel) { originalVersion -> if (originalVersion != null) { - ShowForkInformation(originalVersion, Modifier.fillMaxWidth(), accountViewModel, nav) + ForkInformationRow(originalVersion, Modifier.fillMaxWidth(), accountViewModel, nav) } } } @@ -934,8 +933,8 @@ private fun RenderWikiHeaderForThread( } @Composable -fun ShowForkInformation( - originalVersion: AddressableNote, +fun ForkInformationRow( + originalVersion: Note, modifier: Modifier = Modifier, accountViewModel: AccountViewModel, nav: (String) -> Unit, diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/VideoScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/VideoScreen.kt index 0ac705db3..24ff01a66 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/VideoScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/VideoScreen.kt @@ -476,9 +476,12 @@ fun ReactionsColumn( accountViewModel = accountViewModel, iconSizeModifier = Size40Modifier, iconSize = Size40dp, - ) { - wantsToQuote = baseNote - } + onQuotePress = { + wantsToQuote = baseNote + }, + onForkPress = { + }, + ) LikeReaction( baseNote = baseNote, grayTint = MaterialTheme.colorScheme.onBackground, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d68c4f42e..f83505b19 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -47,6 +47,7 @@ Boost boosted Quote + Fork New Amount in Sats Add "replying to " @@ -777,6 +778,7 @@ Max Limit Restricted Writes Forked from + FORK Git Repository: %1$s Web: Clone: diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/BaseTextNoteEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/BaseTextNoteEvent.kt index 39ba084ed..7d387e918 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/BaseTextNoteEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/BaseTextNoteEvent.kt @@ -22,6 +22,7 @@ package com.vitorpamplona.quartz.events import android.util.Log import androidx.compose.runtime.Immutable +import com.vitorpamplona.quartz.encoders.ATag import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.Nip19Bech32 import com.vitorpamplona.quartz.encoders.Nip19Bech32.nip19regex @@ -42,6 +43,18 @@ open class BaseTextNoteEvent( ) : Event(id, pubKey, createdAt, kind, tags, content, sig) { fun mentions() = taggedUsers() + fun isAFork() = tags.any { it.size > 3 && (it[0] == "a" || it[0] == "e") && it[3] == "fork" } + + fun forkFromAddress() = + tags.firstOrNull { it.size > 3 && it[0] == "a" && it[3] == "fork" }?.let { + val aTagValue = it[1] + val relay = it.getOrNull(2) + + ATag.parse(aTagValue, relay) + } + + fun forkFromVersion() = tags.firstOrNull { it.size > 3 && it[0] == "e" && it[3] == "fork" }?.get(1) + open fun replyTos(): List { val oldStylePositional = tags.filter { it.size > 1 && it.size <= 3 && it[0] == "e" }.map { it[1] } val newStyleReply = tags.lastOrNull { it.size > 3 && it[0] == "e" && it[3] == "reply" }?.get(1) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/TextNoteEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/TextNoteEvent.kt index bd0efc504..79a3161b2 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/TextNoteEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/TextNoteEvent.kt @@ -57,6 +57,7 @@ class TextNoteEvent( directMentions: Set = emptySet(), geohash: String? = null, nip94attachments: List? = null, + forkedFrom: Event? = null, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (TextNoteEvent) -> Unit, @@ -69,6 +70,7 @@ class TextNoteEvent( root = root, replyingTo = replyingTo, directMentions = directMentions, + forkedFrom = forkedFrom?.id, ), ) } @@ -93,6 +95,7 @@ class TextNoteEvent( root = root, replyingTo = replyingTo, directMentions = directMentions, + forkedFrom = (forkedFrom as? AddressableEvent)?.address()?.toTag(), ), ) } @@ -136,6 +139,7 @@ class TextNoteEvent( root: String?, replyingTo: String?, directMentions: Set, + forkedFrom: String?, ) = sortedWith { o1, o2 -> when { o1 == o2 -> 0 @@ -150,6 +154,7 @@ class TextNoteEvent( when (it) { root -> arrayOf(tagName, it, "", "root") replyingTo -> arrayOf(tagName, it, "", "reply") + forkedFrom -> arrayOf(tagName, it, "", "fork") in directMentions -> arrayOf(tagName, it, "", "mention") else -> arrayOf(tagName, it) } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/WikiNoteEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/WikiNoteEvent.kt index 25bb51454..a08bb9992 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/WikiNoteEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/WikiNoteEvent.kt @@ -41,16 +41,6 @@ class WikiNoteEvent( fun topics() = hashtags() - fun forkFromAddress() = - tags.firstOrNull { it.size > 3 && it[0] == "a" && it[3] == "fork" }?.let { - val aTagValue = it[1] - val relay = it.getOrNull(2) - - ATag.parse(aTagValue, relay) - } - - fun forkFromVersion() = tags.firstOrNull { it.size > 3 && it[0] == "e" && it[3] == "fork" }?.get(1) - fun title() = tags.firstOrNull { it.size > 1 && it[0] == "title" }?.get(1) fun summary() = tags.firstOrNull { it.size > 1 && it[0] == "summary" }?.get(1)