diff --git a/app/containers/Emoji/Emoji.tsx b/app/containers/Emoji/Emoji.tsx new file mode 100644 index 00000000000..bd6fa9fbd87 --- /dev/null +++ b/app/containers/Emoji/Emoji.tsx @@ -0,0 +1,73 @@ +import React from 'react'; +import { Text, useWindowDimensions } from 'react-native'; +import type { StyleProp, TextStyle, ImageStyle } from 'react-native'; + +import { useTheme } from '../../theme'; +import useShortnameToUnicode from '../../lib/hooks/useShortnameToUnicode'; +import CustomEmoji from '../EmojiPicker/CustomEmoji'; +import { useResponsiveLayout } from '../../lib/hooks/useResponsiveLayout/useResponsiveLayout'; + +interface ISharedEmojiProps { + literal?: string; + isBigEmoji?: boolean; + style?: StyleProp; + isAvatar?: boolean; + getCustomEmoji?: (name: string) => any; + customEmoji?: any; +} + +const Emoji = ({ literal, isBigEmoji, style, isAvatar, getCustomEmoji, customEmoji }: ISharedEmojiProps) => { + const { colors } = useTheme(); + const { formatShortnameToUnicode } = useShortnameToUnicode(); + const { fontScale } = useWindowDimensions(); + const { fontScaleLimited } = useResponsiveLayout(); + + + // Calculate emoji sizes once to avoid duplication + const customEmojiSize = { + width: 15 * fontScale, + height: 15 * fontScale + }; + const customEmojiBigSize = { + width: 30 * fontScale, + height: 30 * fontScale + }; + + if (customEmoji) { + return ]} emoji={customEmoji} />; + } + + if (!literal) { + return null; + } + + const emojiUnicode = formatShortnameToUnicode(literal); + const emojiName = literal.replace(/:/g, ''); + const foundCustomEmoji = getCustomEmoji?.(emojiName); + + if (foundCustomEmoji) { + return ]} emoji={foundCustomEmoji} />; + } + + const avatarStyle = { + fontSize: 30 * fontScaleLimited, + lineHeight: 30 * fontScaleLimited, + textAlign: 'center' as const + }; + + return ( + + {emojiUnicode} + + ); +}; + +export default Emoji; diff --git a/app/containers/Emoji/__tests__/Emoji.test.tsx b/app/containers/Emoji/__tests__/Emoji.test.tsx new file mode 100644 index 00000000000..c5df64f072e --- /dev/null +++ b/app/containers/Emoji/__tests__/Emoji.test.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import { render } from '@testing-library/react-native'; + +import Emoji from '../Emoji'; + +// Mock dependencies +jest.mock('../../../lib/hooks/useShortnameToUnicode', () => ({ + __esModule: true, + default: () => ({ + formatShortnameToUnicode: (str: string) => (str === ':smile:' ? '😄' : str) + }) +})); + +jest.mock('../../EmojiPicker/CustomEmoji', () => { + const { View } = require('react-native'); + return (props: any) => ; +}); + +jest.mock('../../../theme', () => ({ + useTheme: () => ({ colors: { fontDefault: 'black' } }) +})); + +jest.mock('../../../lib/hooks/useResponsiveLayout/useResponsiveLayout', () => ({ + useResponsiveLayout: () => ({ fontScaleLimited: 1 }) +})); + +describe('Emoji', () => { + it('renders standard emoji correctly', () => { + const { getByText } = render(); + expect(getByText('😄')).toBeTruthy(); + }); + + it('renders custom emoji correctly', () => { + const customEmoji = { name: 'party_parrot', extension: 'gif' }; + const { getByTestId } = render(); + expect(getByTestId('mock-custom-emoji')).toBeTruthy(); + }); +}); diff --git a/app/containers/Emoji/index.ts b/app/containers/Emoji/index.ts new file mode 100644 index 00000000000..9ce6dcb23c0 --- /dev/null +++ b/app/containers/Emoji/index.ts @@ -0,0 +1 @@ +export { default as Emoji } from './Emoji'; diff --git a/app/containers/EmojiPicker/Emoji.tsx b/app/containers/EmojiPicker/Emoji.tsx index dfee5e54049..fbfedea3296 100644 --- a/app/containers/EmojiPicker/Emoji.tsx +++ b/app/containers/EmojiPicker/Emoji.tsx @@ -1,17 +1,12 @@ import React from 'react'; -import { Text } from 'react-native'; -import useShortnameToUnicode from '../../lib/hooks/useShortnameToUnicode'; import styles from './styles'; -import CustomEmoji from './CustomEmoji'; import { type IEmojiProps } from './interfaces'; +import SharedEmoji from '../Emoji/Emoji'; export const Emoji = ({ emoji }: IEmojiProps): React.ReactElement => { - const { formatShortnameToUnicode } = useShortnameToUnicode(true); - const unicodeEmoji = formatShortnameToUnicode(`:${emoji}:`); - if (typeof emoji === 'string') { - return {unicodeEmoji}; + return ; } - return ; + return ; }; diff --git a/app/containers/markdown/__snapshots__/Markdown.test.tsx.snap b/app/containers/markdown/__snapshots__/Markdown.test.tsx.snap index faa65cf2e84..7e34dc28d4d 100644 --- a/app/containers/markdown/__snapshots__/Markdown.test.tsx.snap +++ b/app/containers/markdown/__snapshots__/Markdown.test.tsx.snap @@ -488,13 +488,11 @@ exports[`Story Snapshots: Emoji should match snapshot 1`] = ` "color": "#2F343D", }, { - "backgroundColor": "transparent", - "fontFamily": "Inter", "fontSize": 16, - "fontWeight": "400", "lineHeight": 22, - "textAlign": "left", }, + undefined, + undefined, ] } > @@ -507,13 +505,11 @@ exports[`Story Snapshots: Emoji should match snapshot 1`] = ` "color": "#2F343D", }, { - "backgroundColor": "transparent", - "fontFamily": "Inter", "fontSize": 16, - "fontWeight": "400", "lineHeight": 22, - "textAlign": "left", }, + undefined, + undefined, ] } > @@ -526,13 +522,11 @@ exports[`Story Snapshots: Emoji should match snapshot 1`] = ` "color": "#2F343D", }, { - "backgroundColor": "transparent", - "fontFamily": "Inter", "fontSize": 16, - "fontWeight": "400", "lineHeight": 22, - "textAlign": "left", }, + undefined, + undefined, ] } > @@ -600,19 +594,14 @@ exports[`Story Snapshots: Emoji should match snapshot 1`] = ` "color": "#2F343D", }, { - "backgroundColor": "transparent", - "fontFamily": "Inter", "fontSize": 16, - "fontWeight": "400", "lineHeight": 22, - "textAlign": "left", }, - {}, - false, + undefined, + undefined, ] } > - 😂 - 👍 @@ -863,13 +847,11 @@ exports[`Story Snapshots: Emoji should match snapshot 1`] = ` "color": "#2F343D", }, { - "backgroundColor": "transparent", - "fontFamily": "Inter", "fontSize": 30, - "fontWeight": "400", "lineHeight": 43, - "textAlign": "left", }, + undefined, + undefined, ] } > @@ -882,15 +864,11 @@ exports[`Story Snapshots: Emoji should match snapshot 1`] = ` "color": "#2F343D", }, { - "backgroundColor": "transparent", - "fontFamily": "Inter", "fontSize": 30, - "fontWeight": "400", "lineHeight": 43, - "textAlign": "left", }, - {}, - false, + undefined, + undefined, ] } > diff --git a/app/containers/markdown/components/emoji/Emoji.tsx b/app/containers/markdown/components/emoji/Emoji.tsx index 1d02aa2c852..783631db5ed 100644 --- a/app/containers/markdown/components/emoji/Emoji.tsx +++ b/app/containers/markdown/components/emoji/Emoji.tsx @@ -1,16 +1,8 @@ import React, { useContext } from 'react'; -import { Text, useWindowDimensions } from 'react-native'; import { type Emoji as EmojiProps } from '@rocket.chat/message-parser'; -import Plain from '../Plain'; -import useShortnameToUnicode from '../../../../lib/hooks/useShortnameToUnicode'; -import { useTheme } from '../../../../theme'; -import styles from '../../styles'; -import CustomEmoji from '../../../EmojiPicker/CustomEmoji'; import MarkdownContext from '../../contexts/MarkdownContext'; -import { useAppSelector } from '../../../../lib/hooks/useAppSelector'; -import { getUserSelector } from '../../../../selectors/login'; -import { useResponsiveLayout } from '../../../../lib/hooks/useResponsiveLayout/useResponsiveLayout'; +import SharedEmoji from '../../../Emoji/Emoji'; interface IEmojiProps { block: EmojiProps; @@ -20,69 +12,21 @@ interface IEmojiProps { isAvatar?: boolean; } -function getEmojiToken(block: EmojiProps, isAvatar: boolean) { +const getEmojiToken = (block: EmojiProps, isAvatar: boolean) => { if ('unicode' in block) { return block.unicode; } - if (isAvatar) { return block.value?.value; } - return block?.shortCode ? `:${block.shortCode}:` : `:${block.value?.value}:`; -} +}; -const Emoji = ({ block, isBigEmoji, style = {}, index, isAvatar = false }: IEmojiProps) => { - const { colors } = useTheme(); +const Emoji = ({ block, isBigEmoji, style, isAvatar }: IEmojiProps) => { const { getCustomEmoji } = useContext(MarkdownContext); - const { fontScale } = useWindowDimensions(); - const { fontScaleLimited } = useResponsiveLayout(); - const { formatShortnameToUnicode } = useShortnameToUnicode(); - const spaceLeft = index && index > 0 ? ' ' : ''; - const convertAsciiEmoji = useAppSelector(state => getUserSelector(state)?.settings?.preferences?.convertAsciiEmoji); - - if ('unicode' in block) { - return {block.unicode}; - } - - const emojiToken = getEmojiToken(block, isAvatar); - const emojiUnicode = formatShortnameToUnicode(emojiToken); - const emoji = getCustomEmoji?.(block.value?.value.replace(/\:/g, '')); - const isAsciiEmoji = !!block?.shortCode && block.value?.value !== block?.shortCode; - const displayAsciiEmoji = !convertAsciiEmoji && isAsciiEmoji && !!block.value; - const customEmojiSize = { - width: 15 * fontScale, - height: 15 * fontScale - }; - - const customEmojiBigSize = { - width: 30 * fontScale, - height: 30 * fontScale - }; - - const avatarStyle = { - fontSize: 30 * fontScaleLimited, - lineHeight: 30 * fontScaleLimited, - textAlign: 'center', - textAlignVertical: 'center' - }; - - if (emoji) { - return ; - } + const literal = getEmojiToken(block, isAvatar ?? false); - return ( - - {spaceLeft} - {displayAsciiEmoji ? : emojiUnicode} - - ); + return ; }; export default Emoji;