Skip to content

Commit

Permalink
Merge pull request #40617 from wildan-m/wildan/fix-14676-prevent-form…
Browse files Browse the repository at this point in the history
…at-emoji

Prevent format emoji On Submitted text
  • Loading branch information
stitesExpensify authored Apr 30, 2024
2 parents f6965a8 + 2fb2a49 commit b195e21
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ function EmojiRenderer({tnode}: CustomRendererProps<TText | TPhrasing>) {
const style = 'islarge' in tnode.attributes ? styles.onlyEmojisText : {};
return (
<EmojiWithTooltip
style={[style, styles.cursorDefault]}
style={[style, styles.cursorDefault, styles.emojiDefaultStyles]}
emojiCode={'data' in tnode ? tnode.data : ''}
/>
);
Expand Down
19 changes: 17 additions & 2 deletions src/components/InlineCodeBlock/WrappedText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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) {
Expand All @@ -61,7 +62,21 @@ function WrappedText({children, wordStyles, textStyles}: WrappedTextProps) {
style={styles.codeWordWrapper}
>
<View style={[wordStyles, colIndex === 0 && styles.codeFirstWordStyle, colIndex === rowText.length - 1 && styles.codeLastWordStyle]}>
<Text style={[textStyles, !containsEmoji(colText) && styles.codePlainTextStyle]}>{colText}</Text>
<Text style={[textStyles, !containsEmoji(colText) && styles.codePlainTextStyle]}>
{Array.from(colText).map((char, charIndex) =>
containsOnlyEmojis(char) ? (
<Text
// eslint-disable-next-line react/no-array-index-key
key={`${colIndex}-${charIndex}`}
style={[textStyles, styles.emojiDefaultStyles]}
>
{char}
</Text>
) : (
char
),
)}
</Text>
</View>
</View>
))}
Expand Down
11 changes: 0 additions & 11 deletions src/components/InlineCodeBlock/getCurrentData.ts

This file was deleted.

17 changes: 16 additions & 1 deletion src/components/InlineCodeBlock/index.native.tsx
Original file line number Diff line number Diff line change
@@ -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<TTextOrTPhrasing>): string {
if ('data' in defaultRendererProps.tnode) {
return defaultRendererProps.tnode.data;
}
return defaultRendererProps.tnode.children.map((child) => ('data' in child ? child.data : '')).join('');
}

function InlineCodeBlock<TComponent extends TTextOrTPhrasing>({TDefaultRenderer, defaultRendererProps, textStyle, boxModelStyle}: InlineCodeBlockProps<TComponent>) {
const styles = useThemeStyles();
const data = getCurrentData(defaultRendererProps);
Expand Down
59 changes: 54 additions & 5 deletions src/components/InlineCodeBlock/index.tsx
Original file line number Diff line number Diff line change
@@ -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<TTextOrTPhrasing>, textStyles: StyleProp<TextStyle>, styles: ThemeStyles) {
const elements: Array<string | React.JSX.Element> = [];

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(
<EmojiWithTooltip
style={[textStyles, styles.cursorDefault, styles.emojiDefaultStyles]}
key={child.data}
emojiCode={child.data}
/>,
);
} else {
elements.push(child.data);
}
});

return elements;
}

function InlineCodeBlock<TComponent extends TTextOrTPhrasing>({TDefaultRenderer, textStyle, defaultRendererProps, boxModelStyle}: InlineCodeBlockProps<TComponent>) {
const styles = useThemeStyles();
const flattenTextStyle = StyleSheet.flatten(textStyle);
const {textDecorationLine, ...textStyles} = flattenTextStyle;

const data = getCurrentData(defaultRendererProps);
const elements = renderElements(defaultRendererProps, textStyles, styles);

return (
<TDefaultRenderer
// eslint-disable-next-line react/jsx-props-no-spreading
{...defaultRendererProps}
>
<Text style={[boxModelStyle, textStyles]}>{data}</Text>
<Text style={[boxModelStyle, textStyles]}>{elements}</Text>
</TDefaultRenderer>
);
}

InlineCodeBlock.displayName = 'InlineCodeBlock';

export default InlineCodeBlock;
2 changes: 2 additions & 0 deletions src/styles/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -256,6 +257,7 @@ const styles = (theme: ThemeColors) =>
...objectFit,
...textDecorationLine,
editedLabelStyles,
emojiDefaultStyles,

autoCompleteSuggestionsContainer: {
backgroundColor: theme.appBG,
Expand Down
9 changes: 9 additions & 0 deletions src/styles/utils/emojiDefaultStyles/index.native.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type EmojiDefaultStyles from './types';

const emojiDefaultStyles: EmojiDefaultStyles = {
fontStyle: 'normal',
fontWeight: 'normal',
textDecorationLine: 'none',
};

export default emojiDefaultStyles;
11 changes: 11 additions & 0 deletions src/styles/utils/emojiDefaultStyles/index.ts
Original file line number Diff line number Diff line change
@@ -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;
5 changes: 5 additions & 0 deletions src/styles/utils/emojiDefaultStyles/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type {TextStyle} from 'react-native';

type EmojiDefaultStyles = TextStyle;

export default EmojiDefaultStyles;

0 comments on commit b195e21

Please sign in to comment.