import React from 'react' import {Text, TextStyle, StyleProp} from 'react-native' import {Link} from './Link' import {s} from '../../lib/styles' type TextSlice = [number, number] type Entity = { index: TextSlice type: string value: string } export function RichText({ text, entities, style, }: { text: string entities?: Entity[] style?: StyleProp }) { if (!entities?.length) { return {text} } if (!style) style = [] else if (!Array.isArray(style)) style = [style] entities.sort(sortByIndex) const segments = Array.from(toSegments(text, entities)) const els = [] let key = 0 for (const segment of segments) { if (typeof segment === 'string') { els.push( {segment} , ) } else { els.push( {segment.text} , ) } key++ } return <>{els} } function sortByIndex(a: Entity, b: Entity) { return a.index[0] - b.index[0] } function* toSegments(text: string, entities: Entity[]) { let cursor = 0 let i = 0 do { let currEnt = entities[i] if (cursor < currEnt.index[0]) { yield text.slice(cursor, currEnt.index[0]) } else if (cursor > currEnt.index[0]) { i++ continue } if (currEnt.index[0] < currEnt.index[1]) { let subtext = text.slice(currEnt.index[0], currEnt.index[1]) if ( !subtext.trim() || stripUsername(subtext) !== stripUsername(currEnt.value) ) { // dont yield links to empty strings or strings that don't match the entity value yield subtext } else { yield { entity: currEnt, text: subtext, } } } cursor = currEnt.index[1] i++ } while (i < entities.length) if (cursor < text.length) { yield text.slice(cursor, text.length) } } function stripUsername(v: string): string { return v.trim().replace('@', '') }