mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-06 03:38:23 +00:00
refactor(richtext): RichTextViewer moves to commonsUI over a platform slot
The shared rich-text core already asked a RichTextSegmentRenderer for every platform segment; what kept the viewer in the app was how a caller reached the app's renderer. That is now a slot: - RichTextPlatform (commonsUI): builds the segment renderer for a viewer call and renders markdown. LocalRichTextPlatform defaults to Plain, which renders those segments as text. - InlineQuoteRenderer and LocalInlineQuoteRenderer move to commonsUI with a plain default (a link to the quoted note). - The app provides AndroidRichTextPlatform (its existing AmethystRichTextSegmentRenderer and markdown renderer) and its quoted-note card in AmethystTheme, which wraps every Activity root. Chat bubbles still override the quote renderer locally. RichTextViewer (formerly delegating to CommonsBackedRichTextViewer, now folded into it), LoadNote, BechLink, TagLink, HashTag, the paragraph layout and ClickableRoute move to commonsUI commons.ui.components. The platform leaves (Blossom URI renderers, the zoomable-media wrapper, the legacy segment switchboards and the secret-emoji message that uses them) stay in the app in RichTextPlatformLeaves.kt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S7FuNBSKiyVecARSoE4B9P
This commit is contained in:
@@ -80,6 +80,7 @@ import com.vitorpamplona.amethyst.commons.resources.lightning_invoice
|
||||
import com.vitorpamplona.amethyst.commons.resources.what_s_on_your_mind
|
||||
import com.vitorpamplona.amethyst.commons.richtext.RichTextParser
|
||||
import com.vitorpamplona.amethyst.commons.service.upload.ui.StrippingFailureDialog
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.BechLink
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.OutlinedThinPaddingTextField
|
||||
import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
@@ -100,7 +101,6 @@ import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.service.playback.composable.VideoView
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromFiles
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromGallery
|
||||
import com.vitorpamplona.amethyst.ui.components.BechLink
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadUrlPreview
|
||||
import com.vitorpamplona.amethyst.ui.note.NoteCompose
|
||||
import com.vitorpamplona.amethyst.ui.note.creators.invoice.InvoiceRequest
|
||||
|
||||
+26
-67
@@ -21,22 +21,17 @@
|
||||
package com.vitorpamplona.amethyst.ui.components
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import com.vitorpamplona.amethyst.commons.model.ImmutableListOfLists
|
||||
import com.vitorpamplona.amethyst.commons.model.navigation.Route
|
||||
import com.vitorpamplona.amethyst.commons.richtext.BlossomUriSegment
|
||||
import com.vitorpamplona.amethyst.commons.richtext.BuzzInviteLinkSegment
|
||||
import com.vitorpamplona.amethyst.commons.richtext.CachedRichTextParser
|
||||
import com.vitorpamplona.amethyst.commons.richtext.CashuSegment
|
||||
import com.vitorpamplona.amethyst.commons.richtext.ClinkOfferSegment
|
||||
import com.vitorpamplona.amethyst.commons.richtext.ConcordInviteLinkSegment
|
||||
@@ -52,6 +47,7 @@ import com.vitorpamplona.amethyst.commons.richtext.RichTextViewerState
|
||||
import com.vitorpamplona.amethyst.commons.richtext.SecretEmoji
|
||||
import com.vitorpamplona.amethyst.commons.richtext.Segment
|
||||
import com.vitorpamplona.amethyst.commons.richtext.WithdrawSegment
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.BechLink
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.ClickableBuzzInviteLink
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.ClickableConcordInviteLink
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.ClickableEmail
|
||||
@@ -59,17 +55,16 @@ import com.vitorpamplona.amethyst.commons.ui.components.ClickablePhone
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.ClickableRelayGroupLink
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.ClickableRelayUrl
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.NowhereLinkCard
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.RichTextViewer
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.TagLink
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.richtext.LocalRichTextInteractions
|
||||
import com.vitorpamplona.amethyst.commons.ui.richtext.LocalRichTextSegmentRenderer
|
||||
import com.vitorpamplona.amethyst.commons.ui.richtext.RichTextInteractions
|
||||
import com.vitorpamplona.amethyst.commons.ui.richtext.RichTextPlatform
|
||||
import com.vitorpamplona.amethyst.commons.ui.richtext.RichTextSegmentRenderer
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.HalfVertPadding
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.markdown.RenderContentAsMarkdown
|
||||
import com.vitorpamplona.amethyst.ui.note.creators.invoice.ClinkOfferPreview
|
||||
import com.vitorpamplona.amethyst.ui.note.creators.invoice.MayBeInvoicePreview
|
||||
import com.vitorpamplona.amethyst.commons.ui.richtext.RichTextViewer as CommonsRichTextViewer
|
||||
|
||||
/**
|
||||
* The Android implementation of the shared [RichTextSegmentRenderer] contract: it
|
||||
@@ -79,7 +74,7 @@ import com.vitorpamplona.amethyst.commons.ui.richtext.RichTextViewer as CommonsR
|
||||
* leaves need but that the cross-platform contract deliberately keeps out of its
|
||||
* method signatures.
|
||||
*
|
||||
* Recreate one per [CommonsBackedRichTextViewer] call (it captures per-call state);
|
||||
* Recreate one per [RichTextViewer] call (it captures per-call state);
|
||||
* it is cheap and [remember]ed against its inputs so the CompositionLocal doesn't
|
||||
* churn its readers.
|
||||
*/
|
||||
@@ -255,63 +250,27 @@ class AmethystRichTextSegmentRenderer(
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders rich text through the shared cross-platform [CommonsRichTextViewer] core.
|
||||
* This is the production plain-text path that [RichTextViewer] delegates to: it
|
||||
* parses with the existing [CachedRichTextParser], keeps the markdown path native,
|
||||
* and for plain rich text provides the Android segment renderer
|
||||
* ([AmethystRichTextSegmentRenderer]) plus the universal interactions into the two
|
||||
* CompositionLocals the core reads.
|
||||
*
|
||||
* The core renders text, custom emoji, and hashtags (with their shared inline
|
||||
* icons) itself; every divergent segment routes back to Amethyst's existing
|
||||
* leaves through the renderer. Note: url/email/phone open via the platform URI
|
||||
* handler rather than Amethyst's per-type Clickable* composables, so their tap
|
||||
* behavior is the generic open action (rendering is unchanged clickable text).
|
||||
* Android's [RichTextPlatform]: its segment renderer ([AmethystRichTextSegmentRenderer]) and its
|
||||
* markdown renderer. Installed in the app theme, so every screen's rich text gets them.
|
||||
*/
|
||||
@Composable
|
||||
fun CommonsBackedRichTextViewer(
|
||||
content: String,
|
||||
canPreview: Boolean,
|
||||
quotesLeft: Int,
|
||||
modifier: Modifier,
|
||||
tags: ImmutableListOfLists<String>,
|
||||
backgroundColor: MutableState<Color>,
|
||||
callbackUri: String? = null,
|
||||
authorPubKey: String? = null,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
if (remember(content) { CachedRichTextParser.isMarkdown(content) }) {
|
||||
RenderContentAsMarkdown(content, tags, canPreview, quotesLeft, backgroundColor, callbackUri, accountViewModel, nav)
|
||||
return@Column
|
||||
}
|
||||
object AndroidRichTextPlatform : RichTextPlatform {
|
||||
override fun segmentRenderer(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
backgroundColor: MutableState<Color>,
|
||||
callbackUri: String?,
|
||||
canPreview: Boolean,
|
||||
): RichTextSegmentRenderer = AmethystRichTextSegmentRenderer(accountViewModel, nav, backgroundColor, callbackUri, canPreview)
|
||||
|
||||
val state by remember(content, tags) {
|
||||
mutableStateOf(CachedRichTextParser.parseText(content, tags, callbackUri, authorPubKey))
|
||||
}
|
||||
|
||||
val renderer =
|
||||
remember(accountViewModel, nav, backgroundColor, callbackUri, canPreview) {
|
||||
AmethystRichTextSegmentRenderer(accountViewModel, nav, backgroundColor, callbackUri, canPreview)
|
||||
}
|
||||
|
||||
val interactions =
|
||||
remember(nav) {
|
||||
RichTextInteractions(
|
||||
onClickHashtag = { nav.nav(Route.Hashtag(it.lowercase())) },
|
||||
)
|
||||
}
|
||||
|
||||
CompositionLocalProvider(
|
||||
LocalRichTextSegmentRenderer provides renderer,
|
||||
LocalRichTextInteractions provides interactions,
|
||||
) {
|
||||
CommonsRichTextViewer(
|
||||
state = state,
|
||||
canPreview = canPreview,
|
||||
quotesLeft = quotesLeft,
|
||||
)
|
||||
}
|
||||
}
|
||||
@Composable
|
||||
override fun Markdown(
|
||||
content: String,
|
||||
tags: ImmutableListOfLists<String>,
|
||||
canPreview: Boolean,
|
||||
quotesLeft: Int,
|
||||
backgroundColor: MutableState<Color>,
|
||||
callbackUri: String?,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) = RenderContentAsMarkdown(content, tags, canPreview, quotesLeft, backgroundColor, callbackUri, accountViewModel, nav)
|
||||
}
|
||||
|
||||
+1
@@ -43,6 +43,7 @@ import com.vitorpamplona.amethyst.commons.model.ImmutableListOfLists
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.show_more
|
||||
import com.vitorpamplona.amethyst.commons.richtext.ExpandableTextCutOffCalculator
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.RichTextViewer
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.ButtonBorder
|
||||
|
||||
+2
-36
@@ -21,44 +21,12 @@
|
||||
package com.vitorpamplona.amethyst.ui.components
|
||||
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import com.vitorpamplona.amethyst.commons.model.Note
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.InlineQuoteRenderer
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.innerPostModifier
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.note.NoteCompose
|
||||
import com.vitorpamplona.amethyst.ui.note.types.ReplyRenderType
|
||||
|
||||
/**
|
||||
* Strategy for rendering a note quoted inline in another note's text (a
|
||||
* `nostr:nevent1...`/`nostr:note1...` mention with preview budget left, or a
|
||||
* legacy `#[index]` event tag).
|
||||
*
|
||||
* Both rich-text paths (plain and markdown) funnel through [DisplayFullNote],
|
||||
* which reads [LocalInlineQuoteRenderer], so providing a different renderer
|
||||
* re-skins inline quotes for a whole subtree without threading a parameter
|
||||
* through the parser stack. Chat bubbles use this to keep a quoted chat
|
||||
* message in the chat reply design instead of the default quoted-note card
|
||||
* (see `chatInlineQuoteRenderer`).
|
||||
*
|
||||
* A renderer draws only the quoted note itself; any leftover characters
|
||||
* around the mention stay with the caller ([DisplayFullNote]).
|
||||
*/
|
||||
fun interface InlineQuoteRenderer {
|
||||
@Composable
|
||||
fun Render(
|
||||
note: Note,
|
||||
quotesLeft: Int,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
)
|
||||
}
|
||||
|
||||
/** The quoted-note card used everywhere outside chats. */
|
||||
/** The quoted-note card used everywhere outside chats; the app installs it in its theme root. */
|
||||
val DefaultInlineQuoteRenderer =
|
||||
InlineQuoteRenderer { note, quotesLeft, backgroundColor, accountViewModel, nav ->
|
||||
NoteCompose(
|
||||
@@ -72,5 +40,3 @@ val DefaultInlineQuoteRenderer =
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
|
||||
val LocalInlineQuoteRenderer = compositionLocalOf { DefaultInlineQuoteRenderer }
|
||||
|
||||
+8
-416
@@ -22,21 +22,13 @@ package com.vitorpamplona.amethyst.ui.components
|
||||
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.text.InlineTextContent
|
||||
import androidx.compose.foundation.text.appendInlineContent
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -48,32 +40,14 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalFontFamilyResolver
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.TextMeasurer
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.em
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.commons.emojicoder.EmojiCoder
|
||||
import com.vitorpamplona.amethyst.commons.model.EmptyTagList
|
||||
import com.vitorpamplona.amethyst.commons.model.ImmutableListOfLists
|
||||
import com.vitorpamplona.amethyst.commons.model.Note
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
|
||||
import com.vitorpamplona.amethyst.commons.model.navigation.Route
|
||||
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.user.UserFinderFilterAssemblerSubscription
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.user.observeUserDisplayNickname
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.user.observeUserInfo
|
||||
import com.vitorpamplona.amethyst.commons.richtext.Base64Segment
|
||||
import com.vitorpamplona.amethyst.commons.richtext.BechSegment
|
||||
import com.vitorpamplona.amethyst.commons.richtext.BlossomUriSegment
|
||||
@@ -92,7 +66,6 @@ import com.vitorpamplona.amethyst.commons.richtext.InvoiceSegment
|
||||
import com.vitorpamplona.amethyst.commons.richtext.LinkSegment
|
||||
import com.vitorpamplona.amethyst.commons.richtext.MathSegment
|
||||
import com.vitorpamplona.amethyst.commons.richtext.NowhereLinkSegment
|
||||
import com.vitorpamplona.amethyst.commons.richtext.ParagraphState
|
||||
import com.vitorpamplona.amethyst.commons.richtext.PdfSegment
|
||||
import com.vitorpamplona.amethyst.commons.richtext.PhoneSegment
|
||||
import com.vitorpamplona.amethyst.commons.richtext.RegularTextSegment
|
||||
@@ -105,6 +78,7 @@ import com.vitorpamplona.amethyst.commons.richtext.Segment
|
||||
import com.vitorpamplona.amethyst.commons.richtext.VideoSegment
|
||||
import com.vitorpamplona.amethyst.commons.richtext.WithdrawSegment
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.AnimatedBorderTextCornerRadius
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.BechLink
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.ClickableBuzzInviteLink
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.ClickableConcordInviteLink
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.ClickableEmail
|
||||
@@ -112,63 +86,27 @@ import com.vitorpamplona.amethyst.commons.ui.components.ClickablePhone
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.ClickableRelayGroupLink
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.ClickableRelayUrl
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.ClickableTextPrimary
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.CreateClickableText
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.HashTag
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.NowhereLinkCard
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.RenderCustomEmoji
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.RenderRegular
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.RenderTextParagraph
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.TagLink
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.measureSpaceWidth
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.EmptyNav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.routes.routeFor
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.LoadUser
|
||||
import com.vitorpamplona.amethyst.commons.ui.richtext.CreateClickableTextWithEmoji
|
||||
import com.vitorpamplona.amethyst.commons.ui.richtext.CreateTextWithEmoji
|
||||
import com.vitorpamplona.amethyst.commons.ui.richtext.HashtagIcon
|
||||
import com.vitorpamplona.amethyst.commons.ui.richtext.checkForHashtagWithIcon
|
||||
import com.vitorpamplona.amethyst.commons.ui.state.produceCachedState
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.CashuCardBorders
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.HalfVertPadding
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.inlinePlaceholder
|
||||
import com.vitorpamplona.amethyst.commons.util.toShortDisplay
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.service.uploads.blossom.bud10.openBlossomUriAsIntent
|
||||
import com.vitorpamplona.amethyst.ui.note.creators.invoice.ClinkOfferPreview
|
||||
import com.vitorpamplona.amethyst.ui.note.creators.invoice.MayBeInvoicePreview
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
|
||||
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
||||
import com.vitorpamplona.quartz.nipB7Blossom.BlossomUri
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* Renders Nostr rich text. The plain (non-markdown) path now runs through the
|
||||
* cross-platform [com.vitorpamplona.amethyst.commons.ui.richtext.RichTextViewer]
|
||||
* core via [CommonsBackedRichTextViewer]; this entry is kept so its ~55 call sites
|
||||
* are untouched. The Android-specific markdown path stays here.
|
||||
*/
|
||||
@Composable
|
||||
fun RichTextViewer(
|
||||
content: String,
|
||||
canPreview: Boolean,
|
||||
quotesLeft: Int,
|
||||
modifier: Modifier,
|
||||
tags: ImmutableListOfLists<String>,
|
||||
backgroundColor: MutableState<Color>,
|
||||
callbackUri: String? = null,
|
||||
authorPubKey: String? = null,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) = CommonsBackedRichTextViewer(
|
||||
content = content,
|
||||
canPreview = canPreview,
|
||||
quotesLeft = quotesLeft,
|
||||
modifier = modifier,
|
||||
tags = tags,
|
||||
backgroundColor = backgroundColor,
|
||||
callbackUri = callbackUri,
|
||||
authorPubKey = authorPubKey,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun RenderStrangeNamePreview() {
|
||||
@@ -356,88 +294,6 @@ fun RenderRegularPreview3() {
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RenderTextParagraph(
|
||||
paragraph: ParagraphState,
|
||||
spaceWidth: Dp,
|
||||
modifier: Modifier,
|
||||
renderWord: @Composable (word: Segment) -> Unit,
|
||||
) {
|
||||
FlowRow(
|
||||
modifier = modifier,
|
||||
horizontalArrangement = Arrangement.spacedBy(spaceWidth),
|
||||
// Center items on the cross axis so a taller item (an equation, whose image
|
||||
// is taller than a text line) sits centered on the line instead of hanging
|
||||
// below the baseline. No-op for the common all-text row.
|
||||
itemVerticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
paragraph.words.forEach { word ->
|
||||
renderWord(word)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
fun RenderRegular(
|
||||
content: String,
|
||||
tags: ImmutableListOfLists<String>,
|
||||
callbackUri: String? = null,
|
||||
authorPubKey: String? = null,
|
||||
renderParagraph: @Composable (ParagraphState, state: RichTextViewerState, Dp, modifier: Modifier) -> Unit,
|
||||
) {
|
||||
val state by remember(content, tags) { mutableStateOf(CachedRichTextParser.parseText(content, tags, callbackUri, authorPubKey)) }
|
||||
|
||||
val spaceWidth = measureSpaceWidth(LocalTextStyle.current)
|
||||
|
||||
val currentTextStyle = LocalTextStyle.current
|
||||
|
||||
val textStyle =
|
||||
remember(currentTextStyle) {
|
||||
currentTextStyle.copy(
|
||||
lineHeight = 1.3.em,
|
||||
)
|
||||
}
|
||||
|
||||
Column {
|
||||
// FlowRow doesn't work well with paragraphs. So we need to split them
|
||||
state.paragraphs.forEach { paragraph ->
|
||||
CompositionLocalProvider(
|
||||
LocalLayoutDirection provides
|
||||
if (paragraph.isRTL) {
|
||||
LayoutDirection.Rtl
|
||||
} else {
|
||||
LayoutDirection.Ltr
|
||||
},
|
||||
LocalTextStyle provides textStyle,
|
||||
) {
|
||||
renderParagraph(
|
||||
paragraph,
|
||||
state,
|
||||
spaceWidth,
|
||||
Modifier.align(if (paragraph.isRTL) Alignment.End else Alignment.Start),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun measureSpaceWidth(textStyle: TextStyle): Dp {
|
||||
val fontFamilyResolver = LocalFontFamilyResolver.current
|
||||
val density = LocalDensity.current
|
||||
val layoutDirection = LocalLayoutDirection.current
|
||||
|
||||
return remember(fontFamilyResolver, density, layoutDirection, textStyle) {
|
||||
val widthPx =
|
||||
TextMeasurer(fontFamilyResolver, density, layoutDirection, 1)
|
||||
.measure(" ", textStyle)
|
||||
.size
|
||||
.width
|
||||
with(density) { widthPx.toDp() }
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RenderWordWithoutPreview(
|
||||
word: Segment,
|
||||
@@ -640,76 +496,6 @@ private fun NoProtocolUrlRenderer(url: String) {
|
||||
ClickableUrl(url, "https://$url")
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RenderCustomEmoji(
|
||||
word: String,
|
||||
state: RichTextViewerState,
|
||||
) {
|
||||
CreateTextWithEmoji(
|
||||
text = word,
|
||||
emojis = state.customEmoji,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BechLink(
|
||||
word: String,
|
||||
canPreview: Boolean,
|
||||
quotesLeft: Int,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val loadedLink by produceCachedState(cache = accountViewModel.bechLinkCache, key = word)
|
||||
|
||||
val baseNote = loadedLink?.baseNote
|
||||
|
||||
// Preload the placeholder's author NIP-65 outbox so a missing quoted event can be fetched from there.
|
||||
baseNote?.author?.let { author ->
|
||||
UserFinderFilterAssemblerSubscription(author, accountViewModel)
|
||||
}
|
||||
|
||||
if (canPreview && quotesLeft > 0 && baseNote != null) {
|
||||
Row {
|
||||
DisplayFullNote(
|
||||
note = baseNote,
|
||||
extraChars = loadedLink?.nip19?.additionalChars?.ifBlank { null },
|
||||
quotesLeft = quotesLeft,
|
||||
backgroundColor = backgroundColor,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
} else if (loadedLink?.nip19 != null) {
|
||||
ClickableRoute(word, loadedLink?.nip19!!, accountViewModel, nav)
|
||||
} else {
|
||||
val text =
|
||||
remember(word) {
|
||||
if (word.length > 16) {
|
||||
word.replaceRange(8, word.length - 8, ":")
|
||||
} else {
|
||||
word
|
||||
}
|
||||
}
|
||||
|
||||
Text(text = text, maxLines = 1)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DisplayFullNote(
|
||||
note: Note,
|
||||
extraChars: String?,
|
||||
quotesLeft: Int,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
LocalInlineQuoteRenderer.current.Render(note, quotesLeft, backgroundColor, accountViewModel, nav)
|
||||
|
||||
extraChars?.let { Text(it) }
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DisplaySecretEmoji(
|
||||
segment: SecretEmoji,
|
||||
@@ -803,197 +589,3 @@ fun CoreSecretMessage(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HashTag(
|
||||
segment: HashTagSegment,
|
||||
nav: INav,
|
||||
) {
|
||||
val primary = MaterialTheme.colorScheme.primary
|
||||
val background = MaterialTheme.colorScheme.onBackground
|
||||
val hashtagIcon: HashtagIcon? = checkForHashtagWithIcon(segment.hashtag)
|
||||
|
||||
val annotatedTermsString =
|
||||
remember(segment.segmentText) {
|
||||
buildAnnotatedString {
|
||||
withStyle(SpanStyle(color = primary)) {
|
||||
pushStringAnnotation("routeToHashtag", "")
|
||||
append("#${segment.hashtag}")
|
||||
pop()
|
||||
}
|
||||
|
||||
if (hashtagIcon != null) {
|
||||
withStyle(SpanStyle(color = primary)) {
|
||||
pushStringAnnotation("routeToHashtag", "")
|
||||
appendInlineContent("inlineContent", "[icon]")
|
||||
pop()
|
||||
}
|
||||
}
|
||||
|
||||
segment.extras?.let { withStyle(SpanStyle(color = background)) { append(it) } }
|
||||
}
|
||||
}
|
||||
|
||||
Text(
|
||||
text = annotatedTermsString,
|
||||
modifier =
|
||||
remember {
|
||||
Modifier.clickable {
|
||||
nav.nav(Route.Hashtag(segment.hashtag.lowercase()))
|
||||
}
|
||||
},
|
||||
inlineContent =
|
||||
if (hashtagIcon != null) {
|
||||
mapOf("inlineContent" to inlineIcon(hashtagIcon))
|
||||
} else {
|
||||
emptyMap()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun inlineIcon(hashtagIcon: HashtagIcon) =
|
||||
InlineTextContent(inlinePlaceholder) {
|
||||
Icon(
|
||||
imageVector = hashtagIcon.icon,
|
||||
contentDescription = hashtagIcon.description,
|
||||
tint = Color.Unspecified,
|
||||
modifier = hashtagIcon.modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TagLink(
|
||||
word: HashIndexUserSegment,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
LoadUser(baseUserHex = word.hex) {
|
||||
if (it == null) {
|
||||
Text(text = word.segmentText)
|
||||
} else {
|
||||
Row {
|
||||
DisplayUserFromTag(it, accountViewModel, nav)
|
||||
word.extras?.let { it2 ->
|
||||
Text(text = it2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LoadNote(
|
||||
baseNoteHex: String,
|
||||
content: @Composable (Note?) -> Unit,
|
||||
) {
|
||||
var note by
|
||||
remember(baseNoteHex) { mutableStateOf(LocalCache.getNoteIfExists(baseNoteHex)) }
|
||||
|
||||
if (note == null) {
|
||||
LaunchedEffect(key1 = baseNoteHex) {
|
||||
note = LocalCache.checkGetOrCreateNote(baseNoteHex)
|
||||
}
|
||||
}
|
||||
|
||||
content(note)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TagLink(
|
||||
word: HashIndexEventSegment,
|
||||
canPreview: Boolean,
|
||||
quotesLeft: Int,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
LoadNote(baseNoteHex = word.hex) {
|
||||
if (it == null) {
|
||||
Text(text = remember { word.segmentText.toShortDisplay() })
|
||||
} else {
|
||||
Row {
|
||||
DisplayNoteFromTag(
|
||||
it,
|
||||
word.extras,
|
||||
canPreview,
|
||||
quotesLeft,
|
||||
accountViewModel,
|
||||
backgroundColor,
|
||||
nav,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun DisplayNoteFromTagPreview() {
|
||||
val dummyPost =
|
||||
TextNoteEvent(
|
||||
id = "0b6d941c46411a95edb1c93da7ad6ca26370497d8c7b7d621f5cb59f48841bad",
|
||||
pubKey = "6dd3b72e325da7383b275eef1c66131ba4664326e162bc060527509b4e33ae43",
|
||||
createdAt = 1753988264,
|
||||
tags = emptyArray(),
|
||||
content = "test",
|
||||
sig = "ec39e60722a083cccbd2d82d2827e13f5499fa7cbcedac5b76011a844c077473adb629d50d01fab147835ac6c8a3d5ba9aaddd87d6723f0c3c864b9119fc4356",
|
||||
)
|
||||
|
||||
LocalCache.justConsume(dummyPost, null, true)
|
||||
val note = LocalCache.getOrCreateNote(dummyPost.id)
|
||||
|
||||
ThemeComparisonColumn(
|
||||
toPreview = {
|
||||
ClickableTextPrimary(
|
||||
text = "@${note.idNote().toShortDisplay()}",
|
||||
onClick = { },
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DisplayNoteFromTag(
|
||||
baseNote: Note,
|
||||
addedChars: String?,
|
||||
canPreview: Boolean,
|
||||
quotesLeft: Int,
|
||||
accountViewModel: AccountViewModel,
|
||||
backgroundColor: MutableState<Color>,
|
||||
nav: INav,
|
||||
) {
|
||||
if (canPreview && quotesLeft > 0) {
|
||||
LocalInlineQuoteRenderer.current.Render(baseNote, quotesLeft, backgroundColor, accountViewModel, nav)
|
||||
} else {
|
||||
ClickableTextPrimary(
|
||||
text = "@${baseNote.idNote().toShortDisplay()}",
|
||||
onClick = { routeFor(baseNote, accountViewModel.account)?.let { nav.nav(it) } },
|
||||
)
|
||||
}
|
||||
|
||||
addedChars?.ifBlank { null }?.let { Text(text = it) }
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DisplayUserFromTag(
|
||||
baseUser: User,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val meta by observeUserInfo(baseUser, accountViewModel)
|
||||
val nickname by observeUserDisplayNickname(baseUser, accountViewModel)
|
||||
val petName = nickname?.petName
|
||||
|
||||
CrossfadeIfEnabled(targetState = meta, label = "DisplayUserFromTag") {
|
||||
Row {
|
||||
CreateClickableTextWithEmoji(
|
||||
clickablePart = remember(meta, petName) { petName ?: it?.info?.bestName() ?: baseUser.pubkeyDisplayHex() },
|
||||
maxLines = 1,
|
||||
route = remember(baseUser) { routeFor(baseUser) },
|
||||
nav = nav,
|
||||
tags = if (petName != null) nickname?.tags else it?.tags,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -36,6 +36,8 @@ import com.halilibo.richtext.ui.string.RichTextString
|
||||
import com.vitorpamplona.amethyst.commons.model.Note
|
||||
import com.vitorpamplona.amethyst.commons.richtext.MediaUrlImage
|
||||
import com.vitorpamplona.amethyst.commons.richtext.RichTextParser
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.DisplayFullNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.DisplayUser
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.richtext.HashtagIcon
|
||||
import com.vitorpamplona.amethyst.commons.ui.richtext.checkForHashtagWithIcon
|
||||
@@ -44,8 +46,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.Size17Modifier
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.LoadedBechLink
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.EventFinderFilterAssemblerSubscription
|
||||
import com.vitorpamplona.amethyst.ui.components.DisplayFullNote
|
||||
import com.vitorpamplona.amethyst.ui.components.DisplayUser
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadUrlPreview
|
||||
import com.vitorpamplona.amethyst.ui.components.ZoomableContentView
|
||||
import com.vitorpamplona.amethyst.ui.fragmentHashtagOrNull
|
||||
|
||||
+1
-1
@@ -61,6 +61,7 @@ import com.vitorpamplona.amethyst.commons.resources.nip05_failed
|
||||
import com.vitorpamplona.amethyst.commons.resources.nip05_verified
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.ClickableTextPrimary
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.routes.routeFor
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.LoadAddressableNote
|
||||
@@ -78,7 +79,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.nip05
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.EventFinderFilterAssemblerSubscription
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.painterRes
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.aTag.firstTaggedAddress
|
||||
|
||||
@@ -40,6 +40,7 @@ import com.vitorpamplona.amethyst.commons.relayClient.user.observeUserInfo
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.and
|
||||
import com.vitorpamplona.amethyst.commons.resources.replying_to
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.CreateClickableTextWithEmoji
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.routes.routeFor
|
||||
import com.vitorpamplona.amethyst.commons.ui.richtext.CreateClickableTextWithEmoji
|
||||
@@ -48,7 +49,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.StdVertSpacer
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.lessImportantLink
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.CreateClickableTextWithEmoji
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
|
||||
@@ -30,13 +30,13 @@ import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.event.observeNote
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.forked_from
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.routes.routeFor
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.LoadAddressableNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.QuietMark
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.quartz.experimental.forks.IForkableEvent
|
||||
|
||||
@Composable
|
||||
|
||||
+1
-1
@@ -90,6 +90,7 @@ import com.vitorpamplona.amethyst.commons.resources.share_as_image
|
||||
import com.vitorpamplona.amethyst.commons.resources.share_as_image_generating
|
||||
import com.vitorpamplona.amethyst.commons.resources.share_as_image_url
|
||||
import com.vitorpamplona.amethyst.commons.resources.share_as_image_watermark
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.TextSpinner
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.TitleExplainer
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
@@ -101,7 +102,6 @@ import com.vitorpamplona.amethyst.service.uploads.CompressorQuality
|
||||
import com.vitorpamplona.amethyst.service.uploads.UploadOrchestrator
|
||||
import com.vitorpamplona.amethyst.service.uploads.UploadingState
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.UploadProgressIndicator
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.note.NoteCompose
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
+1
-1
@@ -58,11 +58,11 @@ import com.vitorpamplona.amethyst.commons.resources.share_as_qr_hint_nostr
|
||||
import com.vitorpamplona.amethyst.commons.resources.share_as_qr_hint_web
|
||||
import com.vitorpamplona.amethyst.commons.resources.share_as_qr_mode_nostr
|
||||
import com.vitorpamplona.amethyst.commons.resources.share_as_qr_mode_web
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarWithBackButton
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.qrcode.KeepScreenBrightAndAwake
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.qrcode.QrCodeDrawer
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ import com.vitorpamplona.amethyst.commons.resources.attestation_valid_to
|
||||
import com.vitorpamplona.amethyst.commons.resources.attestor_proficiency
|
||||
import com.vitorpamplona.amethyst.commons.resources.attestor_recommendation
|
||||
import com.vitorpamplona.amethyst.commons.resources.attestor_recommendation_for_kinds
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.EmptyNav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.LoadAddressableNote
|
||||
@@ -78,7 +79,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.DoubleVertSpacer
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.replyModifier
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
import com.vitorpamplona.amethyst.ui.note.NoteCompose
|
||||
import com.vitorpamplona.amethyst.ui.note.UserCompose
|
||||
|
||||
@@ -78,6 +78,7 @@ import com.vitorpamplona.amethyst.commons.resources.preview_card_image_for
|
||||
import com.vitorpamplona.amethyst.commons.resources.profile_image
|
||||
import com.vitorpamplona.amethyst.commons.resources.quick_action_share
|
||||
import com.vitorpamplona.amethyst.commons.resources.quick_action_share_browser_link
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.RichTextViewer
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.routes.routeFor
|
||||
@@ -102,7 +103,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.innerPostModifier
|
||||
import com.vitorpamplona.amethyst.commons.util.equalImmutableLists
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.MyAsyncImage
|
||||
import com.vitorpamplona.amethyst.ui.components.RichTextViewer
|
||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
import com.vitorpamplona.amethyst.ui.note.LikeReaction
|
||||
import com.vitorpamplona.amethyst.ui.note.ZapReaction
|
||||
|
||||
@@ -64,6 +64,7 @@ import com.vitorpamplona.amethyst.commons.relayClient.user.observeUserInfo
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.preview_card_image_for
|
||||
import com.vitorpamplona.amethyst.commons.resources.rating_stars_out_of_five
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.routes.routeFor
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.LoadAddressableNote
|
||||
@@ -75,7 +76,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.Size5dp
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.StdHorzSpacer
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.components.MyAsyncImage
|
||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
import com.vitorpamplona.quartz.experimental.publications.PublicationIndexEvent
|
||||
|
||||
@@ -32,8 +32,8 @@ import com.vitorpamplona.amethyst.commons.richtext.BaseMediaContent
|
||||
import com.vitorpamplona.amethyst.commons.richtext.MediaLocalImage
|
||||
import com.vitorpamplona.amethyst.commons.richtext.MediaLocalVideo
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.components.SensitivityWarning
|
||||
import com.vitorpamplona.amethyst.ui.components.ZoomableContentView
|
||||
import com.vitorpamplona.quartz.experimental.nip95.header.FileStorageHeaderEvent
|
||||
|
||||
@@ -54,6 +54,9 @@ import com.vitorpamplona.amethyst.commons.model.toImmutableListOfLists
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.event.observeNote
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.user.observeUserInfo
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.ClickableTextPrimary
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.CreateClickableTextWithEmoji
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.RenderUserAsClickableText
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.measureSpaceWidth
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.EmptyNav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.routes.routeFor
|
||||
@@ -64,10 +67,7 @@ import com.vitorpamplona.amethyst.commons.ui.richtext.CreateClickableTextWithEmo
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.ClickableUrl
|
||||
import com.vitorpamplona.amethyst.ui.components.CreateClickableTextWithEmoji
|
||||
import com.vitorpamplona.amethyst.ui.components.RenderUserAsClickableText
|
||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
import com.vitorpamplona.amethyst.ui.components.measureSpaceWidth
|
||||
import com.vitorpamplona.amethyst.ui.components.rememberTranslation
|
||||
import com.vitorpamplona.amethyst.ui.note.nip22Comments.DisplayExternalId
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
|
||||
|
||||
@@ -73,6 +73,7 @@ import com.vitorpamplona.amethyst.commons.resources.music_playlist_private
|
||||
import com.vitorpamplona.amethyst.commons.resources.music_playlist_track_count
|
||||
import com.vitorpamplona.amethyst.commons.resources.music_playlist_unknown_track
|
||||
import com.vitorpamplona.amethyst.commons.resources.preview_card_image_for
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.EmptyNav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.BaseUserPicture
|
||||
@@ -86,7 +87,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.grayText
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.replyModifier
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.components.MyAsyncImage
|
||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.DefaultImageBanner
|
||||
|
||||
@@ -61,6 +61,7 @@ import com.vitorpamplona.amethyst.commons.model.toImmutableListOfLists
|
||||
import com.vitorpamplona.amethyst.commons.music.formatTrackDuration
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.preview_card_image_for
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.EmptyNav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
@@ -76,7 +77,6 @@ import com.vitorpamplona.amethyst.service.playback.composable.PauseControllerWhe
|
||||
import com.vitorpamplona.amethyst.service.playback.composable.VideoView
|
||||
import com.vitorpamplona.amethyst.service.playback.composable.mediaitem.GetMediaItem
|
||||
import com.vitorpamplona.amethyst.service.playback.composable.wavefront.syntheticWaveformFor
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.components.MyAsyncImage
|
||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.DefaultImageHeader
|
||||
|
||||
@@ -35,11 +35,11 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import com.vitorpamplona.amethyst.commons.model.Note
|
||||
import com.vitorpamplona.amethyst.commons.model.toImmutableListOfLists
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.RenderUserAsClickableText
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.HalfHalfTopPadding
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.RenderUserAsClickableText
|
||||
import com.vitorpamplona.amethyst.ui.components.SensitivityWarning
|
||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.DisplayUncitedHashtags
|
||||
|
||||
@@ -63,6 +63,7 @@ import com.vitorpamplona.amethyst.commons.resources.publication_contents
|
||||
import com.vitorpamplona.amethyst.commons.resources.publication_more_sections
|
||||
import com.vitorpamplona.amethyst.commons.resources.publication_section_count
|
||||
import com.vitorpamplona.amethyst.commons.resources.publication_untitled_section
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.LoadAddressableNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.pluralStringRes
|
||||
@@ -70,7 +71,6 @@ import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size5dp
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.grayText
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.components.MyAsyncImage
|
||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
import com.vitorpamplona.quartz.experimental.library.BlossomPieceIndexEvent
|
||||
|
||||
+1
-1
@@ -52,6 +52,7 @@ import com.vitorpamplona.amethyst.commons.relayClient.event.observeNoteEvent
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.publication_untitled_section
|
||||
import com.vitorpamplona.amethyst.commons.richtext.CachedAsciiDocToMarkdown
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.routes.routeFor
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.LoadAddressableNote
|
||||
@@ -60,7 +61,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.Size10dp
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size5dp
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.grayText
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.components.markdown.RenderContentAsMarkdown
|
||||
import com.vitorpamplona.quartz.experimental.publications.PublicationContentEvent
|
||||
import com.vitorpamplona.quartz.experimental.publications.PublicationIndexEvent
|
||||
|
||||
+1
-1
@@ -48,6 +48,7 @@ import com.vitorpamplona.amethyst.commons.model.navigation.Route
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.video_curation_set_private_items
|
||||
import com.vitorpamplona.amethyst.commons.resources.video_curation_set_video_count
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.LoadAddressableNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.pluralStringRes
|
||||
@@ -55,7 +56,6 @@ import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.imageModifier
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.DisplayBlurHash
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.components.MyAsyncImage
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.AddressBookmark
|
||||
|
||||
@@ -22,9 +22,9 @@ package com.vitorpamplona.amethyst.ui.note.types
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.LoadAddressableNote
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.quartz.cyberspace.deck0003Sno.SnoPalette
|
||||
import com.vitorpamplona.quartz.cyberspace.deck0003Sno.SnoPaletteEventReader
|
||||
import com.vitorpamplona.quartz.cyberspace.deck0003Sno.SnoPaletteRef
|
||||
|
||||
@@ -52,6 +52,7 @@ import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.accept_the_suggestion
|
||||
import com.vitorpamplona.amethyst.commons.resources.proposal_to_edit
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.GenericLoadable
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.routes.routeFor
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
@@ -61,7 +62,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.imageModifier
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.innerPostModifier
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.actions.EditPostView
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
import com.vitorpamplona.amethyst.ui.note.NoteBody
|
||||
import com.vitorpamplona.amethyst.ui.note.observeEdits
|
||||
|
||||
@@ -51,6 +51,7 @@ import com.vitorpamplona.amethyst.commons.relayClient.event.observeNoteEvent
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.torrent_file
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.GenericLoadable
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.EmptyNav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.routes.routeFor
|
||||
@@ -60,7 +61,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.replyModifier
|
||||
import com.vitorpamplona.amethyst.commons.util.countToHumanReadableBytes
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
|
||||
import com.vitorpamplona.quartz.nip35Torrents.TorrentCommentEvent
|
||||
import com.vitorpamplona.quartz.nip35Torrents.TorrentEvent
|
||||
|
||||
+1
-1
@@ -46,13 +46,13 @@ import com.vitorpamplona.amethyst.commons.model.Note
|
||||
import com.vitorpamplona.amethyst.commons.model.navigation.Route
|
||||
import com.vitorpamplona.amethyst.commons.model.toImmutableListOfLists
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.event.observeNoteEvent
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.LoadAddressableNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size5dp
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.grayText
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.replyModifier
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||
import com.vitorpamplona.quartz.nip54Wiki.WikiArticleEvent
|
||||
|
||||
+1
-1
@@ -34,12 +34,12 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.commons.model.navigation.Route
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.LoadAddressableNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.LoadUser
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.note.NoteCompose
|
||||
import com.vitorpamplona.amethyst.ui.note.UserCompose
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||
|
||||
@@ -29,10 +29,10 @@ import androidx.compose.ui.graphics.Color
|
||||
import com.vitorpamplona.amethyst.commons.model.Note
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.kind_mute_list
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.note.NoteCompose
|
||||
import com.vitorpamplona.quartz.nip51Lists.muteList.MuteListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.muteList.tags.EventTag
|
||||
|
||||
+1
-1
@@ -131,6 +131,7 @@ import com.vitorpamplona.amethyst.commons.resources.backup_review_unmuted
|
||||
import com.vitorpamplona.amethyst.commons.resources.backup_review_unmuted_explainer
|
||||
import com.vitorpamplona.amethyst.commons.resources.backup_review_was_named
|
||||
import com.vitorpamplona.amethyst.commons.resources.backup_review_words_and_hashtags
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.routes.routeFor
|
||||
@@ -142,7 +143,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
|
||||
import com.vitorpamplona.amethyst.commons.util.toShortDisplay
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.channel.observeChannel
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.components.MyAsyncImage
|
||||
import com.vitorpamplona.amethyst.ui.note.NoteCompose
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.BannerImage
|
||||
|
||||
+1
-1
@@ -87,6 +87,7 @@ import com.vitorpamplona.amethyst.commons.resources.backup_conflict_restore_mine
|
||||
import com.vitorpamplona.amethyst.commons.resources.backup_review_changed_by_other_app
|
||||
import com.vitorpamplona.amethyst.commons.resources.backup_review_title_cleared
|
||||
import com.vitorpamplona.amethyst.commons.resources.backup_review_title_updated
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.routes.routeFor
|
||||
@@ -102,7 +103,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
|
||||
import com.vitorpamplona.amethyst.commons.util.toShortDisplay
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.channel.observeChannel
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.note.NoteCompose
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.quartz.experimental.ephemChat.list.EphemeralChatListDiff
|
||||
|
||||
+1
-1
@@ -42,13 +42,13 @@ import com.vitorpamplona.amethyst.commons.nip51Lists.ui.BookmarkGroupManagementI
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.bookmarks_title
|
||||
import com.vitorpamplona.amethyst.commons.resources.post_bookmark_management_title
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.bottombars.FabBottomBarPadded
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarWithBackButton
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.list.NewListButton
|
||||
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.EventBookmark
|
||||
|
||||
|
||||
+1
-1
@@ -21,8 +21,8 @@
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.feed
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.Note
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.InlineQuoteRenderer
|
||||
import com.vitorpamplona.amethyst.ui.components.DefaultInlineQuoteRenderer
|
||||
import com.vitorpamplona.amethyst.ui.components.InlineQuoteRenderer
|
||||
import com.vitorpamplona.amethyst.ui.note.WatchNoteEvent
|
||||
import com.vitorpamplona.quartz.experimental.ephemChat.chat.EphemeralChatEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
|
||||
+1
-1
@@ -53,12 +53,12 @@ import com.vitorpamplona.amethyst.commons.model.latestBuzzEdit
|
||||
import com.vitorpamplona.amethyst.commons.model.latestConcordEdit
|
||||
import com.vitorpamplona.amethyst.commons.model.latestMarmotEdit
|
||||
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LocalInlineQuoteRenderer
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.routes.routeFor
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.ReactionRowZapraiser
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.StdVertSpacer
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.LocalInlineQuoteRenderer
|
||||
import com.vitorpamplona.amethyst.ui.note.RenderZapRaiser
|
||||
import com.vitorpamplona.amethyst.ui.note.WatchBlockAndReport
|
||||
import com.vitorpamplona.amethyst.ui.note.WatchNoteEvent
|
||||
|
||||
+1
-1
@@ -51,6 +51,7 @@ import com.vitorpamplona.amethyst.commons.resources.channel_image
|
||||
import com.vitorpamplona.amethyst.commons.resources.created_at
|
||||
import com.vitorpamplona.amethyst.commons.resources.groups_no_descriptor
|
||||
import com.vitorpamplona.amethyst.commons.resources.owner
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.NoteAuthorPicture
|
||||
@@ -65,7 +66,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.StdVertSpacer
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.largeProfilePictureModifier
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.channel.observeChannel
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.MoreOptionsButton
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip28PublicChat.header.actions.LinkChatButton
|
||||
|
||||
+1
-1
@@ -42,6 +42,7 @@ import com.vitorpamplona.amethyst.commons.model.nip28PublicChats.PublicChatChann
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.user.observeUserIsFollowingChannel
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.profile_image
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.RobohashFallbackAsyncImage
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.LocalDisplaySettings
|
||||
@@ -52,7 +53,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.Size35dp
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.StdHorzSpacer
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.channel.observeChannel
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.note.LikeReaction
|
||||
import com.vitorpamplona.amethyst.ui.note.ZapReaction
|
||||
|
||||
|
||||
+1
-1
@@ -39,13 +39,13 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.commons.model.Note
|
||||
import com.vitorpamplona.amethyst.commons.model.nip53LiveActivities.LiveActivitiesChannel
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size20Modifier
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size20dp
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.StdHorzSpacer
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.note.ZapReaction
|
||||
import com.vitorpamplona.amethyst.ui.note.types.GoalProgressBar
|
||||
import com.vitorpamplona.quartz.nip75ZapGoals.ZapGoalEvent
|
||||
|
||||
+1
-1
@@ -30,12 +30,12 @@ import com.vitorpamplona.amethyst.commons.model.Note
|
||||
import com.vitorpamplona.amethyst.commons.model.navigation.Route
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.new_community_note
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size26Modifier
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size55Modifier
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.painterRes
|
||||
|
||||
@Composable
|
||||
|
||||
+1
-1
@@ -39,12 +39,12 @@ import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.contact_list_screen_title
|
||||
import com.vitorpamplona.amethyst.commons.resources.contact_list_screen_title_with_count
|
||||
import com.vitorpamplona.amethyst.commons.resources.fetching_follow_list
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarWithBackButton
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.note.UserCompose
|
||||
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
+1
-1
@@ -66,6 +66,7 @@ import com.vitorpamplona.amethyst.commons.resources.nwc_payment_request
|
||||
import com.vitorpamplona.amethyst.commons.resources.wallet_connect_pay_invoice_error_error
|
||||
import com.vitorpamplona.amethyst.commons.service.nwc.nwcFailureDetail
|
||||
import com.vitorpamplona.amethyst.commons.service.nwc.nwcTimeoutMessage
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedEmpty
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.RefresheableBox
|
||||
import com.vitorpamplona.amethyst.commons.ui.layouts.DisappearingScaffold
|
||||
@@ -77,7 +78,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.SimpleImage75Modifier
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.Size35dp
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.EventFinderFilterAssemblerSubscription
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.components.MyAsyncImage
|
||||
import com.vitorpamplona.amethyst.ui.components.ReusableZapButton
|
||||
import com.vitorpamplona.amethyst.ui.components.ZapButtonConfig
|
||||
|
||||
+1
-1
@@ -30,13 +30,13 @@ import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.event.observeNoteAndMap
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.MyExtensibleTopAppBar
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.ArrowBackIcon
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.DoubleHorzSpacer
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.SimpleImage35Modifier
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.components.MyAsyncImage
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.BannerImage
|
||||
import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent
|
||||
|
||||
+1
-1
@@ -70,12 +70,12 @@ import com.vitorpamplona.amethyst.commons.resources.git_repo_code_loading
|
||||
import com.vitorpamplona.amethyst.commons.resources.git_repo_copy_file
|
||||
import com.vitorpamplona.amethyst.commons.resources.git_repo_file_load_error
|
||||
import com.vitorpamplona.amethyst.commons.resources.git_repo_plain_text
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.RichTextViewer
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.util.setText
|
||||
import com.vitorpamplona.amethyst.commons.ui.loadStringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.RichTextViewer
|
||||
import com.vitorpamplona.quartz.nip34Git.git.GitRepoSnapshot
|
||||
import com.vitorpamplona.quartz.nip34Git.git.GitTreeEntry
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
||||
+1
-1
@@ -40,10 +40,10 @@ import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.git_repo_code_loading
|
||||
import com.vitorpamplona.amethyst.commons.resources.git_repo_file_load_error
|
||||
import com.vitorpamplona.amethyst.commons.resources.git_repo_readme_missing
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.RichTextViewer
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.RichTextViewer
|
||||
import com.vitorpamplona.quartz.nip34Git.git.GitRepoSnapshot
|
||||
import com.vitorpamplona.quartz.nip34Git.git.GitTreeEntry
|
||||
import com.vitorpamplona.quartz.nip34Git.repository.GitRepositoryEvent
|
||||
|
||||
+1
-1
@@ -51,13 +51,13 @@ import com.vitorpamplona.amethyst.commons.resources.dvm_home_status_processing
|
||||
import com.vitorpamplona.amethyst.commons.resources.dvm_home_status_requesting
|
||||
import com.vitorpamplona.amethyst.commons.resources.dvm_home_status_requesting_all
|
||||
import com.vitorpamplona.amethyst.commons.resources.dvm_offline_banner
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadingAnimation
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.StdHorzSpacer
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.StdVertSpacer
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.dvms.DvmPaymentActions
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.dvms.rememberDvmHeartbeatFresh
|
||||
import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent
|
||||
|
||||
+1
-1
@@ -24,9 +24,9 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.BuildConfig
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
|
||||
@Composable
|
||||
fun ShowDonationCard(
|
||||
|
||||
+1
-1
@@ -66,6 +66,7 @@ import com.vitorpamplona.amethyst.commons.resources.this_version_brought_to_you_
|
||||
import com.vitorpamplona.amethyst.commons.resources.version_name
|
||||
import com.vitorpamplona.amethyst.commons.resources.zap_the_devs_description
|
||||
import com.vitorpamplona.amethyst.commons.resources.zap_the_devs_title
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.appendLink
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.EmptyNav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
@@ -77,7 +78,6 @@ import com.vitorpamplona.amethyst.commons.ui.theme.Size10dp
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.StdVertSpacer
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.components.ReusableZapButton
|
||||
import com.vitorpamplona.amethyst.ui.components.ZapButtonConfig
|
||||
import com.vitorpamplona.amethyst.ui.note.creators.zapsplits.DisplayZapSplits
|
||||
|
||||
+1
-1
@@ -34,9 +34,9 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.EmptyNav
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip68Picture.PictureEvent
|
||||
|
||||
+1
-1
@@ -94,6 +94,7 @@ import com.vitorpamplona.amethyst.commons.resources.poll_results_selections
|
||||
import com.vitorpamplona.amethyst.commons.resources.poll_results_title
|
||||
import com.vitorpamplona.amethyst.commons.resources.poll_results_your_pick
|
||||
import com.vitorpamplona.amethyst.commons.resources.poll_single_choice
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.routes.routeFor
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarWithBackButton
|
||||
@@ -118,7 +119,6 @@ import com.vitorpamplona.amethyst.commons.viewmodels.nip88Polls.PollOptionResult
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.nip88Polls.PollResultsUiState
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.nip88Polls.PollResultsViewModel
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.nip88Polls.PollVoterRow
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.note.creators.userSuggestions.UserLine
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.MoreOptionsButton
|
||||
import com.vitorpamplona.amethyst.ui.note.types.UserGallery
|
||||
|
||||
+1
-1
@@ -30,11 +30,11 @@ import androidx.compose.ui.Modifier
|
||||
import com.vitorpamplona.amethyst.commons.model.Note
|
||||
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.event.observeNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.routes.routeFor
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.EventFinderFilterAssemblerSubscription
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.components.SensitivityWarning
|
||||
import com.vitorpamplona.amethyst.ui.note.CheckHiddenFeedWatchBlockAndReport
|
||||
import com.vitorpamplona.amethyst.ui.note.WatchNoteEvent
|
||||
|
||||
+1
-1
@@ -43,13 +43,13 @@ import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.looking_for_event
|
||||
import com.vitorpamplona.amethyst.commons.resources.looking_for_event_title
|
||||
import com.vitorpamplona.amethyst.commons.resources.looking_for_private_event
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.layouts.DisappearingScaffold
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.routes.routeFor
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarExtensibleWithBackButton
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.Nav
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
+1
-1
@@ -94,6 +94,7 @@ import com.vitorpamplona.amethyst.commons.resources.send_the_seller_a_message
|
||||
import com.vitorpamplona.amethyst.commons.resources.thread_collapsed_reply_count
|
||||
import com.vitorpamplona.amethyst.commons.richtext.MediaUrlImage
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.GenericLoadable
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.RefresheableBox
|
||||
import com.vitorpamplona.amethyst.commons.ui.layouts.rememberFeedContentPadding
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.EmptyNav
|
||||
@@ -139,7 +140,6 @@ import com.vitorpamplona.amethyst.commons.util.showAmount
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.thread.LevelFeedViewModel
|
||||
import com.vitorpamplona.amethyst.ui.components.AutoNonlazyGrid
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.components.MyAsyncImage
|
||||
import com.vitorpamplona.amethyst.ui.components.ZoomableContentView
|
||||
import com.vitorpamplona.amethyst.ui.note.CheckAndDisplayEditStatus
|
||||
|
||||
+1
-1
@@ -25,6 +25,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.thread_title
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.WatchLifecycleAndUpdateModel
|
||||
import com.vitorpamplona.amethyst.commons.ui.layouts.DisappearingScaffold
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
@@ -32,7 +33,6 @@ import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarExtensible
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.EventFinderFilterAssemblerSubscription
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.threadview.dal.ThreadFeedViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.threadview.datasources.ThreadFilterAssemblerSubscription
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ package com.vitorpamplona.amethyst.ui.theme
|
||||
import android.app.Activity
|
||||
import androidx.compose.material3.ColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
@@ -38,6 +39,8 @@ import com.vitorpamplona.amethyst.commons.model.AccentColorType
|
||||
import com.vitorpamplona.amethyst.commons.model.FontFamilyType
|
||||
import com.vitorpamplona.amethyst.commons.model.FontSizeType
|
||||
import com.vitorpamplona.amethyst.commons.model.ThemeType
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LocalInlineQuoteRenderer
|
||||
import com.vitorpamplona.amethyst.commons.ui.richtext.LocalRichTextPlatform
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.DisplaySettings
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.collectDisplaySettings
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.AmethystMaterialTheme
|
||||
@@ -46,6 +49,8 @@ import com.vitorpamplona.amethyst.commons.ui.theme.amethystLightColors
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.isDarkTheme
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.isLight
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.transparentBackground
|
||||
import com.vitorpamplona.amethyst.ui.components.AndroidRichTextPlatform
|
||||
import com.vitorpamplona.amethyst.ui.components.DefaultInlineQuoteRenderer
|
||||
|
||||
val chartLightColors =
|
||||
VicoTheme(
|
||||
@@ -120,8 +125,15 @@ fun AmethystTheme(
|
||||
// ImageLoaderSetup registers the avatar thumbnail cache and the local Blossom bridge.
|
||||
profilePictureCache = true,
|
||||
colors = colors,
|
||||
content = content,
|
||||
)
|
||||
) {
|
||||
// The platform halves of the shared note and rich-text renderers: media, LaTeX, payments,
|
||||
// link previews, and the quoted-note card. Every Activity root goes through this theme.
|
||||
CompositionLocalProvider(
|
||||
LocalRichTextPlatform provides AndroidRichTextPlatform,
|
||||
LocalInlineQuoteRenderer provides DefaultInlineQuoteRenderer,
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
|
||||
val view = LocalView.current
|
||||
if (!view.isInEditMode) {
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* 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.amethyst.ui.components
|
||||
package com.vitorpamplona.amethyst.commons.ui.components
|
||||
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.amethyst.commons.ui.components
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import com.vitorpamplona.amethyst.commons.model.Note
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.routes.routeFor
|
||||
import com.vitorpamplona.amethyst.commons.util.toShortDisplay
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
|
||||
/**
|
||||
* Strategy for rendering a note quoted inline in another note's text (a
|
||||
* `nostr:nevent1...`/`nostr:note1...` mention with preview budget left, or a
|
||||
* legacy `#[index]` event tag).
|
||||
*
|
||||
* Both rich-text paths (plain and markdown) funnel through [DisplayFullNote],
|
||||
* which reads [LocalInlineQuoteRenderer], so providing a different renderer
|
||||
* re-skins inline quotes for a whole subtree without threading a parameter
|
||||
* through the parser stack. Chat bubbles use this to keep a quoted chat
|
||||
* message in the chat reply design instead of the default quoted-note card
|
||||
* (see `chatInlineQuoteRenderer`).
|
||||
*
|
||||
* A renderer draws only the quoted note itself; any leftover characters
|
||||
* around the mention stay with the caller ([DisplayFullNote]).
|
||||
*/
|
||||
fun interface InlineQuoteRenderer {
|
||||
@Composable
|
||||
fun Render(
|
||||
note: Note,
|
||||
quotesLeft: Int,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A link to the quoted note, for front ends that install no card renderer of their own (Android
|
||||
* installs its quoted-note card in its theme root).
|
||||
*/
|
||||
val PlainInlineQuoteRenderer =
|
||||
InlineQuoteRenderer { note, _, _, accountViewModel, nav ->
|
||||
ClickableTextPrimary(
|
||||
text = "@${note.idNote().toShortDisplay()}",
|
||||
onClick = { routeFor(note, accountViewModel.account)?.let { nav.nav(it) } },
|
||||
)
|
||||
}
|
||||
|
||||
val LocalInlineQuoteRenderer = compositionLocalOf { PlainInlineQuoteRenderer }
|
||||
+498
@@ -0,0 +1,498 @@
|
||||
/*
|
||||
* 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.amethyst.commons.ui.components
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.text.InlineTextContent
|
||||
import androidx.compose.foundation.text.appendInlineContent
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalFontFamilyResolver
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.TextMeasurer
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.em
|
||||
import com.vitorpamplona.amethyst.commons.model.ImmutableListOfLists
|
||||
import com.vitorpamplona.amethyst.commons.model.Note
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
|
||||
import com.vitorpamplona.amethyst.commons.model.navigation.Route
|
||||
import com.vitorpamplona.amethyst.commons.model.navigation.routeFor
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.user.UserFinderFilterAssemblerSubscription
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.user.observeUserDisplayNickname
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.user.observeUserInfo
|
||||
import com.vitorpamplona.amethyst.commons.richtext.CachedRichTextParser
|
||||
import com.vitorpamplona.amethyst.commons.richtext.HashIndexEventSegment
|
||||
import com.vitorpamplona.amethyst.commons.richtext.HashIndexUserSegment
|
||||
import com.vitorpamplona.amethyst.commons.richtext.HashTagSegment
|
||||
import com.vitorpamplona.amethyst.commons.richtext.ParagraphState
|
||||
import com.vitorpamplona.amethyst.commons.richtext.RichTextViewerState
|
||||
import com.vitorpamplona.amethyst.commons.richtext.Segment
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.ClickableTextPrimary
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.CrossfadeIfEnabled
|
||||
import com.vitorpamplona.amethyst.commons.ui.components.LocalInlineQuoteRenderer
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.routes.routeFor
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.LoadUser
|
||||
import com.vitorpamplona.amethyst.commons.ui.richtext.CreateClickableTextWithEmoji
|
||||
import com.vitorpamplona.amethyst.commons.ui.richtext.CreateTextWithEmoji
|
||||
import com.vitorpamplona.amethyst.commons.ui.richtext.HashtagIcon
|
||||
import com.vitorpamplona.amethyst.commons.ui.richtext.LocalRichTextInteractions
|
||||
import com.vitorpamplona.amethyst.commons.ui.richtext.LocalRichTextPlatform
|
||||
import com.vitorpamplona.amethyst.commons.ui.richtext.LocalRichTextSegmentRenderer
|
||||
import com.vitorpamplona.amethyst.commons.ui.richtext.RichTextInteractions
|
||||
import com.vitorpamplona.amethyst.commons.ui.richtext.RichTextSegmentRenderer
|
||||
import com.vitorpamplona.amethyst.commons.ui.richtext.checkForHashtagWithIcon
|
||||
import com.vitorpamplona.amethyst.commons.ui.state.produceCachedState
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.inlinePlaceholder
|
||||
import com.vitorpamplona.amethyst.commons.util.toShortDisplay
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
||||
import com.vitorpamplona.amethyst.commons.ui.richtext.RichTextViewer as CommonsRichTextViewer
|
||||
|
||||
/**
|
||||
* Renders Nostr rich text. Markdown goes to the front end's renderer; plain rich text is parsed
|
||||
* once ([CachedRichTextParser]) and laid out by the shared [CommonsRichTextViewer] core, which
|
||||
* renders text, emoji, hashtags and mentions itself and hands every platform segment (media,
|
||||
* LaTeX, payments, link previews) to the [RichTextSegmentRenderer] the front end's
|
||||
* [LocalRichTextPlatform] builds for this call.
|
||||
*/
|
||||
@Composable
|
||||
fun RichTextViewer(
|
||||
content: String,
|
||||
canPreview: Boolean,
|
||||
quotesLeft: Int,
|
||||
modifier: Modifier,
|
||||
tags: ImmutableListOfLists<String>,
|
||||
backgroundColor: MutableState<Color>,
|
||||
callbackUri: String? = null,
|
||||
authorPubKey: String? = null,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val platform = LocalRichTextPlatform.current
|
||||
|
||||
Column(modifier = modifier) {
|
||||
if (remember(content) { CachedRichTextParser.isMarkdown(content) }) {
|
||||
platform.Markdown(content, tags, canPreview, quotesLeft, backgroundColor, callbackUri, accountViewModel, nav)
|
||||
return@Column
|
||||
}
|
||||
|
||||
val state by remember(content, tags) {
|
||||
mutableStateOf(CachedRichTextParser.parseText(content, tags, callbackUri, authorPubKey))
|
||||
}
|
||||
|
||||
val renderer =
|
||||
remember(platform, accountViewModel, nav, backgroundColor, callbackUri, canPreview) {
|
||||
platform.segmentRenderer(accountViewModel, nav, backgroundColor, callbackUri, canPreview)
|
||||
}
|
||||
|
||||
val interactions =
|
||||
remember(nav) {
|
||||
RichTextInteractions(
|
||||
onClickHashtag = { nav.nav(Route.Hashtag(it.lowercase())) },
|
||||
)
|
||||
}
|
||||
|
||||
CompositionLocalProvider(
|
||||
LocalRichTextSegmentRenderer provides renderer,
|
||||
LocalRichTextInteractions provides interactions,
|
||||
) {
|
||||
CommonsRichTextViewer(
|
||||
state = state,
|
||||
canPreview = canPreview,
|
||||
quotesLeft = quotesLeft,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RenderTextParagraph(
|
||||
paragraph: ParagraphState,
|
||||
spaceWidth: Dp,
|
||||
modifier: Modifier,
|
||||
renderWord: @Composable (word: Segment) -> Unit,
|
||||
) {
|
||||
FlowRow(
|
||||
modifier = modifier,
|
||||
horizontalArrangement = Arrangement.spacedBy(spaceWidth),
|
||||
// Center items on the cross axis so a taller item (an equation, whose image
|
||||
// is taller than a text line) sits centered on the line instead of hanging
|
||||
// below the baseline. No-op for the common all-text row.
|
||||
itemVerticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
paragraph.words.forEach { word ->
|
||||
renderWord(word)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
fun RenderRegular(
|
||||
content: String,
|
||||
tags: ImmutableListOfLists<String>,
|
||||
callbackUri: String? = null,
|
||||
authorPubKey: String? = null,
|
||||
renderParagraph: @Composable (ParagraphState, state: RichTextViewerState, Dp, modifier: Modifier) -> Unit,
|
||||
) {
|
||||
val state by remember(content, tags) { mutableStateOf(CachedRichTextParser.parseText(content, tags, callbackUri, authorPubKey)) }
|
||||
|
||||
val spaceWidth = measureSpaceWidth(LocalTextStyle.current)
|
||||
|
||||
val currentTextStyle = LocalTextStyle.current
|
||||
|
||||
val textStyle =
|
||||
remember(currentTextStyle) {
|
||||
currentTextStyle.copy(
|
||||
lineHeight = 1.3.em,
|
||||
)
|
||||
}
|
||||
|
||||
Column {
|
||||
// FlowRow doesn't work well with paragraphs. So we need to split them
|
||||
state.paragraphs.forEach { paragraph ->
|
||||
CompositionLocalProvider(
|
||||
LocalLayoutDirection provides
|
||||
if (paragraph.isRTL) {
|
||||
LayoutDirection.Rtl
|
||||
} else {
|
||||
LayoutDirection.Ltr
|
||||
},
|
||||
LocalTextStyle provides textStyle,
|
||||
) {
|
||||
renderParagraph(
|
||||
paragraph,
|
||||
state,
|
||||
spaceWidth,
|
||||
Modifier.align(if (paragraph.isRTL) Alignment.End else Alignment.Start),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun measureSpaceWidth(textStyle: TextStyle): Dp {
|
||||
val fontFamilyResolver = LocalFontFamilyResolver.current
|
||||
val density = LocalDensity.current
|
||||
val layoutDirection = LocalLayoutDirection.current
|
||||
|
||||
return remember(fontFamilyResolver, density, layoutDirection, textStyle) {
|
||||
val widthPx =
|
||||
TextMeasurer(fontFamilyResolver, density, layoutDirection, 1)
|
||||
.measure(" ", textStyle)
|
||||
.size
|
||||
.width
|
||||
with(density) { widthPx.toDp() }
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RenderCustomEmoji(
|
||||
word: String,
|
||||
state: RichTextViewerState,
|
||||
) {
|
||||
CreateTextWithEmoji(
|
||||
text = word,
|
||||
emojis = state.customEmoji,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BechLink(
|
||||
word: String,
|
||||
canPreview: Boolean,
|
||||
quotesLeft: Int,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val loadedLink by produceCachedState(cache = accountViewModel.bechLinkCache, key = word)
|
||||
|
||||
val baseNote = loadedLink?.baseNote
|
||||
|
||||
// Preload the placeholder's author NIP-65 outbox so a missing quoted event can be fetched from there.
|
||||
baseNote?.author?.let { author ->
|
||||
UserFinderFilterAssemblerSubscription(author, accountViewModel)
|
||||
}
|
||||
|
||||
if (canPreview && quotesLeft > 0 && baseNote != null) {
|
||||
Row {
|
||||
DisplayFullNote(
|
||||
note = baseNote,
|
||||
extraChars = loadedLink?.nip19?.additionalChars?.ifBlank { null },
|
||||
quotesLeft = quotesLeft,
|
||||
backgroundColor = backgroundColor,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
} else if (loadedLink?.nip19 != null) {
|
||||
ClickableRoute(word, loadedLink?.nip19!!, accountViewModel, nav)
|
||||
} else {
|
||||
val text =
|
||||
remember(word) {
|
||||
if (word.length > 16) {
|
||||
word.replaceRange(8, word.length - 8, ":")
|
||||
} else {
|
||||
word
|
||||
}
|
||||
}
|
||||
|
||||
Text(text = text, maxLines = 1)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DisplayFullNote(
|
||||
note: Note,
|
||||
extraChars: String?,
|
||||
quotesLeft: Int,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
LocalInlineQuoteRenderer.current.Render(note, quotesLeft, backgroundColor, accountViewModel, nav)
|
||||
|
||||
extraChars?.let { Text(it) }
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HashTag(
|
||||
segment: HashTagSegment,
|
||||
nav: INav,
|
||||
) {
|
||||
val primary = MaterialTheme.colorScheme.primary
|
||||
val background = MaterialTheme.colorScheme.onBackground
|
||||
val hashtagIcon: HashtagIcon? = checkForHashtagWithIcon(segment.hashtag)
|
||||
|
||||
val annotatedTermsString =
|
||||
remember(segment.segmentText) {
|
||||
buildAnnotatedString {
|
||||
withStyle(SpanStyle(color = primary)) {
|
||||
pushStringAnnotation("routeToHashtag", "")
|
||||
append("#${segment.hashtag}")
|
||||
pop()
|
||||
}
|
||||
|
||||
if (hashtagIcon != null) {
|
||||
withStyle(SpanStyle(color = primary)) {
|
||||
pushStringAnnotation("routeToHashtag", "")
|
||||
appendInlineContent("inlineContent", "[icon]")
|
||||
pop()
|
||||
}
|
||||
}
|
||||
|
||||
segment.extras?.let { withStyle(SpanStyle(color = background)) { append(it) } }
|
||||
}
|
||||
}
|
||||
|
||||
Text(
|
||||
text = annotatedTermsString,
|
||||
modifier =
|
||||
remember {
|
||||
Modifier.clickable {
|
||||
nav.nav(Route.Hashtag(segment.hashtag.lowercase()))
|
||||
}
|
||||
},
|
||||
inlineContent =
|
||||
if (hashtagIcon != null) {
|
||||
mapOf("inlineContent" to inlineIcon(hashtagIcon))
|
||||
} else {
|
||||
emptyMap()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun inlineIcon(hashtagIcon: HashtagIcon) =
|
||||
InlineTextContent(inlinePlaceholder) {
|
||||
Icon(
|
||||
imageVector = hashtagIcon.icon,
|
||||
contentDescription = hashtagIcon.description,
|
||||
tint = Color.Unspecified,
|
||||
modifier = hashtagIcon.modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TagLink(
|
||||
word: HashIndexUserSegment,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
LoadUser(baseUserHex = word.hex) {
|
||||
if (it == null) {
|
||||
Text(text = word.segmentText)
|
||||
} else {
|
||||
Row {
|
||||
DisplayUserFromTag(it, accountViewModel, nav)
|
||||
word.extras?.let { it2 ->
|
||||
Text(text = it2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LoadNote(
|
||||
baseNoteHex: String,
|
||||
content: @Composable (Note?) -> Unit,
|
||||
) {
|
||||
var note by
|
||||
remember(baseNoteHex) { mutableStateOf(LocalCache.getNoteIfExists(baseNoteHex)) }
|
||||
|
||||
if (note == null) {
|
||||
LaunchedEffect(key1 = baseNoteHex) {
|
||||
note = LocalCache.checkGetOrCreateNote(baseNoteHex)
|
||||
}
|
||||
}
|
||||
|
||||
content(note)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TagLink(
|
||||
word: HashIndexEventSegment,
|
||||
canPreview: Boolean,
|
||||
quotesLeft: Int,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
LoadNote(baseNoteHex = word.hex) {
|
||||
if (it == null) {
|
||||
Text(text = remember { word.segmentText.toShortDisplay() })
|
||||
} else {
|
||||
Row {
|
||||
DisplayNoteFromTag(
|
||||
it,
|
||||
word.extras,
|
||||
canPreview,
|
||||
quotesLeft,
|
||||
accountViewModel,
|
||||
backgroundColor,
|
||||
nav,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun DisplayNoteFromTagPreview() {
|
||||
val dummyPost =
|
||||
TextNoteEvent(
|
||||
id = "0b6d941c46411a95edb1c93da7ad6ca26370497d8c7b7d621f5cb59f48841bad",
|
||||
pubKey = "6dd3b72e325da7383b275eef1c66131ba4664326e162bc060527509b4e33ae43",
|
||||
createdAt = 1753988264,
|
||||
tags = emptyArray(),
|
||||
content = "test",
|
||||
sig = "ec39e60722a083cccbd2d82d2827e13f5499fa7cbcedac5b76011a844c077473adb629d50d01fab147835ac6c8a3d5ba9aaddd87d6723f0c3c864b9119fc4356",
|
||||
)
|
||||
|
||||
LocalCache.justConsume(dummyPost, null, true)
|
||||
val note = LocalCache.getOrCreateNote(dummyPost.id)
|
||||
|
||||
ThemeComparisonColumn(
|
||||
toPreview = {
|
||||
ClickableTextPrimary(
|
||||
text = "@${note.idNote().toShortDisplay()}",
|
||||
onClick = { },
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DisplayNoteFromTag(
|
||||
baseNote: Note,
|
||||
addedChars: String?,
|
||||
canPreview: Boolean,
|
||||
quotesLeft: Int,
|
||||
accountViewModel: AccountViewModel,
|
||||
backgroundColor: MutableState<Color>,
|
||||
nav: INav,
|
||||
) {
|
||||
if (canPreview && quotesLeft > 0) {
|
||||
LocalInlineQuoteRenderer.current.Render(baseNote, quotesLeft, backgroundColor, accountViewModel, nav)
|
||||
} else {
|
||||
ClickableTextPrimary(
|
||||
text = "@${baseNote.idNote().toShortDisplay()}",
|
||||
onClick = { routeFor(baseNote, accountViewModel.account)?.let { nav.nav(it) } },
|
||||
)
|
||||
}
|
||||
|
||||
addedChars?.ifBlank { null }?.let { Text(text = it) }
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DisplayUserFromTag(
|
||||
baseUser: User,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val meta by observeUserInfo(baseUser, accountViewModel)
|
||||
val nickname by observeUserDisplayNickname(baseUser, accountViewModel)
|
||||
val petName = nickname?.petName
|
||||
|
||||
CrossfadeIfEnabled(targetState = meta, label = "DisplayUserFromTag") {
|
||||
Row {
|
||||
CreateClickableTextWithEmoji(
|
||||
clickablePart = remember(meta, petName) { petName ?: it?.info?.bestName() ?: baseUser.pubkeyDisplayHex() },
|
||||
maxLines = 1,
|
||||
route = remember(baseUser) { routeFor(baseUser) },
|
||||
nav = nav,
|
||||
tags = if (petName != null) nickname?.tags else it?.tags,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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.amethyst.commons.ui.richtext
|
||||
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import com.vitorpamplona.amethyst.commons.model.ImmutableListOfLists
|
||||
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel
|
||||
|
||||
/**
|
||||
* What a front end plugs into the shared rich-text viewer: the [RichTextSegmentRenderer] for its
|
||||
* platform leaves (zoomable media, LaTeX, payments, link previews, Blossom links) and its markdown
|
||||
* renderer. The shared viewer parses, lays out paragraphs and renders text, emoji, hashtags and
|
||||
* mentions itself, and asks this for everything else.
|
||||
*
|
||||
* A front end installs its own through [LocalRichTextPlatform] at its composition root (Android
|
||||
* does so in its theme). Without one, [Plain] renders every platform segment as text, which is
|
||||
* what previews and a front end still wiring its leaves get.
|
||||
*/
|
||||
@Stable
|
||||
interface RichTextPlatform {
|
||||
/** The segment renderer for one viewer call; it may close over every argument. */
|
||||
fun segmentRenderer(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
backgroundColor: MutableState<Color>,
|
||||
callbackUri: String?,
|
||||
canPreview: Boolean,
|
||||
): RichTextSegmentRenderer
|
||||
|
||||
/** Renders [content] that the parser recognised as markdown. */
|
||||
@Composable
|
||||
fun Markdown(
|
||||
content: String,
|
||||
tags: ImmutableListOfLists<String>,
|
||||
canPreview: Boolean,
|
||||
quotesLeft: Int,
|
||||
backgroundColor: MutableState<Color>,
|
||||
callbackUri: String?,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
)
|
||||
|
||||
/** Text for every platform segment, and markdown shown as its source. */
|
||||
object Plain : RichTextPlatform {
|
||||
override fun segmentRenderer(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
backgroundColor: MutableState<Color>,
|
||||
callbackUri: String?,
|
||||
canPreview: Boolean,
|
||||
): RichTextSegmentRenderer = PlainTextSegmentRenderer
|
||||
|
||||
@Composable
|
||||
override fun Markdown(
|
||||
content: String,
|
||||
tags: ImmutableListOfLists<String>,
|
||||
canPreview: Boolean,
|
||||
quotesLeft: Int,
|
||||
backgroundColor: MutableState<Color>,
|
||||
callbackUri: String?,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) = Text(content)
|
||||
}
|
||||
}
|
||||
|
||||
/** The front end's [RichTextPlatform]. Static: it is set once at the root and never changes. */
|
||||
val LocalRichTextPlatform = staticCompositionLocalOf<RichTextPlatform> { RichTextPlatform.Plain }
|
||||
Reference in New Issue
Block a user