perf(RichTextViewer): share isMarkdown decision via CachedRichTextParser
The markdown check is purely a function of `content`, but it was a top-level function in RichTextViewer.kt — every distinct instance of the same content (e.g. a quoted note rendered inside its quoter, or the same viral note shown multiple places in a feed) re-scanned the string. Move the decision into `CachedRichTextParser` with its own small LRU keyed on content.hashCode(). Composables still wrap the call in `remember(content)` so recompositions skip even the LRU lookup.
This commit is contained in:
@@ -28,6 +28,7 @@ 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,
|
||||||
@@ -69,6 +70,25 @@ 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 {
|
||||||
|
|||||||
@@ -122,15 +122,6 @@ import com.vitorpamplona.quartz.nipB7Blossom.BlossomUri
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
fun isMarkdown(content: String): Boolean =
|
|
||||||
content.startsWith("> ") ||
|
|
||||||
content.startsWith("# ") ||
|
|
||||||
content.contains("##") ||
|
|
||||||
content.contains("__") ||
|
|
||||||
content.contains("**") ||
|
|
||||||
content.contains("```") ||
|
|
||||||
content.contains("](")
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RichTextViewer(
|
fun RichTextViewer(
|
||||||
content: String,
|
content: String,
|
||||||
@@ -145,7 +136,7 @@ fun RichTextViewer(
|
|||||||
nav: INav,
|
nav: INav,
|
||||||
) {
|
) {
|
||||||
Column(modifier = modifier) {
|
Column(modifier = modifier) {
|
||||||
if (remember(content) { isMarkdown(content) }) {
|
if (remember(content) { CachedRichTextParser.isMarkdown(content) }) {
|
||||||
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)
|
||||||
|
|||||||
Reference in New Issue
Block a user