diff options
-rw-r--r-- | src/alf/typography.tsx | 91 | ||||
-rw-r--r-- | src/components/RichText.tsx | 15 | ||||
-rw-r--r-- | src/components/Typography.tsx | 8 | ||||
-rw-r--r-- | src/components/dms/MessageItem.tsx | 3 | ||||
-rw-r--r-- | src/view/com/util/text/Text.tsx | 23 |
5 files changed, 51 insertions, 89 deletions
diff --git a/src/alf/typography.tsx b/src/alf/typography.tsx index d00910bf8..1b1e81bd4 100644 --- a/src/alf/typography.tsx +++ b/src/alf/typography.tsx @@ -1,10 +1,11 @@ -import React from 'react' +import React, {Children} from 'react' import {TextProps as RNTextProps} from 'react-native' import {StyleProp, TextStyle} from 'react-native' import {UITextView} from 'react-native-uitextview' import createEmojiRegex from 'emoji-regex' import {isNative} from '#/platform/detection' +import {isIOS} from '#/platform/detection' import {Alf, applyFonts, atoms, flatten} from '#/alf' /** @@ -57,7 +58,7 @@ export function normalizeTextStyles( } export type StringChild = string | (string | null)[] -export type TextProps = Omit<RNTextProps, 'children'> & { +export type TextProps = RNTextProps & { /** * Lets the user select text, to use the native copy and paste functionality. */ @@ -71,65 +72,55 @@ export type TextProps = Omit<RNTextProps, 'children'> & { * Appears as a small tooltip on web hover. */ title?: string -} & ( - | { - emoji?: true - children: StringChild - } - | { - emoji?: false - children: RNTextProps['children'] - } - ) + /** + * Whether the children could possibly contain emoji. + */ + emoji?: boolean +} const EMOJI = createEmojiRegex() export function childHasEmoji(children: React.ReactNode) { - return (Array.isArray(children) ? children : [children]).some( - child => typeof child === 'string' && createEmojiRegex().test(child), - ) -} - -export function childIsString( - children: React.ReactNode, -): children is StringChild { - return ( - typeof children === 'string' || - (Array.isArray(children) && - children.every(child => typeof child === 'string' || child === null)) - ) + let hasEmoji = false + Children.forEach(children, child => { + if (typeof child === 'string' && createEmojiRegex().test(child)) { + hasEmoji = true + } + }) + return hasEmoji } export function renderChildrenWithEmoji( - children: StringChild, + children: React.ReactNode, props: Omit<TextProps, 'children'> = {}, + emoji: boolean, ) { - const normalized = Array.isArray(children) ? children : [children] + if (!isIOS || !emoji) { + return children + } + return Children.map(children, child => { + if (typeof child !== 'string') return child - return ( - <UITextView {...props}> - {normalized.map(child => { - if (typeof child !== 'string') return child + const emojis = child.match(EMOJI) - const emojis = child.match(EMOJI) + if (emojis === null) { + return child + } - if (emojis === null) { - return child - } + return child.split(EMOJI).map((stringPart, index) => [ + stringPart, + emojis[index] ? ( + <UITextView + {...props} + style={[props?.style, {color: 'black', fontFamily: 'System'}]}> + {emojis[index]} + </UITextView> + ) : null, + ]) + }) +} - return child.split(EMOJI).map((stringPart, index) => ( - <UITextView key={index} {...props}> - {stringPart} - {emojis[index] ? ( - <UITextView - {...props} - style={[props?.style, {color: 'black', fontFamily: 'System'}]}> - {emojis[index]} - </UITextView> - ) : null} - </UITextView> - )) - })} - </UITextView> - ) +const SINGLE_EMOJI_RE = /^[\p{Emoji_Presentation}\p{Extended_Pictographic}]+$/u +export function isOnlyEmoji(text: string) { + return text.length <= 15 && SINGLE_EMOJI_RE.test(text) } diff --git a/src/components/RichText.tsx b/src/components/RichText.tsx index e2d05ac6c..6d7e50e48 100644 --- a/src/components/RichText.tsx +++ b/src/components/RichText.tsx @@ -9,6 +9,7 @@ import {NavigationProp} from '#/lib/routes/types' import {toShortUrl} from '#/lib/strings/url-helpers' import {isNative} from '#/platform/detection' import {atoms as a, flatten, native, TextStyleProp, useTheme, web} from '#/alf' +import {isOnlyEmoji} from '#/alf/typography' import {useInteractionState} from '#/components/hooks/useInteractionState' import {InlineLinkText, LinkProps} from '#/components/Link' import {ProfileHoverCard} from '#/components/ProfileHoverCard' @@ -150,17 +151,14 @@ export function RichText({ />, ) } else { - els.push( - <Text key={key} emoji style={plainStyles}> - {segment.text} - </Text>, - ) + els.push(segment.text) } key++ } return ( <Text + emoji selectable={selectable} testID={testID} style={plainStyles} @@ -250,10 +248,3 @@ function RichTextTag({ </React.Fragment> ) } - -export function isOnlyEmoji(text: string) { - return ( - text.length <= 15 && - /^[\p{Emoji_Presentation}\p{Extended_Pictographic}]+$/u.test(text) - ) -} diff --git a/src/components/Typography.tsx b/src/components/Typography.tsx index 4bcbf9f09..3e202cb8f 100644 --- a/src/components/Typography.tsx +++ b/src/components/Typography.tsx @@ -1,11 +1,9 @@ import {UITextView} from 'react-native-uitextview' import {logger} from '#/logger' -import {isIOS} from '#/platform/detection' import {atoms, flatten, useAlf, useTheme, web} from '#/alf' import { childHasEmoji, - childIsString, normalizeTextStyles, renderChildrenWithEmoji, TextProps, @@ -39,10 +37,6 @@ export function Text({ `Text: emoji detected but emoji not enabled: "${children}"\n\nPlease add <Text emoji />'`, ) } - - if (emoji && !childIsString(children)) { - logger.error('Text: when <Text emoji />, children can only be strings.') - } } const shared = { @@ -55,7 +49,7 @@ export function Text({ return ( <UITextView {...shared}> - {isIOS && emoji ? renderChildrenWithEmoji(children, shared) : children} + {renderChildrenWithEmoji(children, shared, emoji ?? false)} </UITextView> ) } diff --git a/src/components/dms/MessageItem.tsx b/src/components/dms/MessageItem.tsx index 52220e2ca..79f0997fd 100644 --- a/src/components/dms/MessageItem.tsx +++ b/src/components/dms/MessageItem.tsx @@ -19,10 +19,11 @@ import {ConvoItem} from '#/state/messages/convo/types' import {useSession} from '#/state/session' import {TimeElapsed} from '#/view/com/util/TimeElapsed' import {atoms as a, useTheme} from '#/alf' +import {isOnlyEmoji} from '#/alf/typography' import {ActionsWrapper} from '#/components/dms/ActionsWrapper' import {InlineLinkText} from '#/components/Link' import {Text} from '#/components/Typography' -import {isOnlyEmoji, RichText} from '../RichText' +import {RichText} from '../RichText' import {DateDivider} from './DateDivider' import {MessageItemEmbed} from './MessageItemEmbed' import {localDateString} from './util' diff --git a/src/view/com/util/text/Text.tsx b/src/view/com/util/text/Text.tsx index a5278e0a0..f05274f44 100644 --- a/src/view/com/util/text/Text.tsx +++ b/src/view/com/util/text/Text.tsx @@ -1,5 +1,5 @@ import React from 'react' -import {StyleSheet, Text as RNText, TextProps} from 'react-native' +import {StyleSheet, TextProps} from 'react-native' import {UITextView} from 'react-native-uitextview' import {lh, s} from '#/lib/styles' @@ -9,7 +9,6 @@ import {isIOS, isWeb} from '#/platform/detection' import {applyFonts, useAlf} from '#/alf' import { childHasEmoji, - childIsString, renderChildrenWithEmoji, StringChild, } from '#/alf/typography' @@ -56,10 +55,6 @@ function Text_DEPRECATED({ `Text: emoji detected but emoji not enabled: "${children}"\n\nPlease add <Text emoji />'`, ) } - - if (emoji && !childIsString(children)) { - logger.error('Text: when <Text emoji />, children can only be strings.') - } } const textProps = React.useMemo(() => { @@ -107,19 +102,9 @@ function Text_DEPRECATED({ type, ]) - if (selectable && isIOS) { - return ( - <UITextView {...textProps}> - {isIOS && emoji - ? renderChildrenWithEmoji(children, textProps) - : children} - </UITextView> - ) - } - return ( - <RNText {...textProps}> - {isIOS && emoji ? renderChildrenWithEmoji(children, textProps) : children} - </RNText> + <UITextView {...textProps}> + {renderChildrenWithEmoji(children, textProps, emoji ?? false)} + </UITextView> ) } |