mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
at samuel/exp-cli 176 lines 4.6 kB view raw
1import React from 'react' 2import {type TextStyle} from 'react-native' 3import {AppBskyRichtextFacet, RichText as RichTextAPI} from '@atproto/api' 4 5import {toShortUrl} from '#/lib/strings/url-helpers' 6import {atoms as a, flatten, type TextStyleProp} from '#/alf' 7import {isOnlyEmoji} from '#/alf/typography' 8import {InlineLinkText, type LinkProps} from '#/components/Link' 9import {ProfileHoverCard} from '#/components/ProfileHoverCard' 10import {RichTextTag} from '#/components/RichTextTag' 11import {Text, type TextProps} from '#/components/Typography' 12 13const WORD_WRAP = {wordWrap: 1} 14 15export type RichTextProps = TextStyleProp & 16 Pick<TextProps, 'selectable' | 'onLayout' | 'onTextLayout'> & { 17 value: RichTextAPI | string 18 testID?: string 19 numberOfLines?: number 20 disableLinks?: boolean 21 enableTags?: boolean 22 authorHandle?: string 23 onLinkPress?: LinkProps['onPress'] 24 interactiveStyle?: TextStyle 25 emojiMultiplier?: number 26 shouldProxyLinks?: boolean 27 } 28 29export function RichText({ 30 testID, 31 value, 32 style, 33 numberOfLines, 34 disableLinks, 35 selectable, 36 enableTags = false, 37 authorHandle, 38 onLinkPress, 39 interactiveStyle, 40 emojiMultiplier = 1.85, 41 onLayout, 42 onTextLayout, 43 shouldProxyLinks, 44}: RichTextProps) { 45 const richText = React.useMemo( 46 () => 47 value instanceof RichTextAPI ? value : new RichTextAPI({text: value}), 48 [value], 49 ) 50 51 const flattenedStyle = flatten(style) 52 const plainStyles = [a.leading_snug, flattenedStyle] 53 const interactiveStyles = [ 54 a.leading_snug, 55 flatten(interactiveStyle), 56 flattenedStyle, 57 ] 58 59 const {text, facets} = richText 60 61 if (!facets?.length) { 62 if (isOnlyEmoji(text)) { 63 const fontSize = 64 (flattenedStyle.fontSize ?? a.text_sm.fontSize) * emojiMultiplier 65 return ( 66 <Text 67 emoji 68 selectable={selectable} 69 testID={testID} 70 style={[plainStyles, {fontSize}]} 71 onLayout={onLayout} 72 onTextLayout={onTextLayout} 73 // @ts-ignore web only -prf 74 dataSet={WORD_WRAP}> 75 {text} 76 </Text> 77 ) 78 } 79 return ( 80 <Text 81 emoji 82 selectable={selectable} 83 testID={testID} 84 style={plainStyles} 85 numberOfLines={numberOfLines} 86 onLayout={onLayout} 87 onTextLayout={onTextLayout} 88 // @ts-ignore web only -prf 89 dataSet={WORD_WRAP}> 90 {text} 91 </Text> 92 ) 93 } 94 95 const els = [] 96 let key = 0 97 // N.B. must access segments via `richText.segments`, not via destructuring 98 for (const segment of richText.segments()) { 99 const link = segment.link 100 const mention = segment.mention 101 const tag = segment.tag 102 if ( 103 mention && 104 AppBskyRichtextFacet.validateMention(mention).success && 105 !disableLinks 106 ) { 107 els.push( 108 <ProfileHoverCard key={key} did={mention.did}> 109 <InlineLinkText 110 selectable={selectable} 111 to={`/profile/${mention.did}`} 112 style={interactiveStyles} 113 // @ts-ignore TODO 114 dataSet={WORD_WRAP} 115 shouldProxy={shouldProxyLinks} 116 onPress={onLinkPress}> 117 {segment.text} 118 </InlineLinkText> 119 </ProfileHoverCard>, 120 ) 121 } else if (link && AppBskyRichtextFacet.validateLink(link).success) { 122 if (disableLinks) { 123 els.push(toShortUrl(segment.text)) 124 } else { 125 els.push( 126 <InlineLinkText 127 selectable={selectable} 128 key={key} 129 to={link.uri} 130 style={interactiveStyles} 131 // @ts-ignore TODO 132 dataSet={WORD_WRAP} 133 shareOnLongPress 134 shouldProxy={shouldProxyLinks} 135 onPress={onLinkPress} 136 emoji> 137 {toShortUrl(segment.text)} 138 </InlineLinkText>, 139 ) 140 } 141 } else if ( 142 !disableLinks && 143 enableTags && 144 tag && 145 AppBskyRichtextFacet.validateTag(tag).success 146 ) { 147 els.push( 148 <RichTextTag 149 key={key} 150 display={segment.text} 151 tag={tag.tag} 152 textStyle={interactiveStyles} 153 authorHandle={authorHandle} 154 />, 155 ) 156 } else { 157 els.push(segment.text) 158 } 159 key++ 160 } 161 162 return ( 163 <Text 164 emoji 165 selectable={selectable} 166 testID={testID} 167 style={plainStyles} 168 numberOfLines={numberOfLines} 169 onLayout={onLayout} 170 onTextLayout={onTextLayout} 171 // @ts-ignore web only -prf 172 dataSet={WORD_WRAP}> 173 {els} 174 </Text> 175 ) 176}