Bluesky app fork with some witchin' additions 馃挮
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 116 lines 2.8 kB view raw
1import {Children} from 'react' 2import { 3 type StyleProp, 4 type TextProps as RNTextProps, 5 type TextStyle, 6} from 'react-native' 7import {UITextView} from 'react-native-uitextview' 8import createEmojiRegex from 'emoji-regex' 9 10import {type Alf, applyFonts, atoms, flatten} from '#/alf' 11import {IS_IOS, IS_NATIVE} from '#/env' 12 13/** 14 * Ensures that `lineHeight` defaults to a relative value of `1`, or applies 15 * other relative leading atoms. 16 * 17 * If the `lineHeight` value is > 2, we assume it's an absolute value and 18 * returns it as-is. 19 */ 20export function normalizeTextStyles( 21 styles: StyleProp<TextStyle>, 22 { 23 fontScale, 24 fontFamily, 25 }: { 26 fontScale: number 27 fontFamily: Alf['fonts']['family'] 28 } & Pick<Alf, 'flags'>, 29) { 30 const s = flatten(styles) ?? {} 31 32 // should always be defined on these components 33 s.fontSize = (s.fontSize || atoms.text_md.fontSize) * fontScale 34 35 if (s?.lineHeight) { 36 if (s.lineHeight !== 0 && s.lineHeight <= 2) { 37 s.lineHeight = Math.round(s.fontSize * s.lineHeight) 38 } 39 } else if (!IS_NATIVE) { 40 s.lineHeight = s.fontSize 41 } 42 43 applyFonts(s, fontFamily) 44 45 return s 46} 47 48export type StringChild = string | (string | null)[] 49export type TextProps = RNTextProps & { 50 /** 51 * Lets the user select text, to use the native copy and paste functionality. 52 */ 53 selectable?: boolean 54 /** 55 * Provides `data-*` attributes to the underlying `UITextView` component on 56 * web only. 57 */ 58 dataSet?: Record<string, string | number | undefined> 59 /** 60 * Appears as a small tooltip on web hover. 61 */ 62 title?: string 63 /** 64 * Whether the children could possibly contain emoji. 65 */ 66 emoji?: boolean 67} 68 69const EMOJI = createEmojiRegex() 70 71export function childHasEmoji(children: React.ReactNode) { 72 let hasEmoji = false 73 Children.forEach(children, child => { 74 if (typeof child === 'string' && createEmojiRegex().test(child)) { 75 hasEmoji = true 76 } 77 }) 78 return hasEmoji 79} 80 81export function renderChildrenWithEmoji( 82 children: React.ReactNode, 83 props: Omit<TextProps, 'children'> = {}, 84 emoji: boolean, 85) { 86 if (!IS_IOS || !emoji) { 87 return children 88 } 89 return Children.map(children, child => { 90 if (typeof child !== 'string') return child 91 92 const emojis = child.match(EMOJI) 93 94 if (emojis === null) { 95 return child 96 } 97 98 return child.split(EMOJI).map((stringPart, index) => [ 99 stringPart, 100 emojis[index] ? ( 101 <UITextView 102 {...props} 103 style={[props?.style, {fontFamily: 'System'}]} 104 key={index}> 105 {emojis[index]} 106 </UITextView> 107 ) : null, 108 ]) 109 }) 110} 111 112const SINGLE_EMOJI_RE = 113 /^[\p{Emoji_Presentation}\p{Extended_Pictographic}\uFE0F\u200D]+$/u 114export function isOnlyEmoji(text: string) { 115 return text.length <= 15 && SINGLE_EMOJI_RE.test(text) 116}