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:
Claude
2026-05-11 00:25:36 +00:00
parent 680e5bc0be
commit 7e2e3304e1
4 changed files with 19 additions and 21 deletions
@@ -224,6 +224,7 @@ class RichTextParser {
customEmoji = emojiMap.toImmutableMap(),
paragraphs = segments,
tags = tags,
isMarkdown = isMarkdown(content),
)
}
@@ -411,6 +412,18 @@ class RichTextParser {
}
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 shortDatePattern: Regex = Regex("^\\d{2}-\\d{2}-\\d{2}$")
@@ -33,6 +33,7 @@ class RichTextViewerState(
val customEmoji: ImmutableMap<String, String>,
val paragraphs: ImmutableList<ParagraphState>,
val tags: ImmutableListOfLists<String>,
val isMarkdown: Boolean = false,
)
@Immutable