From 3fc3cc6311b22f8de62e3fcd43f9e029507a887a Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 28 Sep 2026 17:41:15 +0000 Subject: [PATCH] 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 Claude-Session: https://claude.ai/code/session_01S7FuNBSKiyVecARSoE4B9P --- .../amethyst/ui/actions/EditPostView.kt | 2 +- .../ui/components/AmethystRichText.kt | 93 +--- .../ui/components/ExpandableRichTextViewer.kt | 1 + .../ui/components/InlineQuoteRenderer.kt | 38 +- ...extViewer.kt => RichTextPlatformLeaves.kt} | 424 +-------------- .../markdown/MarkdownMediaRenderer.kt | 4 +- .../ui/note/NIP05VerificationDisplay.kt | 2 +- .../amethyst/ui/note/ReplyInformation.kt | 2 +- .../amethyst/ui/note/elements/ForkInfo.kt | 2 +- .../ui/note/share/ShareNoteAsImageScreen.kt | 2 +- .../ui/note/share/ShareNoteAsQrScreen.kt | 2 +- .../amethyst/ui/note/types/Attestation.kt | 2 +- .../amethyst/ui/note/types/CommunityHeader.kt | 2 +- .../amethyst/ui/note/types/EntityRating.kt | 2 +- .../amethyst/ui/note/types/FileStorage.kt | 2 +- .../amethyst/ui/note/types/Highlight.kt | 6 +- .../amethyst/ui/note/types/MusicPlaylist.kt | 2 +- .../amethyst/ui/note/types/MusicTrack.kt | 2 +- .../amethyst/ui/note/types/PublicMessage.kt | 2 +- .../amethyst/ui/note/types/Publication.kt | 2 +- .../ui/note/types/PublicationSection.kt | 2 +- .../ui/note/types/RenderVideoCurationSet.kt | 2 +- .../amethyst/ui/note/types/SnoPalettes.kt | 2 +- .../ui/note/types/TextModification.kt | 2 +- .../amethyst/ui/note/types/TorrentComment.kt | 2 +- .../ui/note/types/WikiCollaboration.kt | 2 +- .../ui/note/types/lists/ListItemRows.kt | 2 +- .../amethyst/ui/note/types/lists/MuteList.kt | 2 +- .../backups/BackupConflictEventViews.kt | 2 +- .../backups/BackupConflictReviewScreen.kt | 2 +- .../PostBookmarkListManagementScreen.kt | 2 +- .../chats/feed/ChatInlineQuoteRenderer.kt | 2 +- .../loggedIn/chats/feed/ChatMessageCompose.kt | 2 +- .../header/LongPublicChatChannelHeader.kt | 2 +- .../header/ShortPublicChatChannelHeader.kt | 2 +- .../header/LiveStreamGoalHeader.kt | 2 +- .../communities/NewCommunityNoteButton.kt | 2 +- .../contactList/ContactListUsersScreen.kt | 2 +- .../dvms/DvmContentDiscoveryScreen.kt | 2 +- .../ui/screen/loggedIn/dvms/DvmTopBar.kt | 2 +- .../loggedIn/gitRepo/code/GitFileViewer.kt | 2 +- .../loggedIn/gitRepo/code/GitReadmeTab.kt | 2 +- .../loggedIn/home/AlgoFeedStatusBanner.kt | 2 +- .../donations/ShowDonationCard.kt | 2 +- .../notifications/donations/ZapTheDevsCard.kt | 2 +- .../pictures/PictureCardComposePreview.kt | 2 +- .../polls/results/PollResultsScreen.kt | 2 +- .../profile/gallery/GalleryCardCompose.kt | 2 +- .../loggedIn/redirect/LoadRedirectScreen.kt | 2 +- .../loggedIn/threadview/ThreadFeedView.kt | 2 +- .../loggedIn/threadview/ThreadScreen.kt | 2 +- .../vitorpamplona/amethyst/ui/theme/Theme.kt | 16 +- .../commons}/ui/components/ClickableRoute.kt | 2 +- .../ui/components/InlineQuoteRenderer.kt | 71 +++ .../commons/ui/components/RichTextViewer.kt | 498 ++++++++++++++++++ .../commons/ui/richtext/RichTextPlatform.kt | 92 ++++ 56 files changed, 763 insertions(+), 572 deletions(-) rename amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/{RichTextViewer.kt => RichTextPlatformLeaves.kt} (64%) rename {amethyst/src/main/java/com/vitorpamplona/amethyst => commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons}/ui/components/ClickableRoute.kt (99%) create mode 100644 commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/InlineQuoteRenderer.kt create mode 100644 commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/RichTextViewer.kt create mode 100644 commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/richtext/RichTextPlatform.kt diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostView.kt index 2ec7ab8e1c..27e2015329 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostView.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/AmethystRichText.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/AmethystRichText.kt index de83ab8df7..0f4ac5d7fb 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/AmethystRichText.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/AmethystRichText.kt @@ -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, - backgroundColor: MutableState, - 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, + 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, + canPreview: Boolean, + quotesLeft: Int, + backgroundColor: MutableState, + callbackUri: String?, + accountViewModel: AccountViewModel, + nav: INav, + ) = RenderContentAsMarkdown(content, tags, canPreview, quotesLeft, backgroundColor, callbackUri, accountViewModel, nav) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ExpandableRichTextViewer.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ExpandableRichTextViewer.kt index 153dcba569..26676f1aa5 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ExpandableRichTextViewer.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ExpandableRichTextViewer.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/InlineQuoteRenderer.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/InlineQuoteRenderer.kt index 9adcf6bf96..624d36eed9 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/InlineQuoteRenderer.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/InlineQuoteRenderer.kt @@ -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, - 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 } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextPlatformLeaves.kt similarity index 64% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextPlatformLeaves.kt index 4167e001b6..827943aedd 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextPlatformLeaves.kt @@ -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, - backgroundColor: MutableState, - 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, - 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, - 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, - 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, - 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, - 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, - ) - } - } -} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/markdown/MarkdownMediaRenderer.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/markdown/MarkdownMediaRenderer.kt index 1aac646d47..f4ccaa1359 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/markdown/MarkdownMediaRenderer.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/markdown/MarkdownMediaRenderer.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NIP05VerificationDisplay.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NIP05VerificationDisplay.kt index 58f4426d56..4a214409b8 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NIP05VerificationDisplay.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NIP05VerificationDisplay.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ReplyInformation.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ReplyInformation.kt index 216c968f7a..d715cd288c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ReplyInformation.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ReplyInformation.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/ForkInfo.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/ForkInfo.kt index 2a307ef088..0f8bde1ce0 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/ForkInfo.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/ForkInfo.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/share/ShareNoteAsImageScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/share/ShareNoteAsImageScreen.kt index d7f9315af1..69b3d43b0f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/share/ShareNoteAsImageScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/share/ShareNoteAsImageScreen.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/share/ShareNoteAsQrScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/share/ShareNoteAsQrScreen.kt index 2cfe54091a..9044bcecb9 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/share/ShareNoteAsQrScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/share/ShareNoteAsQrScreen.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Attestation.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Attestation.kt index 436809a064..751fd843aa 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Attestation.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Attestation.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/CommunityHeader.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/CommunityHeader.kt index 80ff2388a4..5e99f039c8 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/CommunityHeader.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/CommunityHeader.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/EntityRating.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/EntityRating.kt index 701d416ae0..b6ba9537b2 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/EntityRating.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/EntityRating.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/FileStorage.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/FileStorage.kt index f5c15af3db..9e5588742a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/FileStorage.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/FileStorage.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Highlight.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Highlight.kt index f9a4e6aca6..25beb559ee 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Highlight.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Highlight.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/MusicPlaylist.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/MusicPlaylist.kt index 41bea616d1..05a30c8c97 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/MusicPlaylist.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/MusicPlaylist.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/MusicTrack.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/MusicTrack.kt index f17fbeb5e6..a495e6188a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/MusicTrack.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/MusicTrack.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/PublicMessage.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/PublicMessage.kt index 231e641117..9a0e46ca07 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/PublicMessage.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/PublicMessage.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Publication.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Publication.kt index 97db8fe241..53e4cfd621 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Publication.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Publication.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/PublicationSection.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/PublicationSection.kt index a32889e78a..d457e5bb4f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/PublicationSection.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/PublicationSection.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/RenderVideoCurationSet.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/RenderVideoCurationSet.kt index 1547d45ece..cec727b108 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/RenderVideoCurationSet.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/RenderVideoCurationSet.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/SnoPalettes.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/SnoPalettes.kt index 2b278bf70c..a2b0589f99 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/SnoPalettes.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/SnoPalettes.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/TextModification.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/TextModification.kt index 5d83bd3484..895fbc53d8 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/TextModification.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/TextModification.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/TorrentComment.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/TorrentComment.kt index f3724f5263..006c7db35a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/TorrentComment.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/TorrentComment.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/WikiCollaboration.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/WikiCollaboration.kt index afd02a92c1..b5aa732632 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/WikiCollaboration.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/WikiCollaboration.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/lists/ListItemRows.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/lists/ListItemRows.kt index 618b9376f3..d5915e4e28 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/lists/ListItemRows.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/lists/ListItemRows.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/lists/MuteList.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/lists/MuteList.kt index a2f4cf65bf..30f8d0978c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/lists/MuteList.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/lists/MuteList.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/backups/BackupConflictEventViews.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/backups/BackupConflictEventViews.kt index c0e9ae970d..01e44fc9dc 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/backups/BackupConflictEventViews.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/backups/BackupConflictEventViews.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/backups/BackupConflictReviewScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/backups/BackupConflictReviewScreen.kt index 050613849a..5557788cf9 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/backups/BackupConflictReviewScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/backups/BackupConflictReviewScreen.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/bookmarkgroups/membershipManagement/PostBookmarkListManagementScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/bookmarkgroups/membershipManagement/PostBookmarkListManagementScreen.kt index 60e3e6044f..5f2beeadcd 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/bookmarkgroups/membershipManagement/PostBookmarkListManagementScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/bookmarkgroups/membershipManagement/PostBookmarkListManagementScreen.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatInlineQuoteRenderer.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatInlineQuoteRenderer.kt index 1796e4b562..f3c76cb798 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatInlineQuoteRenderer.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatInlineQuoteRenderer.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatMessageCompose.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatMessageCompose.kt index 61105998c9..8132870a1f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatMessageCompose.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatMessageCompose.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/LongPublicChatChannelHeader.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/LongPublicChatChannelHeader.kt index 9db5f6e313..ee2302fecf 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/LongPublicChatChannelHeader.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/LongPublicChatChannelHeader.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/ShortPublicChatChannelHeader.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/ShortPublicChatChannelHeader.kt index e031472a0f..4e6e5f623e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/ShortPublicChatChannelHeader.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/ShortPublicChatChannelHeader.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/header/LiveStreamGoalHeader.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/header/LiveStreamGoalHeader.kt index 6bc704cdd2..3646efc0ed 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/header/LiveStreamGoalHeader.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip53LiveActivities/header/LiveStreamGoalHeader.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/NewCommunityNoteButton.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/NewCommunityNoteButton.kt index 8f8ced784b..ccd2bd2946 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/NewCommunityNoteButton.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/NewCommunityNoteButton.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/contactList/ContactListUsersScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/contactList/ContactListUsersScreen.kt index c9dd501b36..5ac12bff3b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/contactList/ContactListUsersScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/contactList/ContactListUsersScreen.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/dvms/DvmContentDiscoveryScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/dvms/DvmContentDiscoveryScreen.kt index 03f792ea0b..2ca5eb15c0 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/dvms/DvmContentDiscoveryScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/dvms/DvmContentDiscoveryScreen.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/dvms/DvmTopBar.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/dvms/DvmTopBar.kt index 0823605f10..28be318024 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/dvms/DvmTopBar.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/dvms/DvmTopBar.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/gitRepo/code/GitFileViewer.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/gitRepo/code/GitFileViewer.kt index 63c81b7480..204d228e21 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/gitRepo/code/GitFileViewer.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/gitRepo/code/GitFileViewer.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/gitRepo/code/GitReadmeTab.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/gitRepo/code/GitReadmeTab.kt index b1ccd8507f..30ec3d6466 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/gitRepo/code/GitReadmeTab.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/gitRepo/code/GitReadmeTab.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/AlgoFeedStatusBanner.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/AlgoFeedStatusBanner.kt index 7ddb368818..77e241e9a3 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/AlgoFeedStatusBanner.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/AlgoFeedStatusBanner.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/donations/ShowDonationCard.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/donations/ShowDonationCard.kt index 171c9e70de..2903ef7c3d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/donations/ShowDonationCard.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/donations/ShowDonationCard.kt @@ -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( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/donations/ZapTheDevsCard.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/donations/ZapTheDevsCard.kt index 14da60fc52..4313e2c8dd 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/donations/ZapTheDevsCard.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/donations/ZapTheDevsCard.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/pictures/PictureCardComposePreview.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/pictures/PictureCardComposePreview.kt index f07f651977..99894d5207 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/pictures/PictureCardComposePreview.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/pictures/PictureCardComposePreview.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/results/PollResultsScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/results/PollResultsScreen.kt index 482b90af0b..e11c65e917 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/results/PollResultsScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/results/PollResultsScreen.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/gallery/GalleryCardCompose.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/gallery/GalleryCardCompose.kt index 5dfe235f55..1b6dfafd6a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/gallery/GalleryCardCompose.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/gallery/GalleryCardCompose.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/redirect/LoadRedirectScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/redirect/LoadRedirectScreen.kt index 795ae757bb..9a20f90a6d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/redirect/LoadRedirectScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/redirect/LoadRedirectScreen.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/threadview/ThreadFeedView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/threadview/ThreadFeedView.kt index 965c0abd81..2ce5d78e52 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/threadview/ThreadFeedView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/threadview/ThreadFeedView.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/threadview/ThreadScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/threadview/ThreadScreen.kt index a1c170a7f6..7d2d224a21 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/threadview/ThreadScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/threadview/ThreadScreen.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Theme.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Theme.kt index d10655579f..4a3b824c64 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Theme.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Theme.kt @@ -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) { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRoute.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/ClickableRoute.kt similarity index 99% rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRoute.kt rename to commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/ClickableRoute.kt index 4280982f69..784fae2dd7 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRoute.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/ClickableRoute.kt @@ -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 diff --git a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/InlineQuoteRenderer.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/InlineQuoteRenderer.kt new file mode 100644 index 0000000000..1e00429e14 --- /dev/null +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/InlineQuoteRenderer.kt @@ -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, + 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 } diff --git a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/RichTextViewer.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/RichTextViewer.kt new file mode 100644 index 0000000000..c95a05d178 --- /dev/null +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/RichTextViewer.kt @@ -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, + backgroundColor: MutableState, + 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, + 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, + 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, + 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, + 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, + 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, + ) + } + } +} diff --git a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/richtext/RichTextPlatform.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/richtext/RichTextPlatform.kt new file mode 100644 index 0000000000..2d29abddc9 --- /dev/null +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/richtext/RichTextPlatform.kt @@ -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, + callbackUri: String?, + canPreview: Boolean, + ): RichTextSegmentRenderer + + /** Renders [content] that the parser recognised as markdown. */ + @Composable + fun Markdown( + content: String, + tags: ImmutableListOfLists, + canPreview: Boolean, + quotesLeft: Int, + backgroundColor: MutableState, + 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, + callbackUri: String?, + canPreview: Boolean, + ): RichTextSegmentRenderer = PlainTextSegmentRenderer + + @Composable + override fun Markdown( + content: String, + tags: ImmutableListOfLists, + canPreview: Boolean, + quotesLeft: Int, + backgroundColor: MutableState, + 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.Plain }