refactor(RichTextViewer): move isMarkdown onto RichTextViewerState
Cleaner conceptual model: the parsed state object is the place where all derived facts about the content live, so `isMarkdown` becomes a field on `RichTextViewerState` (populated by `RichTextParser.parseText` using the same cheap heuristic). `RichTextViewer` now calls `CachedRichTextParser.parseText` once at the top and dispatches on `state.isMarkdown` instead of running a separate scan. `CachedRichTextParser.isMarkdown(content)` and its dedicated `isMarkdownCache` go away — the single `richTextCache` carries the decision alongside the parsed segments. Inner callers (`DisplaySecretEmoji`, `MultiSetCompose` reaction preview, `DisplayUncitedHashtags`) are unaffected: they continue to receive a fully-parsed state with all segments populated.
This commit is contained in:
@@ -28,7 +28,6 @@ import com.vitorpamplona.amethyst.commons.richtext.UrlParser
|
|||||||
|
|
||||||
object CachedRichTextParser {
|
object CachedRichTextParser {
|
||||||
private val richTextCache = LruCache<Int, RichTextViewerState>(50)
|
private val richTextCache = LruCache<Int, RichTextViewerState>(50)
|
||||||
private val isMarkdownCache = LruCache<Int, Boolean>(200)
|
|
||||||
|
|
||||||
private fun hashCodeCache(
|
private fun hashCodeCache(
|
||||||
content: String,
|
content: String,
|
||||||
@@ -70,25 +69,6 @@ object CachedRichTextParser {
|
|||||||
newUrls
|
newUrls
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shared across every RichTextViewer instance so that the same content quoted in multiple
|
|
||||||
// notes only pays for the scan once. The decision is purely a function of `content`.
|
|
||||||
fun isMarkdown(content: String): Boolean {
|
|
||||||
val key = content.hashCode()
|
|
||||||
isMarkdownCache[key]?.let { return it }
|
|
||||||
val result = computeIsMarkdown(content)
|
|
||||||
isMarkdownCache.put(key, result)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun computeIsMarkdown(content: String): Boolean =
|
|
||||||
content.startsWith("> ") ||
|
|
||||||
content.startsWith("# ") ||
|
|
||||||
content.contains("##") ||
|
|
||||||
content.contains("__") ||
|
|
||||||
content.contains("**") ||
|
|
||||||
content.contains("```") ||
|
|
||||||
content.contains("](")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object CachedUrlParser {
|
object CachedUrlParser {
|
||||||
|
|||||||
@@ -136,7 +136,11 @@ fun RichTextViewer(
|
|||||||
nav: INav,
|
nav: INav,
|
||||||
) {
|
) {
|
||||||
Column(modifier = modifier) {
|
Column(modifier = modifier) {
|
||||||
if (remember(content) { CachedRichTextParser.isMarkdown(content) }) {
|
val state =
|
||||||
|
remember(content, tags) {
|
||||||
|
CachedRichTextParser.parseText(content, tags, callbackUri, authorPubKey)
|
||||||
|
}
|
||||||
|
if (state.isMarkdown) {
|
||||||
RenderContentAsMarkdown(content, tags, canPreview, quotesLeft, backgroundColor, callbackUri, accountViewModel, nav)
|
RenderContentAsMarkdown(content, tags, canPreview, quotesLeft, backgroundColor, callbackUri, accountViewModel, nav)
|
||||||
} else {
|
} else {
|
||||||
RenderRegular(content, tags, canPreview, quotesLeft, backgroundColor, callbackUri, authorPubKey, accountViewModel, nav)
|
RenderRegular(content, tags, canPreview, quotesLeft, backgroundColor, callbackUri, authorPubKey, accountViewModel, nav)
|
||||||
|
|||||||
+13
@@ -224,6 +224,7 @@ class RichTextParser {
|
|||||||
customEmoji = emojiMap.toImmutableMap(),
|
customEmoji = emojiMap.toImmutableMap(),
|
||||||
paragraphs = segments,
|
paragraphs = segments,
|
||||||
tags = tags,
|
tags = tags,
|
||||||
|
isMarkdown = isMarkdown(content),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -411,6 +412,18 @@ class RichTextParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
// Cheap heuristic: stored on the parsed state so callers (e.g. RichTextViewer's
|
||||||
|
// markdown vs regular dispatch) can read the decision off the cached result instead
|
||||||
|
// of running a separate scan + maintaining a separate cache.
|
||||||
|
fun isMarkdown(content: String): Boolean =
|
||||||
|
content.startsWith("> ") ||
|
||||||
|
content.startsWith("# ") ||
|
||||||
|
content.contains("##") ||
|
||||||
|
content.contains("__") ||
|
||||||
|
content.contains("**") ||
|
||||||
|
content.contains("```") ||
|
||||||
|
content.contains("](")
|
||||||
|
|
||||||
val longDatePattern: Regex = Regex("^\\d{4}-\\d{2}-\\d{2}$")
|
val longDatePattern: Regex = Regex("^\\d{4}-\\d{2}-\\d{2}$")
|
||||||
val shortDatePattern: Regex = Regex("^\\d{2}-\\d{2}-\\d{2}$")
|
val shortDatePattern: Regex = Regex("^\\d{2}-\\d{2}-\\d{2}$")
|
||||||
|
|
||||||
|
|||||||
+1
@@ -33,6 +33,7 @@ class RichTextViewerState(
|
|||||||
val customEmoji: ImmutableMap<String, String>,
|
val customEmoji: ImmutableMap<String, String>,
|
||||||
val paragraphs: ImmutableList<ParagraphState>,
|
val paragraphs: ImmutableList<ParagraphState>,
|
||||||
val tags: ImmutableListOfLists<String>,
|
val tags: ImmutableListOfLists<String>,
|
||||||
|
val isMarkdown: Boolean = false,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
|
|||||||
Reference in New Issue
Block a user