diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/EmojiRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/EmojiRenderer.tsx index 6582e99477a8..9ad138444b9c 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/EmojiRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/EmojiRenderer.tsx @@ -8,7 +8,7 @@ function EmojiRenderer({tnode}: CustomRendererProps) { const style = 'islarge' in tnode.attributes ? styles.onlyEmojisText : {}; return ( ); diff --git a/src/components/InlineCodeBlock/WrappedText.tsx b/src/components/InlineCodeBlock/WrappedText.tsx index 3c196f2a7492..3045c15c471b 100644 --- a/src/components/InlineCodeBlock/WrappedText.tsx +++ b/src/components/InlineCodeBlock/WrappedText.tsx @@ -3,6 +3,7 @@ import type {StyleProp, TextStyle, ViewStyle} from 'react-native'; import {View} from 'react-native'; import Text from '@components/Text'; import useThemeStyles from '@hooks/useThemeStyles'; +import {containsOnlyEmojis} from '@libs/EmojiUtils'; import CONST from '@src/CONST'; import type ChildrenProps from '@src/types/utils/ChildrenProps'; @@ -36,7 +37,7 @@ function getTextMatrix(text: string): string[][] { * Validates if the text contains any emoji */ function containsEmoji(text: string): boolean { - return CONST.REGEX.EMOJI.test(text); + return CONST.REGEX.EMOJIS.test(text); } function WrappedText({children, wordStyles, textStyles}: WrappedTextProps) { @@ -61,7 +62,21 @@ function WrappedText({children, wordStyles, textStyles}: WrappedTextProps) { style={styles.codeWordWrapper} > - {colText} + + {Array.from(colText).map((char, charIndex) => + containsOnlyEmojis(char) ? ( + + {char} + + ) : ( + char + ), + )} + ))} diff --git a/src/components/InlineCodeBlock/getCurrentData.ts b/src/components/InlineCodeBlock/getCurrentData.ts deleted file mode 100644 index 591ec74c885d..000000000000 --- a/src/components/InlineCodeBlock/getCurrentData.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type {TDefaultRendererProps} from 'react-native-render-html'; -import type {TTextOrTPhrasing} from './types'; - -// Create a temporary solution to display when there are emojis in the inline code block -// We can remove this after https://github.com/Expensify/App/issues/14676 is fixed -export default function getCurrentData(defaultRendererProps: TDefaultRendererProps): string { - if ('data' in defaultRendererProps.tnode) { - return defaultRendererProps.tnode.data; - } - return defaultRendererProps.tnode.children.map((child) => ('data' in child ? child.data : '')).join(''); -} diff --git a/src/components/InlineCodeBlock/index.native.tsx b/src/components/InlineCodeBlock/index.native.tsx index 1c8a1bea4312..048bcfc960bd 100644 --- a/src/components/InlineCodeBlock/index.native.tsx +++ b/src/components/InlineCodeBlock/index.native.tsx @@ -1,10 +1,25 @@ import React from 'react'; +import type {TDefaultRendererProps} from 'react-native-render-html'; import useThemeStyles from '@hooks/useThemeStyles'; -import getCurrentData from './getCurrentData'; import type InlineCodeBlockProps from './types'; import type {TTextOrTPhrasing} from './types'; import WrappedText from './WrappedText'; +/** + * Retrieves the text content from a Text or Phrasing node. + * + * @param defaultRendererProps - The default renderer props containing the node information. + * @returns The text content of the node. + * + * @template TTextOrTPhrasing + */ +function getCurrentData(defaultRendererProps: TDefaultRendererProps): string { + if ('data' in defaultRendererProps.tnode) { + return defaultRendererProps.tnode.data; + } + return defaultRendererProps.tnode.children.map((child) => ('data' in child ? child.data : '')).join(''); +} + function InlineCodeBlock({TDefaultRenderer, defaultRendererProps, textStyle, boxModelStyle}: InlineCodeBlockProps) { const styles = useThemeStyles(); const data = getCurrentData(defaultRendererProps); diff --git a/src/components/InlineCodeBlock/index.tsx b/src/components/InlineCodeBlock/index.tsx index 26a4e8b7a31f..e1a89719bb82 100644 --- a/src/components/InlineCodeBlock/index.tsx +++ b/src/components/InlineCodeBlock/index.tsx @@ -1,26 +1,75 @@ import React from 'react'; import {StyleSheet} from 'react-native'; +import type {StyleProp, TextStyle} from 'react-native'; +import type {TDefaultRendererProps} from 'react-native-render-html'; +import EmojiWithTooltip from '@components/EmojiWithTooltip'; import Text from '@components/Text'; -import getCurrentData from './getCurrentData'; -import type InlineCodeBlockProps from './types'; +import useThemeStyles from '@hooks/useThemeStyles'; +import type {ThemeStyles} from '@styles/index'; import type {TTextOrTPhrasing} from './types'; +import type InlineCodeBlockProps from './types'; + +/** + * This function is used to render elements based on the provided defaultRendererProps and styles. + * It iterates over the children of the tnode object in defaultRendererProps, and for each child, + * it checks if the child's tagName is 'emoji'. If it is, it creates an EmojiWithTooltip component + * with the appropriate styles and adds it to the elements array. If it's not, it adds the child's + * 'data' property to the elements array. The function then returns the elements array. + * + * @param defaultRendererProps - The default renderer props. + * @param textStyles - The text styles. + * @param styles - The theme styles. + * @returns The array of elements to be rendered. + */ +function renderElements(defaultRendererProps: TDefaultRendererProps, textStyles: StyleProp, styles: ThemeStyles) { + const elements: Array = []; + + if ('data' in defaultRendererProps.tnode) { + elements.push(defaultRendererProps.tnode.data); + return elements; + } + + if (!defaultRendererProps.tnode.children) { + return elements; + } + + defaultRendererProps.tnode.children.forEach((child) => { + if (!('data' in child)) { + return; + } + + if (child.tagName === 'emoji') { + elements.push( + , + ); + } else { + elements.push(child.data); + } + }); + + return elements; +} function InlineCodeBlock({TDefaultRenderer, textStyle, defaultRendererProps, boxModelStyle}: InlineCodeBlockProps) { + const styles = useThemeStyles(); const flattenTextStyle = StyleSheet.flatten(textStyle); const {textDecorationLine, ...textStyles} = flattenTextStyle; - const data = getCurrentData(defaultRendererProps); + const elements = renderElements(defaultRendererProps, textStyles, styles); return ( - {data} + {elements} ); } - InlineCodeBlock.displayName = 'InlineCodeBlock'; export default InlineCodeBlock; diff --git a/src/styles/index.ts b/src/styles/index.ts index 5accd3f966ff..8383d29e4863 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -19,6 +19,7 @@ import codeStyles from './utils/codeStyles'; import cursor from './utils/cursor'; import display from './utils/display'; import editedLabelStyles from './utils/editedLabelStyles'; +import emojiDefaultStyles from './utils/emojiDefaultStyles'; import flex from './utils/flex'; import FontUtils from './utils/FontUtils'; import getPopOverVerticalOffset from './utils/getPopOverVerticalOffset'; @@ -256,6 +257,7 @@ const styles = (theme: ThemeColors) => ...objectFit, ...textDecorationLine, editedLabelStyles, + emojiDefaultStyles, autoCompleteSuggestionsContainer: { backgroundColor: theme.appBG, diff --git a/src/styles/utils/emojiDefaultStyles/index.native.ts b/src/styles/utils/emojiDefaultStyles/index.native.ts new file mode 100644 index 000000000000..20bb887161d6 --- /dev/null +++ b/src/styles/utils/emojiDefaultStyles/index.native.ts @@ -0,0 +1,9 @@ +import type EmojiDefaultStyles from './types'; + +const emojiDefaultStyles: EmojiDefaultStyles = { + fontStyle: 'normal', + fontWeight: 'normal', + textDecorationLine: 'none', +}; + +export default emojiDefaultStyles; diff --git a/src/styles/utils/emojiDefaultStyles/index.ts b/src/styles/utils/emojiDefaultStyles/index.ts new file mode 100644 index 000000000000..a24424f6677c --- /dev/null +++ b/src/styles/utils/emojiDefaultStyles/index.ts @@ -0,0 +1,11 @@ +// eslint-disable-next-line no-restricted-imports +import display from '@styles/utils/display'; +import type EmojiDefaultStyles from './types'; + +const emojiDefaultStyles: EmojiDefaultStyles = { + fontStyle: 'normal', + fontWeight: 'normal', + ...display.dInlineFlex, +}; + +export default emojiDefaultStyles; diff --git a/src/styles/utils/emojiDefaultStyles/types.ts b/src/styles/utils/emojiDefaultStyles/types.ts new file mode 100644 index 000000000000..62537f95d0ac --- /dev/null +++ b/src/styles/utils/emojiDefaultStyles/types.ts @@ -0,0 +1,5 @@ +import type {TextStyle} from 'react-native'; + +type EmojiDefaultStyles = TextStyle; + +export default EmojiDefaultStyles;