fix(app): render inline links via UITextView span on iOS (#1257)

AssistantMarkdownLink's native branch wrapped its children in a plain RN
<Text>. On iOS that <Text> sits inside the paragraph's native UITextView
and is not hoisted into a UITextViewChild, so link text (markdown links,
bare URLs, and inline-code path links) was silently dropped — the same
root cause as the bold/italic/strikethrough fix in this PR.

Route the native link through MarkdownTextSpan so it composes as a
UITextViewChild and stays visible + selectable. MarkdownTextSpan now
forwards onPress/accessibilityRole across platforms: tappable on
Android/web; on iOS react-native-uitextview nulls onPress on the root
native view, so reliable tap-to-open is still tracked by #21 — but
visible, selectable, copyable link text strictly beats an invisible one.

Refs #1253

Co-authored-by: Geoffrey <geoffrey.marc@consulting-for.edenred.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Geoffrey Marc
2026-06-01 11:29:59 +08:00
committed by GitHub
co-authored by Geoffrey Claude Opus 4.8
parent 632c48fde3
commit a3b0af9ac0
4 changed files with 76 additions and 13 deletions
@@ -1,18 +1,33 @@
import { useMemo, type ReactNode } from "react";
import { Text, View, type StyleProp, type TextStyle, type ViewStyle } from "react-native";
import {
Text,
View,
type StyleProp,
type TextProps,
type TextStyle,
type ViewStyle,
} from "react-native";
interface MarkdownTextSpanProps {
style?: StyleProp<TextStyle>;
monoSurface?: boolean;
children: ReactNode;
onPress?: TextProps["onPress"];
accessibilityRole?: TextProps["accessibilityRole"];
}
// Android's <Text selectable> enables per-text-node selection natively. Each
// sibling Text is its own selection scope — drag can't span across siblings
// (that requires a single UITextView ancestor and is iOS-only).
export function MarkdownTextSpan({ style, children }: MarkdownTextSpanProps) {
// (that requires a single UITextView ancestor and is iOS-only). onPress works
// natively here, so links routed through this span stay tappable on Android.
export function MarkdownTextSpan({
style,
children,
onPress,
accessibilityRole,
}: MarkdownTextSpanProps) {
return (
<Text selectable style={style}>
<Text selectable style={style} onPress={onPress} accessibilityRole={accessibilityRole}>
{children}
</Text>
);