From 2fcb0c43f630dc2f54cf0d0ca70ef3bc716960cc Mon Sep 17 00:00:00 2001 From: kbieganowski Date: Thu, 16 May 2024 13:56:28 +0200 Subject: [PATCH 01/28] rebase wip --- src/components/Composer/index.native.tsx | 6 +- src/components/Composer/index.tsx | 7 +- src/hooks/useMarkdownStyle.ts | 8 +- src/libs/EmojiUtils.ts | 115 ++++++++++++++++++ .../home/report/ReportActionItemFragment.tsx | 25 +++- .../report/comment/TextCommentFragment.tsx | 73 ++++++----- .../report/comment/TextWithEmojiFragment.tsx | 71 +++++++++++ src/styles/index.ts | 10 +- src/styles/variables.ts | 2 + 9 files changed, 272 insertions(+), 45 deletions(-) create mode 100644 src/pages/home/report/comment/TextWithEmojiFragment.tsx diff --git a/src/components/Composer/index.native.tsx b/src/components/Composer/index.native.tsx index ac7eb95191ee..22255e5ce92c 100644 --- a/src/components/Composer/index.native.tsx +++ b/src/components/Composer/index.native.tsx @@ -10,6 +10,7 @@ import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import updateIsFullComposerAvailable from '@libs/ComposerUtils/updateIsFullComposerAvailable'; +import {containsOnlyEmojis} from '@libs/EmojiUtils'; import type {ComposerProps} from './types'; function Composer( @@ -34,8 +35,9 @@ function Composer( ) { const textInput = useRef(null); const {isFocused, shouldResetFocus} = useResetComposerFocus(textInput); + const textContainsOnlyEmojis = containsOnlyEmojis(value ?? ''); const theme = useTheme(); - const markdownStyle = useMarkdownStyle(value, !isGroupPolicyReport ? ['mentionReport'] : []); + const markdownStyle = useMarkdownStyle(textContainsOnlyEmojis, !isGroupPolicyReport ? ['mentionReport'] : []); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -66,7 +68,7 @@ function Composer( }, [shouldClear, onClear]); const maxHeightStyle = useMemo(() => StyleUtils.getComposerMaxHeightStyle(maxLines, isComposerFullSize), [StyleUtils, isComposerFullSize, maxLines]); - const composerStyle = useMemo(() => StyleSheet.flatten(style), [style]); + const composerStyle = useMemo(() => StyleSheet.flatten([style, textContainsOnlyEmojis ? {lineHeight: 32} : null]), [style, textContainsOnlyEmojis]); return ( , ) { + const textContainsOnlyEmojis = containsOnlyEmojis(value ?? ''); const theme = useTheme(); const styles = useThemeStyles(); - const markdownStyle = useMarkdownStyle(value, !isGroupPolicyReport ? ['mentionReport'] : []); + const markdownStyle = useMarkdownStyle(textContainsOnlyEmojis, !isGroupPolicyReport ? ['mentionReport'] : []); const StyleUtils = useStyleUtils(); const textRef = useRef(null); const textInput = useRef(null); @@ -314,9 +316,10 @@ function Composer( scrollStyleMemo, StyleUtils.getComposerMaxHeightStyle(maxLines, isComposerFullSize), isComposerFullSize ? ({height: '100%', maxHeight: 'none' as DimensionValue} as TextStyle) : undefined, + textContainsOnlyEmojis ? {paddingBottom: 0} : null, ], - [style, styles.rtlTextRenderForSafari, scrollStyleMemo, StyleUtils, maxLines, isComposerFullSize], + [style, styles.rtlTextRenderForSafari, scrollStyleMemo, StyleUtils, maxLines, isComposerFullSize, textContainsOnlyEmojis], ); return ( diff --git a/src/hooks/useMarkdownStyle.ts b/src/hooks/useMarkdownStyle.ts index 57d17ffefb0b..898be6a0a8cc 100644 --- a/src/hooks/useMarkdownStyle.ts +++ b/src/hooks/useMarkdownStyle.ts @@ -1,13 +1,11 @@ import type {MarkdownStyle} from '@expensify/react-native-live-markdown'; import {useMemo} from 'react'; -import {containsOnlyEmojis} from '@libs/EmojiUtils'; import FontUtils from '@styles/utils/FontUtils'; import variables from '@styles/variables'; import useTheme from './useTheme'; -function useMarkdownStyle(message: string | null = null, excludeStyles: Array = []): MarkdownStyle { +function useMarkdownStyle(containsEmojisOnly?: boolean, excludeStyles: Array = []): MarkdownStyle { const theme = useTheme(); - const emojiFontSize = containsOnlyEmojis(message ?? '') ? variables.fontSizeOnlyEmojis : variables.fontSizeNormal; // this map is used to reset the styles that are not needed - passing undefined value can break the native side const nonStylingDefaultValues: Record = useMemo( @@ -34,7 +32,7 @@ function useMarkdownStyle(message: string | null = null, excludeStyles: Array { + const highOffset = pair.charCodeAt(0) - highSurrogateStart; + const lowOffset = pair.charCodeAt(1) - lowSurrogateStart; + // eslint-disable-next-line no-bitwise + return (highOffset << 10) + lowOffset + 0x10000; +}; + +const isZeroWidthJoiner = (text: string) => text?.charCodeAt(0) === zeroWidthJoiner; +const isWithinInclusiveRange = (value: number, lower: number, upper: number) => value >= lower && value <= upper; +const isFirstOfSurrogatePair = (text: string) => isWithinInclusiveRange(text?.[0].charCodeAt(0), highSurrogateStart, highSurrogateEnd); +const isRegionalIndicator = (text: string) => isWithinInclusiveRange(codePointFromSurrogatePair(text), regionalIndicatorStart, regionalIndicatorEnd); +const isFitzpatrickModifier = (text: string) => isWithinInclusiveRange(codePointFromSurrogatePair(text), fitzpatrickScaleStart, fitzpatrickScaleEnd); +const isVariationSelector = (text: string) => isWithinInclusiveRange(text?.charCodeAt(0), variationModifierStart, variationModifierEnd); + +// Define how many code units make up the character +const nextUnits = (i: number, text: string) => { + const current = text[i]; + // If a value at index is not part of a surrogate pair, or it is at the end take value at i + if (!isFirstOfSurrogatePair(current) || i === text.length - 1) { + return 1; + } + + const currentPair = current + text[i + 1]; + const nextPair = text.substring(i + 2, i + 5); + + if (isRegionalIndicator(currentPair) && isRegionalIndicator(nextPair)) { + return 4; // Flags (combination of 2 regional indicators) + } + + if (isFitzpatrickModifier(nextPair)) { + return 4; // Skin tones + } + return 2; // Variations and non-BMP characters +}; + +const splitTextWithEmojis = (text: string): string[] => { + if (!text) { + return []; + } + + let tmpString = ''; + let i = 0; + let increment = 0; + const tmpResult: string[] = []; + const processedArray: string[] = []; + while (i < text.length) { + increment += nextUnits(i + increment, text); + if (isVariationSelector(text[i + increment])) { + increment++; + } + if (isZeroWidthJoiner(text[i + increment])) { + increment++; + // eslint-disable-next-line no-continue -- without continue we would separate surrogate pair + continue; + } + tmpResult.push(text.substring(i, i + increment)); + i += increment; + increment = 0; + } + + for (let j = 0; j <= tmpResult.length; j++) { + if (!tmpResult[j]?.codePointAt(0)) { + // eslint-disable-next-line no-continue -- prevent error for empty chars + continue; + } + if (tmpResult[j] === ' ') { + tmpString += tmpResult[j]; + processedArray.push(tmpString); + tmpString = ''; + // eslint-disable-next-line no-continue -- skip rest of the checks in current iteration + continue; + } + // @ts-expect-error -- comments contain only BMP characters and emojis so codePointAt will return number + if (tmpResult[j].codePointAt(0) <= 0xffff) { + // is BMP character + tmpString += tmpResult[j]; + if (j === tmpResult.length - 1) { + processedArray.push(tmpString); + } + } else { + processedArray.push(tmpString); + processedArray.push(tmpResult[j]); + tmpString = ''; + } + } + // remove empty characters from array + return processedArray.filter((item) => item); +}; + export type {HeaderIndice, EmojiPickerList, EmojiSpacer, EmojiPickerListItem}; export { @@ -595,4 +709,5 @@ export { hasAccountIDEmojiReacted, getRemovedSkinToneEmoji, getSpacersIndexes, + splitTextWithEmojis, }; diff --git a/src/pages/home/report/ReportActionItemFragment.tsx b/src/pages/home/report/ReportActionItemFragment.tsx index de9e0b6a6ece..fb763ac889e8 100644 --- a/src/pages/home/report/ReportActionItemFragment.tsx +++ b/src/pages/home/report/ReportActionItemFragment.tsx @@ -7,6 +7,7 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; import convertToLTR from '@libs/convertToLTR'; +import {splitTextWithEmojis} from '@libs/EmojiUtils'; import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; @@ -156,18 +157,30 @@ function ReportActionItemFragment({ ); } + const containEmoji = CONST.REGEX.EMOJIS.test(fragment.text); + let processedTextArray: string[] = []; + if (containEmoji) { + processedTextArray = splitTextWithEmojis(fragment.text); + } + return ( - - {fragment?.text} - + {containEmoji ? ( + + {processedTextArray.map((word: string) => (CONST.REGEX.EMOJIS.test(word) ? {word} : word))} + + ) : ( + + {fragment?.text} + + )} ); } diff --git a/src/pages/home/report/comment/TextCommentFragment.tsx b/src/pages/home/report/comment/TextCommentFragment.tsx index cf1ab6f8aa19..62eee66e4653 100644 --- a/src/pages/home/report/comment/TextCommentFragment.tsx +++ b/src/pages/home/report/comment/TextCommentFragment.tsx @@ -10,13 +10,14 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import convertToLTR from '@libs/convertToLTR'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; -import * as EmojiUtils from '@libs/EmojiUtils'; +import {containsOnlyEmojis} from '@libs/EmojiUtils'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import type {OriginalMessageSource} from '@src/types/onyx/OriginalMessage'; import type {Message} from '@src/types/onyx/ReportAction'; import RenderCommentHTML from './RenderCommentHTML'; import shouldRenderAsText from './shouldRenderAsText'; +import TextWithEmojiFragment from './TextWithEmojiFragment'; type TextCommentFragmentProps = { /** The reportAction's source */ @@ -44,19 +45,19 @@ type TextCommentFragmentProps = { function TextCommentFragment({fragment, styleAsDeleted, styleAsMuted = false, source, style, displayAsGroup, iouMessage = ''}: TextCommentFragmentProps) { const theme = useTheme(); const styles = useThemeStyles(); - const {html = '', text} = fragment ?? {}; + const {html = '', text = ''} = fragment ?? {}; const {translate} = useLocalize(); const {isSmallScreenWidth} = useWindowDimensions(); // If the only difference between fragment.text and fragment.html is
tags and emoji tag // on native, we render it as text, not as html // on other device, only render it as text if the only difference is
tag - const containsOnlyEmojis = EmojiUtils.containsOnlyEmojis(text ?? ''); - if (!shouldRenderAsText(html, text ?? '') && !(containsOnlyEmojis && styleAsDeleted)) { + const textContainsOnlyEmojis = containsOnlyEmojis(text ?? ''); + if (!shouldRenderAsText(html, text ?? '') && !(textContainsOnlyEmojis && styleAsDeleted)) { const editedTag = fragment?.isEdited ? `` : ''; const htmlWithDeletedTag = styleAsDeleted ? `${html}` : html; - const htmlContent = containsOnlyEmojis ? Str.replaceAll(htmlWithDeletedTag, '', '') : htmlWithDeletedTag; + const htmlContent = textContainsOnlyEmojis ? Str.replaceAll(htmlWithDeletedTag, '', '') : htmlWithDeletedTag; let htmlWithTag = editedTag ? `${htmlContent}${editedTag}` : htmlContent; if (styleAsMuted) { @@ -74,38 +75,52 @@ function TextCommentFragment({fragment, styleAsDeleted, styleAsMuted = false, so const message = isEmpty(iouMessage) ? text : iouMessage; return ( - + - - {convertToLTR(message ?? '')} - - {fragment?.isEdited && ( + {CONST.REGEX.EMOJIS.test(message ?? '') && !textContainsOnlyEmojis ? ( + + ) : ( <> - {' '} - - - {translate('reportActionCompose.edited')} + {convertToLTR(message ?? '')} + {fragment?.isEdited && ( + <> + + {' '} + + + {translate('reportActionCompose.edited')} + + + )} )} diff --git a/src/pages/home/report/comment/TextWithEmojiFragment.tsx b/src/pages/home/report/comment/TextWithEmojiFragment.tsx new file mode 100644 index 000000000000..c0ba268cac72 --- /dev/null +++ b/src/pages/home/report/comment/TextWithEmojiFragment.tsx @@ -0,0 +1,71 @@ +import React from 'react'; +import type {StyleProp, TextStyle} from 'react-native'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; +import * as DeviceCapabilities from '@libs/DeviceCapabilities'; +import {splitTextWithEmojis} from '@libs/EmojiUtils'; +import variables from '@styles/variables'; +import CONST from '@src/CONST'; + +type ComponentProps = { + text: string; + passedStyles: StyleProp; + styleAsDeleted?: boolean; + styleAsMuted?: boolean; + isSmallScreenWidth?: boolean; + isEdited?: boolean; + emojisOnly?: boolean; +}; +function TextWithEmojiFragment({text, passedStyles, styleAsDeleted, styleAsMuted, isSmallScreenWidth, isEdited, emojisOnly}: ComponentProps) { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + const theme = useTheme(); + const processedTextArray = splitTextWithEmojis(text); + + return ( + + {processedTextArray.map((word: string) => + CONST.REGEX.EMOJIS.test(word) ? ( + {word} + ) : ( + + {word} + + ), + )} + + {isEdited && ( + <> + + {' '} + + + {translate('reportActionCompose.edited')} + + + )} + + ); +} + +TextWithEmojiFragment.displayName = 'TextWithEmojiFragment'; + +export default TextWithEmojiFragment; diff --git a/src/styles/index.ts b/src/styles/index.ts index 45e11dbe6cb9..6b9894b856a6 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -1662,6 +1662,14 @@ const styles = (theme: ThemeColors) => lineHeight: variables.fontSizeOnlyEmojisHeight, }, + emojisWithinText: { + fontSize: variables.fontSizeEmojisWithinText, + }, + + enhancedLineHeight: { + lineHeight: variables.lineHeightComment, + }, + createMenuPositionSidebar: (windowHeight: number) => ({ horizontal: 18, @@ -1982,7 +1990,7 @@ const styles = (theme: ThemeColors) => fontFamily: FontUtils.fontFamily.platform.EXP_NEUE_BOLD, fontSize: variables.fontSizeNormal, fontWeight: FontUtils.fontWeight.bold, - lineHeight: variables.lineHeightXLarge, + lineHeight: variables.lineHeightXXLarge, ...wordBreak.breakWord, }, diff --git a/src/styles/variables.ts b/src/styles/variables.ts index 7ab469af9533..07fac2140633 100644 --- a/src/styles/variables.ts +++ b/src/styles/variables.ts @@ -50,6 +50,7 @@ export default { breadcrumbsFontSize: getValueUsingPixelRatio(19, 32), fontSizeOnlyEmojis: 30, fontSizeOnlyEmojisHeight: 35, + fontSizeEmojisWithinText: 19, fontSizeSmall: getValueUsingPixelRatio(11, 17), fontSizeExtraSmall: 9, fontSizeLabel: getValueUsingPixelRatio(13, 19), @@ -112,6 +113,7 @@ export default { lineHeightSizeh1: getValueUsingPixelRatio(28, 32), lineHeightSizeh2: getValueUsingPixelRatio(24, 28), lineHeightSignInHeroXSmall: getValueUsingPixelRatio(32, 37), + lineHeightComment: 23, inputHeight: getValueUsingPixelRatio(52, 72), inputHeightSmall: 28, formErrorLineHeight: getValueUsingPixelRatio(18, 23), From 7dcb7100a9f2c6660a04e6bcd4908c807f8b5dd7 Mon Sep 17 00:00:00 2001 From: kbieganowski Date: Tue, 23 Apr 2024 15:01:19 +0200 Subject: [PATCH 02/28] wip: updated emojis size in settings input and LHP username --- .../TextInput/BaseTextInput/index.tsx | 1 + src/pages/settings/InitialSettingsPage.tsx | 27 ++++++++++++++----- .../settings/Profile/DisplayNamePage.tsx | 1 + src/styles/index.ts | 4 +++ src/styles/variables.ts | 1 + 5 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/components/TextInput/BaseTextInput/index.tsx b/src/components/TextInput/BaseTextInput/index.tsx index c73509aa7b8f..6ee88aaa6057 100644 --- a/src/components/TextInput/BaseTextInput/index.tsx +++ b/src/components/TextInput/BaseTextInput/index.tsx @@ -381,6 +381,7 @@ function BaseTextInput( // Add disabled color theme when field is not editable. inputProps.disabled && styles.textInputDisabled, styles.pointerEventsAuto, + isMarkdownEnabled ? {lineHeight: 18} : null, ]} multiline={isMultiline} maxLength={maxLength} diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index 2dfeb1841a74..f75650c6a886 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -29,6 +29,7 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWaitForNavigation from '@hooks/useWaitForNavigation'; import * as CurrencyUtils from '@libs/CurrencyUtils'; +import {splitTextWithEmojis} from '@libs/EmojiUtils'; import Navigation from '@libs/Navigation/Navigation'; import shouldShowSubscriptionsMenu from '@libs/shouldShowSubscriptionsMenu'; import * as UserUtils from '@libs/UserUtils'; @@ -360,6 +361,11 @@ function InitialSettingsPage({session, userWallet, bankAccountList, fundList, wa const currentUserDetails = currentUserPersonalDetails; const avatarURL = currentUserDetails?.avatar ?? ''; const accountID = currentUserDetails?.accountID ?? ''; + const usernameContainEmojis = CONST.REGEX.EMOJIS.test(currentUserPersonalDetails?.displayName ?? ''); + let processedTextArray: string[] = []; + if (usernameContainEmojis) { + processedTextArray = splitTextWithEmojis(currentUserPersonalDetails?.displayName ?? ''); + } const headerContent = ( @@ -430,12 +436,21 @@ function InitialSettingsPage({session, userWallet, bankAccountList, fundList, wa editIconStyle={styles.smallEditIconAccount} /> - - {currentUserPersonalDetails.displayName ? currentUserPersonalDetails.displayName : formatPhoneNumber(session?.email ?? '')} - + {currentUserPersonalDetails?.displayName && usernameContainEmojis ? ( + + {processedTextArray.map((word: string) => (CONST.REGEX.EMOJIS.test(word) ? {word} : word))} + + ) : ( + + {currentUserPersonalDetails.displayName ? currentUserPersonalDetails.displayName : formatPhoneNumber(session?.email ?? '')} + + )} {Boolean(currentUserPersonalDetails.displayName) && ( diff --git a/src/styles/index.ts b/src/styles/index.ts index 6b9894b856a6..94036299238c 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -1670,6 +1670,10 @@ const styles = (theme: ThemeColors) => lineHeight: variables.lineHeightComment, }, + initialSettingsUsernameEmoji: { + fontSize: variables.fontSizeUsernameEmoji, + }, + createMenuPositionSidebar: (windowHeight: number) => ({ horizontal: 18, diff --git a/src/styles/variables.ts b/src/styles/variables.ts index 07fac2140633..87581057a5c1 100644 --- a/src/styles/variables.ts +++ b/src/styles/variables.ts @@ -71,6 +71,7 @@ export default { fontSizeSignInHeroXSmall: 26, fontSizeSignInHeroSmall: 28, fontSizeSignInHeroBody: 20, + fontSizeUsernameEmoji: 25, lineHeightHero: 45, iconSizeXXXSmall: 4, iconSizeXXSmall: 8, From 1ded82a51cd561f4e4e12563e21999f46dc00c35 Mon Sep 17 00:00:00 2001 From: kbieganowski Date: Wed, 24 Apr 2024 12:47:35 +0200 Subject: [PATCH 03/28] wip: emojis font size - CR changes --- src/libs/EmojiUtils.ts | 65 ++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/src/libs/EmojiUtils.ts b/src/libs/EmojiUtils.ts index 6d67da5974b3..4b1e6c58f74f 100644 --- a/src/libs/EmojiUtils.ts +++ b/src/libs/EmojiUtils.ts @@ -577,39 +577,56 @@ function getSpacersIndexes(allEmojis: EmojiPickerList): number[] { */ // Surrogate pairs (combined emojis) -const highSurrogateStart = 0xd800; -const highSurrogateEnd = 0xdbff; -const lowSurrogateStart = 0xdc00; -const zeroWidthJoiner = 0x200d; +const HIGH_SURROGATE_START = 0xd800; +const HIGH_SURROGATE_END = 0xdbff; +const LOW_SURROGATE_START = 0xdc00; +const ZERO_WIDTH_JOINER = 0x200d; // Regional indicator symbols (flags) -const regionalIndicatorStart = 0x1f1e6; // 1st letter of a two-letter country code -const regionalIndicatorEnd = 0x1f1ff; // Last letter of a two-letter country code +const REGIONAL_INDICATOR_START = 0x1f1e6; // 1st letter of a two-letter country code +const REGIONAL_INDICATOR_END = 0x1f1ff; // Last letter of a two-letter country code // Fitzpatrick scale modifiers (skin tone) -const fitzpatrickScaleStart = 0x1f3fb; // Type 1 (represents light skin tone) -const fitzpatrickScaleEnd = 0x1f3ff; // Type 6 (represents dark skin tone) +const FITZPATRICK_SCALE_START = 0x1f3fb; // Type 1 (represents light skin tone) +const FITZPATRICK_SCALE_END = 0x1f3ff; // Type 6 (represents dark skin tone) // Variation selectors (specific variations in the presentation of other characters) -const variationModifierStart = 0xfe00; // Request text presentation of emoji -const variationModifierEnd = 0xfe0f; // Indicate that the character should be displayed as an emoji +const VARIATION_MODIFIER_START = 0xfe00; // Request text presentation of emoji +const VARIATION_MODIFIER_END = 0xfe0f; // Indicate that the character should be displayed as an emoji -const codePointFromSurrogatePair = (pair: string) => { - const highOffset = pair.charCodeAt(0) - highSurrogateStart; - const lowOffset = pair.charCodeAt(1) - lowSurrogateStart; +function codePointFromSurrogatePair(pair: string) { + const highOffset = pair.charCodeAt(0) - HIGH_SURROGATE_START; + const lowOffset = pair.charCodeAt(1) - LOW_SURROGATE_START; // eslint-disable-next-line no-bitwise return (highOffset << 10) + lowOffset + 0x10000; -}; +} -const isZeroWidthJoiner = (text: string) => text?.charCodeAt(0) === zeroWidthJoiner; -const isWithinInclusiveRange = (value: number, lower: number, upper: number) => value >= lower && value <= upper; -const isFirstOfSurrogatePair = (text: string) => isWithinInclusiveRange(text?.[0].charCodeAt(0), highSurrogateStart, highSurrogateEnd); -const isRegionalIndicator = (text: string) => isWithinInclusiveRange(codePointFromSurrogatePair(text), regionalIndicatorStart, regionalIndicatorEnd); -const isFitzpatrickModifier = (text: string) => isWithinInclusiveRange(codePointFromSurrogatePair(text), fitzpatrickScaleStart, fitzpatrickScaleEnd); -const isVariationSelector = (text: string) => isWithinInclusiveRange(text?.charCodeAt(0), variationModifierStart, variationModifierEnd); +function isZeroWidthJoiner(text: string) { + return text?.charCodeAt(0) === ZERO_WIDTH_JOINER; +} + +function isWithinInclusiveRange(value: number, lower: number, upper: number) { + return value >= lower && value <= upper; +} + +function isFirstOfSurrogatePair(text: string) { + return isWithinInclusiveRange(text?.[0].charCodeAt(0), HIGH_SURROGATE_START, HIGH_SURROGATE_END); +} + +function isRegionalIndicator(text: string) { + return isWithinInclusiveRange(codePointFromSurrogatePair(text), REGIONAL_INDICATOR_START, REGIONAL_INDICATOR_END); +} + +function isFitzpatrickModifier(text: string) { + return isWithinInclusiveRange(codePointFromSurrogatePair(text), FITZPATRICK_SCALE_START, FITZPATRICK_SCALE_END); +} + +function isVariationSelector(text: string) { + return isWithinInclusiveRange(text?.charCodeAt(0), VARIATION_MODIFIER_START, VARIATION_MODIFIER_END); +} // Define how many code units make up the character -const nextUnits = (i: number, text: string) => { +function nextUnits(i: number, text: string) { const current = text[i]; // If a value at index is not part of a surrogate pair, or it is at the end take value at i if (!isFirstOfSurrogatePair(current) || i === text.length - 1) { @@ -627,9 +644,9 @@ const nextUnits = (i: number, text: string) => { return 4; // Skin tones } return 2; // Variations and non-BMP characters -}; +} -const splitTextWithEmojis = (text: string): string[] => { +function splitTextWithEmojis(text: string): string[] { if (!text) { return []; } @@ -681,7 +698,7 @@ const splitTextWithEmojis = (text: string): string[] => { } // remove empty characters from array return processedArray.filter((item) => item); -}; +} export type {HeaderIndice, EmojiPickerList, EmojiSpacer, EmojiPickerListItem}; From ee38c51fd70511cf27872137a1de296ac017da45 Mon Sep 17 00:00:00 2001 From: kbieganowski Date: Thu, 16 May 2024 14:02:23 +0200 Subject: [PATCH 04/28] rebase wip --- src/CONST.ts | 4 +- src/libs/EmojiUtils.ts | 168 +++++------------- .../home/report/ReportActionItemFragment.tsx | 5 +- .../report/comment/TextCommentFragment.tsx | 5 +- .../report/comment/TextWithEmojiFragment.tsx | 14 +- src/pages/settings/InitialSettingsPage.tsx | 5 +- 6 files changed, 62 insertions(+), 139 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index a983a18e3d6a..78dba83c3110 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1910,8 +1910,8 @@ const CONST = { // eslint-disable-next-line max-len, no-misleading-character-class EMOJI: /[\p{Extended_Pictographic}\u200d\u{1f1e6}-\u{1f1ff}\u{1f3fb}-\u{1f3ff}\u{e0020}-\u{e007f}\u20E3\uFE0F]|[#*0-9]\uFE0F?\u20E3/gu, - // eslint-disable-next-line max-len, no-misleading-character-class - EMOJIS: /[\p{Extended_Pictographic}](\u200D[\p{Extended_Pictographic}]|[\u{1F3FB}-\u{1F3FF}]|[\u{E0020}-\u{E007F}]|\uFE0F|\u20E3)*|[\u{1F1E6}-\u{1F1FF}]{2}|[#*0-9]\uFE0F?\u20E3/gu, + // eslint-disable-next-line max-len, no-misleading-character-class, no-empty-character-class + EMOJIS: /[\p{Extended_Pictographic}](\u200D[\p{Extended_Pictographic}]|[\u{1F3FB}-\u{1F3FF}]|[\u{E0020}-\u{E007F}]|\uFE0F|\u20E3)*|[\u{1F1E6}-\u{1F1FF}]{2}|[#*0-9]\uFE0F?\u20E3/du, // eslint-disable-next-line max-len, no-misleading-character-class EMOJI_SKIN_TONES: /[\u{1f3fb}-\u{1f3ff}]/gu, diff --git a/src/libs/EmojiUtils.ts b/src/libs/EmojiUtils.ts index 4b1e6c58f74f..80b1f5c9c0fc 100644 --- a/src/libs/EmojiUtils.ts +++ b/src/libs/EmojiUtils.ts @@ -74,7 +74,7 @@ const getEmojiUnicode = memoize((input: string) => { .charCodeAt(0) .toString() .split(' ') - .map((val) => parseInt(val, 10).toString(16)) + .map((val: string) => parseInt(val, 10).toString(16)) .join(' '); } @@ -127,7 +127,8 @@ function isFirstLetterEmoji(message: string): boolean { */ function containsOnlyEmojis(message: string): boolean { const trimmedMessage = Str.replaceAll(message.replace(/ /g, ''), '\n', ''); - const match = trimmedMessage.match(CONST.REGEX.EMOJIS); + const emojisRegex = new RegExp(CONST.REGEX.EMOJIS, CONST.REGEX.EMOJIS.flags.concat('g')); + const match = trimmedMessage.match(emojisRegex); if (!match) { return false; @@ -569,138 +570,57 @@ function getSpacersIndexes(allEmojis: EmojiPickerList): number[] { return spacersIndexes; } -/** - * Split string into plain text and emojis array with attention to: - * Surrogate pairs (combined emojis including flags) - * Modifiers (different skin tones and emoji variations) - * @param text - */ - -// Surrogate pairs (combined emojis) -const HIGH_SURROGATE_START = 0xd800; -const HIGH_SURROGATE_END = 0xdbff; -const LOW_SURROGATE_START = 0xdc00; -const ZERO_WIDTH_JOINER = 0x200d; - -// Regional indicator symbols (flags) -const REGIONAL_INDICATOR_START = 0x1f1e6; // 1st letter of a two-letter country code -const REGIONAL_INDICATOR_END = 0x1f1ff; // Last letter of a two-letter country code - -// Fitzpatrick scale modifiers (skin tone) -const FITZPATRICK_SCALE_START = 0x1f3fb; // Type 1 (represents light skin tone) -const FITZPATRICK_SCALE_END = 0x1f3ff; // Type 6 (represents dark skin tone) - -// Variation selectors (specific variations in the presentation of other characters) -const VARIATION_MODIFIER_START = 0xfe00; // Request text presentation of emoji -const VARIATION_MODIFIER_END = 0xfe0f; // Indicate that the character should be displayed as an emoji - -function codePointFromSurrogatePair(pair: string) { - const highOffset = pair.charCodeAt(0) - HIGH_SURROGATE_START; - const lowOffset = pair.charCodeAt(1) - LOW_SURROGATE_START; - // eslint-disable-next-line no-bitwise - return (highOffset << 10) + lowOffset + 0x10000; -} - -function isZeroWidthJoiner(text: string) { - return text?.charCodeAt(0) === ZERO_WIDTH_JOINER; -} - -function isWithinInclusiveRange(value: number, lower: number, upper: number) { - return value >= lower && value <= upper; -} - -function isFirstOfSurrogatePair(text: string) { - return isWithinInclusiveRange(text?.[0].charCodeAt(0), HIGH_SURROGATE_START, HIGH_SURROGATE_END); -} - -function isRegionalIndicator(text: string) { - return isWithinInclusiveRange(codePointFromSurrogatePair(text), REGIONAL_INDICATOR_START, REGIONAL_INDICATOR_END); -} - -function isFitzpatrickModifier(text: string) { - return isWithinInclusiveRange(codePointFromSurrogatePair(text), FITZPATRICK_SCALE_START, FITZPATRICK_SCALE_END); -} - -function isVariationSelector(text: string) { - return isWithinInclusiveRange(text?.charCodeAt(0), VARIATION_MODIFIER_START, VARIATION_MODIFIER_END); -} - -// Define how many code units make up the character -function nextUnits(i: number, text: string) { - const current = text[i]; - // If a value at index is not part of a surrogate pair, or it is at the end take value at i - if (!isFirstOfSurrogatePair(current) || i === text.length - 1) { - return 1; - } - - const currentPair = current + text[i + 1]; - const nextPair = text.substring(i + 2, i + 5); - - if (isRegionalIndicator(currentPair) && isRegionalIndicator(nextPair)) { - return 4; // Flags (combination of 2 regional indicators) - } - - if (isFitzpatrickModifier(nextPair)) { - return 4; // Skin tones - } - return 2; // Variations and non-BMP characters -} +type TextWithEmoji = { + text: string; + isEmoji: boolean; +}; -function splitTextWithEmojis(text: string): string[] { +function splitTextWithEmojis(text = ''): TextWithEmoji[] { if (!text) { return []; } - let tmpString = ''; - let i = 0; - let increment = 0; - const tmpResult: string[] = []; - const processedArray: string[] = []; - while (i < text.length) { - increment += nextUnits(i + increment, text); - if (isVariationSelector(text[i + increment])) { - increment++; - } - if (isZeroWidthJoiner(text[i + increment])) { - increment++; - // eslint-disable-next-line no-continue -- without continue we would separate surrogate pair - continue; - } - tmpResult.push(text.substring(i, i + increment)); - i += increment; - increment = 0; - } - - for (let j = 0; j <= tmpResult.length; j++) { - if (!tmpResult[j]?.codePointAt(0)) { - // eslint-disable-next-line no-continue -- prevent error for empty chars - continue; - } - if (tmpResult[j] === ' ') { - tmpString += tmpResult[j]; - processedArray.push(tmpString); - tmpString = ''; - // eslint-disable-next-line no-continue -- skip rest of the checks in current iteration - continue; - } - // @ts-expect-error -- comments contain only BMP characters and emojis so codePointAt will return number - if (tmpResult[j].codePointAt(0) <= 0xffff) { - // is BMP character - tmpString += tmpResult[j]; - if (j === tmpResult.length - 1) { - processedArray.push(tmpString); + // The regex needs to be cloned because `exec()` is a stateful operation and maintains the state inside + // the regex variable itself, so we must have a independent instance for each function's call. + const emojisRegex = new RegExp(CONST.REGEX.EMOJIS, CONST.REGEX.EMOJIS.flags.concat('g')); + + const splitText: TextWithEmoji[] = []; + let regexResult: RegExpExecArray | null; + let lastMatchIndexEnd = 0; + do { + regexResult = emojisRegex.exec(text); + + if (regexResult?.indices) { + const matchIndexStart = regexResult.indices[0][0]; + const matchIndexEnd = regexResult.indices[0][1]; + + if (matchIndexStart > lastMatchIndexEnd) { + splitText.push({ + text: text.slice(lastMatchIndexEnd, matchIndexStart), + isEmoji: false, + }); } - } else { - processedArray.push(tmpString); - processedArray.push(tmpResult[j]); - tmpString = ''; + + splitText.push({ + text: text.slice(matchIndexStart, matchIndexEnd), + isEmoji: true, + }); + + lastMatchIndexEnd = matchIndexEnd; } + } while (regexResult !== null); + + if (lastMatchIndexEnd < text.length) { + splitText.push({ + text: text.slice(lastMatchIndexEnd, text.length), + isEmoji: false, + }); } - // remove empty characters from array - return processedArray.filter((item) => item); + + return splitText; } -export type {HeaderIndice, EmojiPickerList, EmojiSpacer, EmojiPickerListItem}; +export type {HeaderIndice, EmojiPickerList, EmojiSpacer, EmojiPickerListItem, TextWithEmoji}; export { findEmojiByName, diff --git a/src/pages/home/report/ReportActionItemFragment.tsx b/src/pages/home/report/ReportActionItemFragment.tsx index fb763ac889e8..0d0ecec63caa 100644 --- a/src/pages/home/report/ReportActionItemFragment.tsx +++ b/src/pages/home/report/ReportActionItemFragment.tsx @@ -7,6 +7,7 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; import convertToLTR from '@libs/convertToLTR'; +import type {TextWithEmoji} from '@libs/EmojiUtils'; import {splitTextWithEmojis} from '@libs/EmojiUtils'; import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; @@ -158,7 +159,7 @@ function ReportActionItemFragment({ } const containEmoji = CONST.REGEX.EMOJIS.test(fragment.text); - let processedTextArray: string[] = []; + let processedTextArray: TextWithEmoji[] = []; if (containEmoji) { processedTextArray = splitTextWithEmojis(fragment.text); } @@ -171,7 +172,7 @@ function ReportActionItemFragment({ > {containEmoji ? ( - {processedTextArray.map((word: string) => (CONST.REGEX.EMOJIS.test(word) ? {word} : word))} + {processedTextArray.map(({text, isEmoji}) => (isEmoji ? {text} : text))} ) : ( @@ -80,9 +81,9 @@ function TextCommentFragment({fragment, styleAsDeleted, styleAsMuted = false, so text={text} displayAsGroup={displayAsGroup} /> - {CONST.REGEX.EMOJIS.test(message ?? '') && !textContainsOnlyEmojis ? ( + {emojisRegex.test(message ?? '') && !textContainsOnlyEmojis ? ( ; styleAsDeleted?: boolean; styleAsMuted?: boolean; @@ -18,17 +18,17 @@ type ComponentProps = { isEdited?: boolean; emojisOnly?: boolean; }; -function TextWithEmojiFragment({text, passedStyles, styleAsDeleted, styleAsMuted, isSmallScreenWidth, isEdited, emojisOnly}: ComponentProps) { +function TextWithEmojiFragment({message, passedStyles, styleAsDeleted, styleAsMuted, isSmallScreenWidth, isEdited, emojisOnly}: ComponentProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const theme = useTheme(); - const processedTextArray = splitTextWithEmojis(text); + const processedTextArray = splitTextWithEmojis(message); return ( - {processedTextArray.map((word: string) => - CONST.REGEX.EMOJIS.test(word) ? ( - {word} + {processedTextArray.map(({text, isEmoji}) => + isEmoji ? ( + {text} ) : ( - {word} + {text} ), )} diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index f75650c6a886..4d95e36c586b 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -30,6 +30,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWaitForNavigation from '@hooks/useWaitForNavigation'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import {splitTextWithEmojis} from '@libs/EmojiUtils'; +import type {TextWithEmoji} from '@libs/EmojiUtils'; import Navigation from '@libs/Navigation/Navigation'; import shouldShowSubscriptionsMenu from '@libs/shouldShowSubscriptionsMenu'; import * as UserUtils from '@libs/UserUtils'; @@ -362,7 +363,7 @@ function InitialSettingsPage({session, userWallet, bankAccountList, fundList, wa const avatarURL = currentUserDetails?.avatar ?? ''; const accountID = currentUserDetails?.accountID ?? ''; const usernameContainEmojis = CONST.REGEX.EMOJIS.test(currentUserPersonalDetails?.displayName ?? ''); - let processedTextArray: string[] = []; + let processedTextArray: TextWithEmoji[] = []; if (usernameContainEmojis) { processedTextArray = splitTextWithEmojis(currentUserPersonalDetails?.displayName ?? ''); } @@ -441,7 +442,7 @@ function InitialSettingsPage({session, userWallet, bankAccountList, fundList, wa style={[styles.textHeadline, styles.pre, styles.textAlignCenter]} numberOfLines={1} > - {processedTextArray.map((word: string) => (CONST.REGEX.EMOJIS.test(word) ? {word} : word))} + {processedTextArray.map(({text, isEmoji}) => (isEmoji ? {text} : text))} ) : ( Date: Thu, 16 May 2024 14:05:49 +0200 Subject: [PATCH 05/28] rebase wip --- src/components/Composer/index.native.tsx | 3 ++- src/components/InlineCodeBlock/WrappedText.tsx | 3 ++- src/components/TextInput/BaseTextInput/index.tsx | 2 +- src/hooks/useMarkdownStyle.ts | 6 +++--- src/libs/EmojiUtils.ts | 6 ++++-- src/libs/ValidationUtils.ts | 7 +++++-- .../home/report/comment/TextWithEmojiFragment.tsx | 14 +++++++++++--- src/styles/variables.ts | 3 +++ 8 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/components/Composer/index.native.tsx b/src/components/Composer/index.native.tsx index 22255e5ce92c..b1a60e7ec201 100644 --- a/src/components/Composer/index.native.tsx +++ b/src/components/Composer/index.native.tsx @@ -11,6 +11,7 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import updateIsFullComposerAvailable from '@libs/ComposerUtils/updateIsFullComposerAvailable'; import {containsOnlyEmojis} from '@libs/EmojiUtils'; +import variables from '@styles/variables'; import type {ComposerProps} from './types'; function Composer( @@ -68,7 +69,7 @@ function Composer( }, [shouldClear, onClear]); const maxHeightStyle = useMemo(() => StyleUtils.getComposerMaxHeightStyle(maxLines, isComposerFullSize), [StyleUtils, isComposerFullSize, maxLines]); - const composerStyle = useMemo(() => StyleSheet.flatten([style, textContainsOnlyEmojis ? {lineHeight: 32} : null]), [style, textContainsOnlyEmojis]); + const composerStyle = useMemo(() => StyleSheet.flatten([style, textContainsOnlyEmojis ? {lineHeight: variables.lineHeightEmojisOnlyComposer} : null]), [style, textContainsOnlyEmojis]); return ( = []): MarkdownStyle { +function useMarkdownStyle(inputContainsOnlyEmojis?: boolean, excludeStyles: Array = []): MarkdownStyle { const theme = useTheme(); // this map is used to reset the styles that are not needed - passing undefined value can break the native side @@ -32,7 +32,7 @@ function useMarkdownStyle(containsEmojisOnly?: boolean, excludeStyles: Array; + /** Additional styles to add after local styles. */ + passedStyles?: StyleProp; + /** Should this message fragment be styled as deleted? */ styleAsDeleted?: boolean; + /** Should this message fragment be styled as muted? */ styleAsMuted?: boolean; + /** Is message displayed on narrow screen? */ isSmallScreenWidth?: boolean; + /** Should "(edited)" suffix be rendered? */ isEdited?: boolean; + /** Does message contain only emojis? */ emojisOnly?: boolean; }; -function TextWithEmojiFragment({message, passedStyles, styleAsDeleted, styleAsMuted, isSmallScreenWidth, isEdited, emojisOnly}: ComponentProps) { + +function TextWithEmojiFragment({message, passedStyles, styleAsDeleted, styleAsMuted, isSmallScreenWidth, isEdited, emojisOnly}: TextWithEmojiFragmentProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const theme = useTheme(); diff --git a/src/styles/variables.ts b/src/styles/variables.ts index 87581057a5c1..eb5747e8ad50 100644 --- a/src/styles/variables.ts +++ b/src/styles/variables.ts @@ -51,6 +51,7 @@ export default { fontSizeOnlyEmojis: 30, fontSizeOnlyEmojisHeight: 35, fontSizeEmojisWithinText: 19, + fontSizeEmojisOnlyComposer: 27, fontSizeSmall: getValueUsingPixelRatio(11, 17), fontSizeExtraSmall: 9, fontSizeLabel: getValueUsingPixelRatio(13, 19), @@ -115,6 +116,8 @@ export default { lineHeightSizeh2: getValueUsingPixelRatio(24, 28), lineHeightSignInHeroXSmall: getValueUsingPixelRatio(32, 37), lineHeightComment: 23, + lineHeightMarkdownEnabledInput: 18, + lineHeightEmojisOnlyComposer: 32, inputHeight: getValueUsingPixelRatio(52, 72), inputHeightSmall: 28, formErrorLineHeight: getValueUsingPixelRatio(18, 23), From d10aeb2b3128087d0ec2ecbae3e657ae30ffe176 Mon Sep 17 00:00:00 2001 From: kbieganowski Date: Thu, 9 May 2024 12:17:31 +0200 Subject: [PATCH 06/28] CR fixes --- src/pages/home/report/comment/TextWithEmojiFragment.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/pages/home/report/comment/TextWithEmojiFragment.tsx b/src/pages/home/report/comment/TextWithEmojiFragment.tsx index 8bd33ca17934..dfe7e904c78d 100644 --- a/src/pages/home/report/comment/TextWithEmojiFragment.tsx +++ b/src/pages/home/report/comment/TextWithEmojiFragment.tsx @@ -12,16 +12,22 @@ import CONST from '@src/CONST'; type TextWithEmojiFragmentProps = { /** The message to be displayed */ message: string; + /** Additional styles to add after local styles. */ passedStyles?: StyleProp; + /** Should this message fragment be styled as deleted? */ styleAsDeleted?: boolean; + /** Should this message fragment be styled as muted? */ styleAsMuted?: boolean; + /** Is message displayed on narrow screen? */ isSmallScreenWidth?: boolean; + /** Should "(edited)" suffix be rendered? */ isEdited?: boolean; + /** Does message contain only emojis? */ emojisOnly?: boolean; }; From 2278d7e5389a08290d83a581920d88f594ac90e1 Mon Sep 17 00:00:00 2001 From: kbieganowski Date: Mon, 20 May 2024 16:00:41 +0200 Subject: [PATCH 07/28] wip: CR fixes --- src/components/Composer/index.native.tsx | 4 ++-- src/components/Composer/index.tsx | 4 ++-- src/pages/home/report/ReportActionItemFragment.tsx | 7 ++++--- src/pages/home/report/comment/TextCommentFragment.tsx | 4 ++-- src/pages/home/report/comment/TextWithEmojiFragment.tsx | 4 ++-- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/components/Composer/index.native.tsx b/src/components/Composer/index.native.tsx index b1a60e7ec201..b246c46b1291 100644 --- a/src/components/Composer/index.native.tsx +++ b/src/components/Composer/index.native.tsx @@ -10,7 +10,7 @@ import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import updateIsFullComposerAvailable from '@libs/ComposerUtils/updateIsFullComposerAvailable'; -import {containsOnlyEmojis} from '@libs/EmojiUtils'; +import * as EmojiUtils from '@libs/EmojiUtils'; import variables from '@styles/variables'; import type {ComposerProps} from './types'; @@ -36,7 +36,7 @@ function Composer( ) { const textInput = useRef(null); const {isFocused, shouldResetFocus} = useResetComposerFocus(textInput); - const textContainsOnlyEmojis = containsOnlyEmojis(value ?? ''); + const textContainsOnlyEmojis = EmojiUtils.containsOnlyEmojis(value ?? ''); const theme = useTheme(); const markdownStyle = useMarkdownStyle(textContainsOnlyEmojis, !isGroupPolicyReport ? ['mentionReport'] : []); const styles = useThemeStyles(); diff --git a/src/components/Composer/index.tsx b/src/components/Composer/index.tsx index df1f2ac4398f..61d3ff401896 100755 --- a/src/components/Composer/index.tsx +++ b/src/components/Composer/index.tsx @@ -16,7 +16,7 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import * as Browser from '@libs/Browser'; import updateIsFullComposerAvailable from '@libs/ComposerUtils/updateIsFullComposerAvailable'; -import {containsOnlyEmojis} from '@libs/EmojiUtils'; +import * as EmojiUtils from '@libs/EmojiUtils'; import * as FileUtils from '@libs/fileDownload/FileUtils'; import isEnterWhileComposition from '@libs/KeyboardShortcut/isEnterWhileComposition'; import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager'; @@ -76,7 +76,7 @@ function Composer( }: ComposerProps, ref: ForwardedRef, ) { - const textContainsOnlyEmojis = containsOnlyEmojis(value ?? ''); + const textContainsOnlyEmojis = EmojiUtils.containsOnlyEmojis(value ?? ''); const theme = useTheme(); const styles = useThemeStyles(); const markdownStyle = useMarkdownStyle(textContainsOnlyEmojis, !isGroupPolicyReport ? ['mentionReport'] : []); diff --git a/src/pages/home/report/ReportActionItemFragment.tsx b/src/pages/home/report/ReportActionItemFragment.tsx index 0d0ecec63caa..adf61be6433a 100644 --- a/src/pages/home/report/ReportActionItemFragment.tsx +++ b/src/pages/home/report/ReportActionItemFragment.tsx @@ -8,7 +8,7 @@ import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; import convertToLTR from '@libs/convertToLTR'; import type {TextWithEmoji} from '@libs/EmojiUtils'; -import {splitTextWithEmojis} from '@libs/EmojiUtils'; +import * as EmojiUtils from '@libs/EmojiUtils'; import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; @@ -158,10 +158,11 @@ function ReportActionItemFragment({ ); } - const containEmoji = CONST.REGEX.EMOJIS.test(fragment.text); + const emojisRegex = new RegExp(CONST.REGEX.EMOJIS, CONST.REGEX.EMOJIS.flags.concat('g')); + const containEmoji = emojisRegex.test(fragment.text); let processedTextArray: TextWithEmoji[] = []; if (containEmoji) { - processedTextArray = splitTextWithEmojis(fragment.text); + processedTextArray = EmojiUtils.splitTextWithEmojis(fragment.text); } return ( diff --git a/src/pages/home/report/comment/TextCommentFragment.tsx b/src/pages/home/report/comment/TextCommentFragment.tsx index 3e38cebcb724..0c8226906785 100644 --- a/src/pages/home/report/comment/TextCommentFragment.tsx +++ b/src/pages/home/report/comment/TextCommentFragment.tsx @@ -10,7 +10,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import convertToLTR from '@libs/convertToLTR'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; -import {containsOnlyEmojis} from '@libs/EmojiUtils'; +import * as EmojiUtils from '@libs/EmojiUtils'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import type {OriginalMessageSource} from '@src/types/onyx/OriginalMessage'; @@ -52,7 +52,7 @@ function TextCommentFragment({fragment, styleAsDeleted, styleAsMuted = false, so // If the only difference between fragment.text and fragment.html is
tags and emoji tag // on native, we render it as text, not as html // on other device, only render it as text if the only difference is
tag - const textContainsOnlyEmojis = containsOnlyEmojis(text ?? ''); + const textContainsOnlyEmojis = EmojiUtils.containsOnlyEmojis(text ?? ''); if (!shouldRenderAsText(html, text ?? '') && !(textContainsOnlyEmojis && styleAsDeleted)) { const editedTag = fragment?.isEdited ? `` : ''; const htmlWithDeletedTag = styleAsDeleted ? `${html}` : html; diff --git a/src/pages/home/report/comment/TextWithEmojiFragment.tsx b/src/pages/home/report/comment/TextWithEmojiFragment.tsx index dfe7e904c78d..f3286bbbf838 100644 --- a/src/pages/home/report/comment/TextWithEmojiFragment.tsx +++ b/src/pages/home/report/comment/TextWithEmojiFragment.tsx @@ -5,7 +5,7 @@ import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; -import {splitTextWithEmojis} from '@libs/EmojiUtils'; +import * as EmojiUtils from '@libs/EmojiUtils'; import variables from '@styles/variables'; import CONST from '@src/CONST'; @@ -36,7 +36,7 @@ function TextWithEmojiFragment({message, passedStyles, styleAsDeleted, styleAsMu const styles = useThemeStyles(); const {translate} = useLocalize(); const theme = useTheme(); - const processedTextArray = splitTextWithEmojis(message); + const processedTextArray = EmojiUtils.splitTextWithEmojis(message); return ( From bfea0519024eeab9c83f735dbb74b69e400f2791 Mon Sep 17 00:00:00 2001 From: kbieganowski Date: Thu, 23 May 2024 09:41:13 +0200 Subject: [PATCH 08/28] wip: CR fixes --- src/components/Composer/index.tsx | 4 ++-- .../home/report/ReportActionItemFragment.tsx | 5 ++++- src/styles/index.ts | 6 ++++++ src/styles/variables.ts | 21 ++++++++++++------- 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/components/Composer/index.tsx b/src/components/Composer/index.tsx index 61d3ff401896..98d49df2bb66 100755 --- a/src/components/Composer/index.tsx +++ b/src/components/Composer/index.tsx @@ -316,10 +316,10 @@ function Composer( scrollStyleMemo, StyleUtils.getComposerMaxHeightStyle(maxLines, isComposerFullSize), isComposerFullSize ? ({height: '100%', maxHeight: 'none' as DimensionValue} as TextStyle) : undefined, - textContainsOnlyEmojis ? {paddingBottom: 0} : null, + textContainsOnlyEmojis ? styles.emojisOnlyComposer : null, ], - [style, styles.rtlTextRenderForSafari, scrollStyleMemo, StyleUtils, maxLines, isComposerFullSize, textContainsOnlyEmojis], + [style, StyleUtils, isComposerFullSize, styles.rtlTextRenderForSafari, styles.emojisOnlyComposer, scrollStyleMemo, maxLines, textContainsOnlyEmojis], ); return ( diff --git a/src/pages/home/report/ReportActionItemFragment.tsx b/src/pages/home/report/ReportActionItemFragment.tsx index adf61be6433a..8e9b1747bc6c 100644 --- a/src/pages/home/report/ReportActionItemFragment.tsx +++ b/src/pages/home/report/ReportActionItemFragment.tsx @@ -172,7 +172,10 @@ function ReportActionItemFragment({ icon={actorIcon} > {containEmoji ? ( - + {processedTextArray.map(({text, isEmoji}) => (isEmoji ? {text} : text))} ) : ( diff --git a/src/styles/index.ts b/src/styles/index.ts index 94036299238c..bb02a17e5094 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -1656,6 +1656,7 @@ const styles = (theme: ThemeColors) => onlyEmojisText: { fontSize: variables.fontSizeOnlyEmojis, lineHeight: variables.fontSizeOnlyEmojisHeight, + marginTop: variables.emojiOnlyMarginTop, }, onlyEmojisTextLineHeight: { @@ -1666,6 +1667,11 @@ const styles = (theme: ThemeColors) => fontSize: variables.fontSizeEmojisWithinText, }, + emojisOnlyComposer: { + paddingTop: variables.emojiOnlyComposerPaddingTop, + paddingBottom: variables.emojiOnlyComposerPaddingBottom, + }, + enhancedLineHeight: { lineHeight: variables.lineHeightComment, }, diff --git a/src/styles/variables.ts b/src/styles/variables.ts index eb5747e8ad50..44b02db6d1c1 100644 --- a/src/styles/variables.ts +++ b/src/styles/variables.ts @@ -48,10 +48,6 @@ export default { defaultAvatarPreviewSize: 360, fabBottom: 25, breadcrumbsFontSize: getValueUsingPixelRatio(19, 32), - fontSizeOnlyEmojis: 30, - fontSizeOnlyEmojisHeight: 35, - fontSizeEmojisWithinText: 19, - fontSizeEmojisOnlyComposer: 27, fontSizeSmall: getValueUsingPixelRatio(11, 17), fontSizeExtraSmall: 9, fontSizeLabel: getValueUsingPixelRatio(13, 19), @@ -72,7 +68,6 @@ export default { fontSizeSignInHeroXSmall: 26, fontSizeSignInHeroSmall: 28, fontSizeSignInHeroBody: 20, - fontSizeUsernameEmoji: 25, lineHeightHero: 45, iconSizeXXXSmall: 4, iconSizeXXSmall: 8, @@ -88,8 +83,6 @@ export default { iconBottomBar: 24, sidebarAvatarSize: 28, iconHeader: 48, - emojiSize: 20, - emojiLineHeight: 28, iouAmountTextSize: 40, extraSmallMobileResponsiveWidthBreakpoint: 320, extraSmallMobileResponsiveHeightBreakpoint: 667, @@ -117,7 +110,6 @@ export default { lineHeightSignInHeroXSmall: getValueUsingPixelRatio(32, 37), lineHeightComment: 23, lineHeightMarkdownEnabledInput: 18, - lineHeightEmojisOnlyComposer: 32, inputHeight: getValueUsingPixelRatio(52, 72), inputHeightSmall: 28, formErrorLineHeight: getValueUsingPixelRatio(18, 23), @@ -211,6 +203,19 @@ export default { onboardingModalWidth: 500, welcomeVideoDelay: 1000, + // Emoji related variables + emojiSize: 20, + emojiLineHeight: 28, + fontSizeOnlyEmojis: 30, + fontSizeOnlyEmojisHeight: 35, + fontSizeEmojisWithinText: 19, + fontSizeEmojisOnlyComposer: 27, + fontSizeUsernameEmoji: 25, + lineHeightEmojisOnlyComposer: 32, + emojiOnlyMarginTop: 5, + emojiOnlyComposerPaddingBottom: 0, + emojiOnlyComposerPaddingTop: 7, + // The height of the empty list is 14px (2px for borders and 12px for vertical padding) // This is calculated based on the values specified in the 'getGoogleListViewStyle' function of the 'StyleUtils' utility googleEmptyListViewHeight: 14, From 8a31012490c3e9444e5644c3c3fd9f3346e1768a Mon Sep 17 00:00:00 2001 From: kbieganowski Date: Mon, 27 May 2024 15:02:26 +0200 Subject: [PATCH 09/28] CR fixes --- src/components/Composer/index.native.tsx | 5 ++++- src/styles/index.ts | 1 + src/styles/variables.ts | 3 ++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/Composer/index.native.tsx b/src/components/Composer/index.native.tsx index b246c46b1291..c74a0f9a5442 100644 --- a/src/components/Composer/index.native.tsx +++ b/src/components/Composer/index.native.tsx @@ -69,7 +69,10 @@ function Composer( }, [shouldClear, onClear]); const maxHeightStyle = useMemo(() => StyleUtils.getComposerMaxHeightStyle(maxLines, isComposerFullSize), [StyleUtils, isComposerFullSize, maxLines]); - const composerStyle = useMemo(() => StyleSheet.flatten([style, textContainsOnlyEmojis ? {lineHeight: variables.lineHeightEmojisOnlyComposer} : null]), [style, textContainsOnlyEmojis]); + const composerStyle = useMemo( + () => StyleSheet.flatten([style, {lineHeight: textContainsOnlyEmojis ? variables.lineHeightEmojisOnlyComposer : variables.lineHeightEmojisWithTextComposer}]), + [style, textContainsOnlyEmojis], + ); return ( emojisWithinText: { fontSize: variables.fontSizeEmojisWithinText, + lineHeight: variables.lineHeightComment, }, emojisOnlyComposer: { diff --git a/src/styles/variables.ts b/src/styles/variables.ts index 44b02db6d1c1..2f5b4a02ee26 100644 --- a/src/styles/variables.ts +++ b/src/styles/variables.ts @@ -108,7 +108,7 @@ export default { lineHeightSizeh1: getValueUsingPixelRatio(28, 32), lineHeightSizeh2: getValueUsingPixelRatio(24, 28), lineHeightSignInHeroXSmall: getValueUsingPixelRatio(32, 37), - lineHeightComment: 23, + lineHeightComment: 24, lineHeightMarkdownEnabledInput: 18, inputHeight: getValueUsingPixelRatio(52, 72), inputHeightSmall: 28, @@ -212,6 +212,7 @@ export default { fontSizeEmojisOnlyComposer: 27, fontSizeUsernameEmoji: 25, lineHeightEmojisOnlyComposer: 32, + lineHeightEmojisWithTextComposer: 32, emojiOnlyMarginTop: 5, emojiOnlyComposerPaddingBottom: 0, emojiOnlyComposerPaddingTop: 7, From 472d7460fffdcd26a69780a07521db7ca2040c39 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 28 May 2024 16:46:03 +0200 Subject: [PATCH 10/28] Minor improvement --- src/styles/variables.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/variables.ts b/src/styles/variables.ts index 2f5b4a02ee26..f6a0f07108fa 100644 --- a/src/styles/variables.ts +++ b/src/styles/variables.ts @@ -212,7 +212,7 @@ export default { fontSizeEmojisOnlyComposer: 27, fontSizeUsernameEmoji: 25, lineHeightEmojisOnlyComposer: 32, - lineHeightEmojisWithTextComposer: 32, + lineHeightEmojisWithTextComposer: 22, emojiOnlyMarginTop: 5, emojiOnlyComposerPaddingBottom: 0, emojiOnlyComposerPaddingTop: 7, From 71a1bf40516b4a1cb4b9a17d79ad304c333f04b8 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Fri, 31 May 2024 16:06:36 +0200 Subject: [PATCH 11/28] Composer display fixes --- src/components/Composer/index.native.tsx | 5 ++++- src/components/Composer/index.tsx | 3 +-- src/styles/index.ts | 6 +++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/components/Composer/index.native.tsx b/src/components/Composer/index.native.tsx index aa6dce964122..2ab39007d54f 100644 --- a/src/components/Composer/index.native.tsx +++ b/src/components/Composer/index.native.tsx @@ -67,7 +67,10 @@ function Composer( }, [shouldClear, onClear]); const maxHeightStyle = useMemo(() => StyleUtils.getComposerMaxHeightStyle(maxLines, isComposerFullSize), [StyleUtils, isComposerFullSize, maxLines]); - const composerStyle = useMemo(() => StyleSheet.flatten([style, textContainsOnlyEmojis ? styles.onlyEmojisTextLineHeight : {}]), [style, textContainsOnlyEmojis, styles]); + const composerStyle = useMemo( + () => StyleSheet.flatten([style, textContainsOnlyEmojis ? styles.onlyEmojisTextLineHeight : styles.emojisWithTextLineHeight]), + [style, textContainsOnlyEmojis, styles], + ); return ( }, onlyEmojisTextLineHeight: { - lineHeight: variables.fontSizeOnlyEmojisHeight, + lineHeight: variables.lineHeightEmojisOnlyComposer, + }, + + emojisWithTextLineHeight: { + lineHeight: variables.lineHeightEmojisWithTextComposer, }, emojisWithinText: { From f7948b70c2d8d9816fcf5f0b278f0892bc9c3f19 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Fri, 31 May 2024 16:28:22 +0200 Subject: [PATCH 12/28] Lint fix --- src/components/Composer/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Composer/index.tsx b/src/components/Composer/index.tsx index 3028f41c709e..2335e0dbdb5e 100755 --- a/src/components/Composer/index.tsx +++ b/src/components/Composer/index.tsx @@ -318,7 +318,7 @@ function Composer( textContainsOnlyEmojis ? styles.emojisOnlyComposer : null, ], - [style, styles.rtlTextRenderForSafari, scrollStyleMemo, StyleUtils, maxLines, isComposerFullSize, textContainsOnlyEmojis], + [style, styles.rtlTextRenderForSafari, styles.emojisOnlyComposer, scrollStyleMemo, StyleUtils, maxLines, isComposerFullSize, textContainsOnlyEmojis], ); return ( From 536e1d92d68faf94da361aee4783a96dc1a18095 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 11 Jun 2024 14:30:57 +0200 Subject: [PATCH 13/28] Fix composer scroll while only emojis there --- src/components/Composer/index.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/Composer/index.tsx b/src/components/Composer/index.tsx index a1ac5858c401..10e74bf9a693 100755 --- a/src/components/Composer/index.tsx +++ b/src/components/Composer/index.tsx @@ -316,10 +316,9 @@ function Composer( scrollStyleMemo, StyleUtils.getComposerMaxHeightStyle(maxLines, isComposerFullSize), isComposerFullSize ? ({height: '100%', maxHeight: 'none' as DimensionValue} as TextStyle) : undefined, - textContainsOnlyEmojis ? styles.emojisOnlyComposer : null, ], - [style, styles.rtlTextRenderForSafari, styles.emojisOnlyComposer, scrollStyleMemo, StyleUtils, maxLines, isComposerFullSize, textContainsOnlyEmojis], + [style, styles.rtlTextRenderForSafari, scrollStyleMemo, StyleUtils, maxLines, isComposerFullSize], ); return ( From 3a3ff16025efeef97d3ea3834adcac626a5502e6 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 11 Jun 2024 15:48:17 +0200 Subject: [PATCH 14/28] Fix cursor alignment --- src/hooks/useMarkdownStyle.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hooks/useMarkdownStyle.ts b/src/hooks/useMarkdownStyle.ts index 1b0438cc4b34..e5b185af5990 100644 --- a/src/hooks/useMarkdownStyle.ts +++ b/src/hooks/useMarkdownStyle.ts @@ -33,6 +33,7 @@ function useMarkdownStyle(inputContainsOnlyEmojis?: boolean, excludeStyles: Arra }, emoji: { fontSize: inputContainsOnlyEmojis ? variables.fontSizeEmojisOnlyComposer : variables.fontSizeEmojisWithinText, + verticalAlign: 'middle', }, blockquote: { borderColor: theme.border, From 74eeede9e28c1fae45ea0a10d5a28f42d0d1fb90 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Tue, 11 Jun 2024 17:34:16 +0200 Subject: [PATCH 15/28] Fix gap between actor name and emoji --- src/styles/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/styles/index.ts b/src/styles/index.ts index 5fd09045a6e3..d313d629ccd9 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -1665,7 +1665,6 @@ const styles = (theme: ThemeColors) => onlyEmojisText: { fontSize: variables.fontSizeOnlyEmojis, lineHeight: variables.fontSizeOnlyEmojisHeight, - marginTop: variables.emojiOnlyMarginTop, }, onlyEmojisTextLineHeight: { From 83dd616180e3e2791acb8d85b23babdba504550f Mon Sep 17 00:00:00 2001 From: VickyStash Date: Thu, 13 Jun 2024 11:26:28 +0200 Subject: [PATCH 16/28] Fix date alignment --- src/pages/home/report/ReportActionItemFragment.tsx | 2 +- src/styles/index.ts | 5 +++++ src/styles/variables.ts | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionItemFragment.tsx b/src/pages/home/report/ReportActionItemFragment.tsx index 3800b6f9a5d2..933e10f42bd5 100644 --- a/src/pages/home/report/ReportActionItemFragment.tsx +++ b/src/pages/home/report/ReportActionItemFragment.tsx @@ -176,7 +176,7 @@ function ReportActionItemFragment({ numberOfLines={isSingleLine ? 1 : undefined} style={[styles.chatItemMessageHeaderSender, isSingleLine ? styles.pre : styles.preWrap]} > - {processedTextArray.map(({text, isEmoji}) => (isEmoji ? {text} : text))} + {processedTextArray.map(({text, isEmoji}) => (isEmoji ? {text} : text))} ) : ( lineHeight: variables.lineHeightComment, }, + emojisWithinDisplayName: { + fontSize: variables.fontSizeEmojisWithinText, + lineHeight: variables.lineHeightDisplayName, + }, + emojisOnlyComposer: { paddingTop: variables.emojiOnlyComposerPaddingTop, paddingBottom: variables.emojiOnlyComposerPaddingBottom, diff --git a/src/styles/variables.ts b/src/styles/variables.ts index 7214750fc2cb..e60bea6ec891 100644 --- a/src/styles/variables.ts +++ b/src/styles/variables.ts @@ -111,6 +111,7 @@ export default { lineHeightSizeh2: getValueUsingPixelRatio(24, 28), lineHeightSignInHeroXSmall: getValueUsingPixelRatio(32, 37), lineHeightComment: 24, + lineHeightDisplayName: 25, lineHeightMarkdownEnabledInput: 18, inputHeight: getValueUsingPixelRatio(52, 72), inputHeightSmall: 28, From 8fba1536c781f1b290eaa890bca74bfba3fee8a2 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Wed, 19 Jun 2024 15:22:40 +0200 Subject: [PATCH 17/28] Fix emojis are cut off --- src/components/SelectionList/UserListItem.tsx | 2 ++ .../{index.native.tsx => index.android.tsx} | 0 src/components/TextWithTooltip/index.ios.tsx | 26 +++++++++++++++++++ src/components/TextWithTooltip/types.ts | 3 +++ src/styles/index.ts | 2 +- src/styles/variables.ts | 1 + 6 files changed, 33 insertions(+), 1 deletion(-) rename src/components/TextWithTooltip/{index.native.tsx => index.android.tsx} (100%) create mode 100644 src/components/TextWithTooltip/index.ios.tsx diff --git a/src/components/SelectionList/UserListItem.tsx b/src/components/SelectionList/UserListItem.tsx index 104990cf479c..706f0a9d8f03 100644 --- a/src/components/SelectionList/UserListItem.tsx +++ b/src/components/SelectionList/UserListItem.tsx @@ -12,6 +12,7 @@ import useLocalize from '@hooks/useLocalize'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import variables from '@styles/variables'; import CONST from '@src/CONST'; import BaseListItem from './BaseListItem'; import type {ListItem, UserListItemProps} from './types'; @@ -131,6 +132,7 @@ function UserListItem({ {!!item.alternateText && ( diff --git a/src/components/TextWithTooltip/index.native.tsx b/src/components/TextWithTooltip/index.android.tsx similarity index 100% rename from src/components/TextWithTooltip/index.native.tsx rename to src/components/TextWithTooltip/index.android.tsx diff --git a/src/components/TextWithTooltip/index.ios.tsx b/src/components/TextWithTooltip/index.ios.tsx new file mode 100644 index 000000000000..f8e025a2a09e --- /dev/null +++ b/src/components/TextWithTooltip/index.ios.tsx @@ -0,0 +1,26 @@ +import React, {useMemo} from 'react'; +import Text from '@components/Text'; +import * as EmojiUtils from '@libs/EmojiUtils'; +import CONST from '@src/CONST'; +import type TextWithTooltipProps from './types'; + +function TextWithTooltip({text, style, emojiFontSize, numberOfLines = 1}: TextWithTooltipProps) { + const containsEmoji = useMemo(() => { + const emojisRegex = new RegExp(CONST.REGEX.EMOJIS, CONST.REGEX.EMOJIS.flags.concat('g')); + return !!(emojiFontSize && emojisRegex.test(text)); + }, [emojiFontSize, text]); + const processedTextArray = useMemo(() => (containsEmoji ? EmojiUtils.splitTextWithEmojis(text) : []), [containsEmoji, text]); + + return ( + + {containsEmoji ? processedTextArray.map(({text: textItem, isEmoji}) => (isEmoji ? {textItem} : textItem)) : text} + + ); +} + +TextWithTooltip.displayName = 'TextWithTooltip'; + +export default TextWithTooltip; diff --git a/src/components/TextWithTooltip/types.ts b/src/components/TextWithTooltip/types.ts index 4705e2b69a68..1df5af02b67a 100644 --- a/src/components/TextWithTooltip/types.ts +++ b/src/components/TextWithTooltip/types.ts @@ -12,6 +12,9 @@ type TextWithTooltipProps = { /** Custom number of lines for text wrapping */ numberOfLines?: number; + + /** Emoji font size */ + emojiFontSize?: number; }; export default TextWithTooltipProps; diff --git a/src/styles/index.ts b/src/styles/index.ts index 21421c399c94..abaaee583932 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -401,7 +401,7 @@ const styles = (theme: ThemeColors) => fontWeight: FontUtils.fontWeight.bold, fontFamily: FontUtils.fontFamily.platform.EXP_NEUE_BOLD, fontSize: variables.fontSizeSmall, - lineHeight: variables.lineHeightSmall, + lineHeight: variables.lineHeightNormal, }, textMicroSupporting: { diff --git a/src/styles/variables.ts b/src/styles/variables.ts index 9781c8b9825f..8384ea9fef85 100644 --- a/src/styles/variables.ts +++ b/src/styles/variables.ts @@ -212,6 +212,7 @@ export default { // Emoji related variables emojiSize: 20, + emojiSizeSmall: 12, emojiLineHeight: 28, fontSizeOnlyEmojis: 30, fontSizeOnlyEmojisHeight: 35, From e1a68281e602cdde96f335a26e0e2f06b690f8cd Mon Sep 17 00:00:00 2001 From: VickyStash Date: Wed, 10 Jul 2024 11:42:21 +0200 Subject: [PATCH 18/28] Remove unnecessary param --- src/hooks/useMarkdownStyle.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/hooks/useMarkdownStyle.ts b/src/hooks/useMarkdownStyle.ts index ee5e6a8da809..5e84f65d2f10 100644 --- a/src/hooks/useMarkdownStyle.ts +++ b/src/hooks/useMarkdownStyle.ts @@ -35,7 +35,6 @@ function useMarkdownStyle(inputContainsOnlyEmojis?: boolean, excludeStyles: Arra }, emoji: { fontSize: inputContainsOnlyEmojis ? variables.fontSizeEmojisOnlyComposer : variables.fontSizeEmojisWithinText, - verticalAlign: 'middle', }, blockquote: { borderColor: theme.border, From d28b3a7d3b2ca5fc92abd0eb33daad463a339e4e Mon Sep 17 00:00:00 2001 From: VickyStash Date: Wed, 10 Jul 2024 12:09:04 +0200 Subject: [PATCH 19/28] Minor improvements --- src/components/SelectionList/Search/UserInfoCell.tsx | 2 +- src/components/SelectionList/UserListItem.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/SelectionList/Search/UserInfoCell.tsx b/src/components/SelectionList/Search/UserInfoCell.tsx index 2a5a7da51979..d89220935dc5 100644 --- a/src/components/SelectionList/Search/UserInfoCell.tsx +++ b/src/components/SelectionList/Search/UserInfoCell.tsx @@ -32,7 +32,7 @@ function UserInfoCell({participant, displayName}: UserInfoCellProps) { /> {displayName} diff --git a/src/components/SelectionList/UserListItem.tsx b/src/components/SelectionList/UserListItem.tsx index 706f0a9d8f03..119ebd9fd535 100644 --- a/src/components/SelectionList/UserListItem.tsx +++ b/src/components/SelectionList/UserListItem.tsx @@ -132,7 +132,7 @@ function UserListItem({ {!!item.alternateText && ( From 7799a13e93bae7e5a5fae0d7863353eff943c8db Mon Sep 17 00:00:00 2001 From: VickyStash Date: Fri, 26 Jul 2024 09:58:35 +0200 Subject: [PATCH 20/28] Apply recommended updates on InitialSettingsPage --- src/pages/settings/InitialSettingsPage.tsx | 26 ++++++++++++---------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index 57e33d3d79d3..b208e63909a8 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -366,14 +366,16 @@ function InitialSettingsPage({session, userWallet, bankAccountList, fundList, wa const generalMenuItems = useMemo(() => getMenuItemsSection(generalMenuItemsData), [generalMenuItemsData, getMenuItemsSection]); const workspaceMenuItems = useMemo(() => getMenuItemsSection(workspaceMenuItemsData), [workspaceMenuItemsData, getMenuItemsSection]); - const currentUserDetails = currentUserPersonalDetails; - const avatarURL = currentUserDetails?.avatar ?? ''; - const accountID = currentUserDetails?.accountID ?? '-1'; - const usernameContainEmojis = CONST.REGEX.EMOJIS.test(currentUserPersonalDetails?.displayName ?? ''); - let processedTextArray: TextWithEmoji[] = []; - if (usernameContainEmojis) { - processedTextArray = splitTextWithEmojis(currentUserPersonalDetails?.displayName ?? ''); - } + const avatarURL = currentUserPersonalDetails?.avatar ?? ''; + const accountID = currentUserPersonalDetails?.accountID ?? '-1'; + + const processedTextArray: TextWithEmoji[] = useMemo(() => { + const doesUsernameContainEmojis = CONST.REGEX.EMOJIS.test(currentUserPersonalDetails?.displayName ?? ''); + if (!doesUsernameContainEmojis) { + return []; + } + return splitTextWithEmojis(currentUserPersonalDetails?.displayName ?? ''); + }, [currentUserPersonalDetails?.displayName]); const headerContent = ( @@ -423,7 +425,7 @@ function InitialSettingsPage({session, userWallet, bankAccountList, fundList, wa Navigation.navigate(ROUTES.PROFILE_AVATAR.getRoute(String(accountID)))} previewSource={UserUtils.getFullSizeAvatar(avatarURL, accountID)} - originalFileName={currentUserDetails.originalFileName} + originalFileName={currentUserPersonalDetails.originalFileName} headerTitle={translate('profilePage.profileAvatar')} - fallbackIcon={currentUserDetails?.fallbackIcon} + fallbackIcon={currentUserPersonalDetails?.fallbackIcon} editIconStyle={styles.smallEditIconAccount} /> - {currentUserPersonalDetails?.displayName && usernameContainEmojis ? ( + {processedTextArray.length !== 0 ? ( Date: Fri, 26 Jul 2024 10:36:12 +0200 Subject: [PATCH 21/28] Add isMarkdownEnabled to name input, update boolean values naming --- src/components/Composer/index.native.tsx | 8 ++++---- src/components/Composer/index.tsx | 4 ++-- src/components/TextWithTooltip/index.ios.tsx | 13 +++++++++---- src/hooks/useMarkdownStyle.ts | 6 +++--- src/libs/EmojiUtils.ts | 2 +- src/pages/settings/Profile/DisplayNamePage.tsx | 1 + 6 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/components/Composer/index.native.tsx b/src/components/Composer/index.native.tsx index 273aa5e9f24d..9c380eb336c3 100644 --- a/src/components/Composer/index.native.tsx +++ b/src/components/Composer/index.native.tsx @@ -39,9 +39,9 @@ function Composer( ) { const textInput = useRef(null); const {isFocused, shouldResetFocusRef} = useResetComposerFocus(textInput); - const textContainsOnlyEmojis = useMemo(() => EmojiUtils.containsOnlyEmojis(value ?? ''), [value]); + const doesTextContainOnlyEmojis = useMemo(() => EmojiUtils.containsOnlyEmojis(value ?? ''), [value]); const theme = useTheme(); - const markdownStyle = useMarkdownStyle(textContainsOnlyEmojis, !isGroupPolicyReport ? excludeReportMentionStyle : excludeNoStyles); + const markdownStyle = useMarkdownStyle(doesTextContainOnlyEmojis, !isGroupPolicyReport ? excludeReportMentionStyle : excludeNoStyles); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -74,8 +74,8 @@ function Composer( const maxHeightStyle = useMemo(() => StyleUtils.getComposerMaxHeightStyle(maxLines, isComposerFullSize), [StyleUtils, isComposerFullSize, maxLines]); const composerStyle = useMemo( - () => StyleSheet.flatten([style, textContainsOnlyEmojis ? styles.onlyEmojisTextLineHeight : styles.emojisWithTextLineHeight]), - [style, textContainsOnlyEmojis, styles], + () => StyleSheet.flatten([style, doesTextContainOnlyEmojis ? styles.onlyEmojisTextLineHeight : styles.emojisWithTextLineHeight]), + [style, doesTextContainOnlyEmojis, styles], ); return ( diff --git a/src/components/Composer/index.tsx b/src/components/Composer/index.tsx index 14116d308cf7..4e192e908458 100755 --- a/src/components/Composer/index.tsx +++ b/src/components/Composer/index.tsx @@ -80,10 +80,10 @@ function Composer( }: ComposerProps, ref: ForwardedRef, ) { - const textContainsOnlyEmojis = useMemo(() => EmojiUtils.containsOnlyEmojis(value ?? ''), [value]); + const doesTextContainOnlyEmojis = useMemo(() => EmojiUtils.containsOnlyEmojis(value ?? ''), [value]); const theme = useTheme(); const styles = useThemeStyles(); - const markdownStyle = useMarkdownStyle(textContainsOnlyEmojis, !isGroupPolicyReport ? excludeReportMentionStyle : excludeNoStyles); + const markdownStyle = useMarkdownStyle(doesTextContainOnlyEmojis, !isGroupPolicyReport ? excludeReportMentionStyle : excludeNoStyles); const StyleUtils = useStyleUtils(); const textRef = useRef(null); const textInput = useRef(null); diff --git a/src/components/TextWithTooltip/index.ios.tsx b/src/components/TextWithTooltip/index.ios.tsx index f8e025a2a09e..f7c325b6d14e 100644 --- a/src/components/TextWithTooltip/index.ios.tsx +++ b/src/components/TextWithTooltip/index.ios.tsx @@ -5,18 +5,23 @@ import CONST from '@src/CONST'; import type TextWithTooltipProps from './types'; function TextWithTooltip({text, style, emojiFontSize, numberOfLines = 1}: TextWithTooltipProps) { - const containsEmoji = useMemo(() => { + const processedTextArray = useMemo(() => { const emojisRegex = new RegExp(CONST.REGEX.EMOJIS, CONST.REGEX.EMOJIS.flags.concat('g')); - return !!(emojiFontSize && emojisRegex.test(text)); + const doesTextContainEmojis = !!(emojiFontSize && emojisRegex.test(text)); + + if (!doesTextContainEmojis) { + return []; + } + + return EmojiUtils.splitTextWithEmojis(text); }, [emojiFontSize, text]); - const processedTextArray = useMemo(() => (containsEmoji ? EmojiUtils.splitTextWithEmojis(text) : []), [containsEmoji, text]); return ( - {containsEmoji ? processedTextArray.map(({text: textItem, isEmoji}) => (isEmoji ? {textItem} : textItem)) : text} + {processedTextArray.length !== 0 ? processedTextArray.map(({text: textItem, isEmoji}) => (isEmoji ? {textItem} : textItem)) : text} ); } diff --git a/src/hooks/useMarkdownStyle.ts b/src/hooks/useMarkdownStyle.ts index 5e84f65d2f10..e74181de19fe 100644 --- a/src/hooks/useMarkdownStyle.ts +++ b/src/hooks/useMarkdownStyle.ts @@ -6,7 +6,7 @@ import useTheme from './useTheme'; const defaultEmptyArray: Array = []; -function useMarkdownStyle(inputContainsOnlyEmojis?: boolean, excludeStyles: Array = defaultEmptyArray): MarkdownStyle { +function useMarkdownStyle(doesInputContainOnlyEmojis?: boolean, excludeStyles: Array = defaultEmptyArray): MarkdownStyle { const theme = useTheme(); // this map is used to reset the styles that are not needed - passing undefined value can break the native side @@ -34,7 +34,7 @@ function useMarkdownStyle(inputContainsOnlyEmojis?: boolean, excludeStyles: Arra fontSize: variables.fontSizeLarge, }, emoji: { - fontSize: inputContainsOnlyEmojis ? variables.fontSizeEmojisOnlyComposer : variables.fontSizeEmojisWithinText, + fontSize: doesInputContainOnlyEmojis ? variables.fontSizeEmojisOnlyComposer : variables.fontSizeEmojisWithinText, }, blockquote: { borderColor: theme.border, @@ -86,7 +86,7 @@ function useMarkdownStyle(inputContainsOnlyEmojis?: boolean, excludeStyles: Arra } return styling; - }, [theme, inputContainsOnlyEmojis, excludeStyles, nonStylingDefaultValues]); + }, [theme, doesInputContainOnlyEmojis, excludeStyles, nonStylingDefaultValues]); return markdownStyle; } diff --git a/src/libs/EmojiUtils.ts b/src/libs/EmojiUtils.ts index efb21a44eec4..66a51099a5e9 100644 --- a/src/libs/EmojiUtils.ts +++ b/src/libs/EmojiUtils.ts @@ -107,7 +107,7 @@ const getEmojiUnicode = memoize( .charCodeAt(0) .toString() .split(' ') - .map((val: string) => parseInt(val, 10).toString(16)) + .map((val) => parseInt(val, 10).toString(16)) .join(' '); } diff --git a/src/pages/settings/Profile/DisplayNamePage.tsx b/src/pages/settings/Profile/DisplayNamePage.tsx index fec2ffcd0919..35f5d77cb124 100644 --- a/src/pages/settings/Profile/DisplayNamePage.tsx +++ b/src/pages/settings/Profile/DisplayNamePage.tsx @@ -102,6 +102,7 @@ function DisplayNamePage({isLoadingApp = true, currentUserPersonalDetails}: Disp role={CONST.ROLE.PRESENTATION} defaultValue={currentUserDetails.firstName ?? ''} spellCheck={false} + isMarkdownEnabled /> From 6a3113377d3f6ff1b7fef1dd028851996729f959 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Fri, 26 Jul 2024 11:04:03 +0200 Subject: [PATCH 22/28] Update TextWithEmojiFragment component --- .../report/comment/TextCommentFragment.tsx | 2 +- .../report/comment/TextWithEmojiFragment.tsx | 22 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/pages/home/report/comment/TextCommentFragment.tsx b/src/pages/home/report/comment/TextCommentFragment.tsx index 5c74aafb8e30..81bcd4a7a5d3 100644 --- a/src/pages/home/report/comment/TextCommentFragment.tsx +++ b/src/pages/home/report/comment/TextCommentFragment.tsx @@ -88,7 +88,7 @@ function TextCommentFragment({fragment, styleAsDeleted, styleAsMuted = false, so styleAsDeleted={styleAsDeleted} styleAsMuted={styleAsMuted} isEdited={fragment?.isEdited} - emojisOnly={containsOnlyEmojis} + hasEmojisOnly={containsOnlyEmojis} /> ) : ( <> diff --git a/src/pages/home/report/comment/TextWithEmojiFragment.tsx b/src/pages/home/report/comment/TextWithEmojiFragment.tsx index f3286bbbf838..cda59c7515a9 100644 --- a/src/pages/home/report/comment/TextWithEmojiFragment.tsx +++ b/src/pages/home/report/comment/TextWithEmojiFragment.tsx @@ -1,9 +1,10 @@ -import React from 'react'; +import React, {useMemo} from 'react'; import type {StyleProp, TextStyle} from 'react-native'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import useWindowDimensions from '@hooks/useWindowDimensions'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import * as EmojiUtils from '@libs/EmojiUtils'; import variables from '@styles/variables'; @@ -22,27 +23,26 @@ type TextWithEmojiFragmentProps = { /** Should this message fragment be styled as muted? */ styleAsMuted?: boolean; - /** Is message displayed on narrow screen? */ - isSmallScreenWidth?: boolean; - /** Should "(edited)" suffix be rendered? */ isEdited?: boolean; /** Does message contain only emojis? */ - emojisOnly?: boolean; + hasEmojisOnly?: boolean; }; -function TextWithEmojiFragment({message, passedStyles, styleAsDeleted, styleAsMuted, isSmallScreenWidth, isEdited, emojisOnly}: TextWithEmojiFragmentProps) { +function TextWithEmojiFragment({message, passedStyles, styleAsDeleted, styleAsMuted, isEdited, hasEmojisOnly}: TextWithEmojiFragmentProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const theme = useTheme(); - const processedTextArray = EmojiUtils.splitTextWithEmojis(message); + const {isSmallScreenWidth} = useWindowDimensions(); + + const processedTextArray = useMemo(() => EmojiUtils.splitTextWithEmojis(message), [message]); return ( - + {processedTextArray.map(({text, isEmoji}) => isEmoji ? ( - {text} + {text} ) : ( {text} @@ -62,7 +62,7 @@ function TextWithEmojiFragment({message, passedStyles, styleAsDeleted, styleAsMu {isEdited && ( <> {' '} From bea55ee488cf7aa282ce942d784e83b47e6c853f Mon Sep 17 00:00:00 2001 From: VickyStash Date: Fri, 26 Jul 2024 11:59:38 +0200 Subject: [PATCH 23/28] Create ReportActionItemMessageHeaderSender component --- .../home/report/ReportActionItemFragment.tsx | 35 ++-------- .../ReportActionItemMessageHeaderSender.tsx | 65 +++++++++++++++++++ 2 files changed, 71 insertions(+), 29 deletions(-) create mode 100644 src/pages/home/report/ReportActionItemMessageHeaderSender.tsx diff --git a/src/pages/home/report/ReportActionItemFragment.tsx b/src/pages/home/report/ReportActionItemFragment.tsx index 54bc027e9df7..7c0974f74a4b 100644 --- a/src/pages/home/report/ReportActionItemFragment.tsx +++ b/src/pages/home/report/ReportActionItemFragment.tsx @@ -2,13 +2,10 @@ import React, {memo} from 'react'; import type {StyleProp, TextStyle} from 'react-native'; import RenderHTML from '@components/RenderHTML'; import Text from '@components/Text'; -import UserDetailsTooltip from '@components/UserDetailsTooltip'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; import convertToLTR from '@libs/convertToLTR'; -import type {TextWithEmoji} from '@libs/EmojiUtils'; -import * as EmojiUtils from '@libs/EmojiUtils'; import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; @@ -17,6 +14,7 @@ import type {Message} from '@src/types/onyx/ReportAction'; import type ReportActionName from '@src/types/onyx/ReportActionName'; import AttachmentCommentFragment from './comment/AttachmentCommentFragment'; import TextCommentFragment from './comment/TextCommentFragment'; +import ReportActionItemMessageHeaderSender from './ReportActionItemMessageHeaderSender'; type ReportActionItemFragmentProps = { /** Users accountID */ @@ -160,35 +158,14 @@ function ReportActionItemFragment({ ); } - const emojisRegex = new RegExp(CONST.REGEX.EMOJIS, CONST.REGEX.EMOJIS.flags.concat('g')); - const containEmoji = emojisRegex.test(fragment.text); - let processedTextArray: TextWithEmoji[] = []; - if (containEmoji) { - processedTextArray = EmojiUtils.splitTextWithEmojis(fragment.text); - } - return ( - - {containEmoji ? ( - - {processedTextArray.map(({text, isEmoji}) => (isEmoji ? {text} : text))} - - ) : ( - - {fragment?.text} - - )} - + fragmentText={fragment.text} + actorIcon={actorIcon} + isSingleLine={isSingleLine} + /> ); } case 'LINK': diff --git a/src/pages/home/report/ReportActionItemMessageHeaderSender.tsx b/src/pages/home/report/ReportActionItemMessageHeaderSender.tsx new file mode 100644 index 000000000000..5bb434b4b7a9 --- /dev/null +++ b/src/pages/home/report/ReportActionItemMessageHeaderSender.tsx @@ -0,0 +1,65 @@ +import React, {useMemo} from 'react'; +import Text from '@components/Text'; +import UserDetailsTooltip from '@components/UserDetailsTooltip'; +import useThemeStyles from '@hooks/useThemeStyles'; +import * as EmojiUtils from '@libs/EmojiUtils'; +import CONST from '@src/CONST'; +import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; + +type ReportActionItemMessageHeaderSenderProps = { + /** Text to display */ + fragmentText: string; + + /** Users accountID */ + accountID: number; + + /** Should this fragment be contained in a single line? */ + isSingleLine?: boolean; + + /** The accountID of the copilot who took this action on behalf of the user */ + delegateAccountID?: number; + + /** Actor icon */ + actorIcon?: OnyxCommon.Icon; +}; + +function ReportActionItemMessageHeaderSender({fragmentText, accountID, delegateAccountID, actorIcon, isSingleLine}: ReportActionItemMessageHeaderSenderProps) { + const styles = useThemeStyles(); + + const processedTextArray = useMemo(() => { + const emojisRegex = new RegExp(CONST.REGEX.EMOJIS, CONST.REGEX.EMOJIS.flags.concat('g')); + const doesTextContainEmojis = emojisRegex.test(fragmentText); + + if (!doesTextContainEmojis) { + return []; + } + + return EmojiUtils.splitTextWithEmojis(fragmentText); + }, [fragmentText]); + + return ( + + {processedTextArray.length !== 0 ? ( + + {processedTextArray.map(({text, isEmoji}) => (isEmoji ? {text} : text))} + + ) : ( + + {fragmentText} + + )} + + ); +} + +export default ReportActionItemMessageHeaderSender; From 8049e444a586e358dc0d53e42a494f44ef038fa6 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Fri, 26 Jul 2024 12:09:30 +0200 Subject: [PATCH 24/28] Minor improvement --- .../ReportActionItemMessageHeaderSender.tsx | 21 ++++++------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/src/pages/home/report/ReportActionItemMessageHeaderSender.tsx b/src/pages/home/report/ReportActionItemMessageHeaderSender.tsx index 5bb434b4b7a9..0923c7625285 100644 --- a/src/pages/home/report/ReportActionItemMessageHeaderSender.tsx +++ b/src/pages/home/report/ReportActionItemMessageHeaderSender.tsx @@ -43,21 +43,12 @@ function ReportActionItemMessageHeaderSender({fragmentText, accountID, delegateA delegateAccountID={delegateAccountID} icon={actorIcon} > - {processedTextArray.length !== 0 ? ( - - {processedTextArray.map(({text, isEmoji}) => (isEmoji ? {text} : text))} - - ) : ( - - {fragmentText} - - )} + + {processedTextArray.length !== 0 ? processedTextArray.map(({text, isEmoji}) => (isEmoji ? {text} : text)) : fragmentText} + ); } From 959fac577871051d134d85fa3b0f580c522e549c Mon Sep 17 00:00:00 2001 From: VickyStash Date: Fri, 26 Jul 2024 12:15:34 +0200 Subject: [PATCH 25/28] Rename boolean variable --- .../report/comment/TextCommentFragment.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/pages/home/report/comment/TextCommentFragment.tsx b/src/pages/home/report/comment/TextCommentFragment.tsx index 81bcd4a7a5d3..8ceedde9ca6d 100644 --- a/src/pages/home/report/comment/TextCommentFragment.tsx +++ b/src/pages/home/report/comment/TextCommentFragment.tsx @@ -52,12 +52,12 @@ function TextCommentFragment({fragment, styleAsDeleted, styleAsMuted = false, so // If the only difference between fragment.text and fragment.html is
tags and emoji tag // on native, we render it as text, not as html // on other device, only render it as text if the only difference is
tag - const containsOnlyEmojis = EmojiUtils.containsOnlyEmojis(text ?? ''); - if (!shouldRenderAsText(html, text ?? '') && !(containsOnlyEmojis && styleAsDeleted)) { - const editedTag = fragment?.isEdited ? `` : ''; + const doesTextContainOnlyEmojis = EmojiUtils.containsOnlyEmojis(text ?? ''); + if (!shouldRenderAsText(html, text ?? '') && !(doesTextContainOnlyEmojis && styleAsDeleted)) { + const editedTag = fragment?.isEdited ? `` : ''; const htmlWithDeletedTag = styleAsDeleted ? `${html}` : html; - const htmlContent = containsOnlyEmojis ? Str.replaceAll(htmlWithDeletedTag, '', '') : htmlWithDeletedTag; + const htmlContent = doesTextContainOnlyEmojis ? Str.replaceAll(htmlWithDeletedTag, '', '') : htmlWithDeletedTag; let htmlWithTag = editedTag ? `${htmlContent}${editedTag}` : htmlContent; if (styleAsMuted) { @@ -76,26 +76,26 @@ function TextCommentFragment({fragment, styleAsDeleted, styleAsMuted = false, so const emojisRegex = new RegExp(CONST.REGEX.EMOJIS, CONST.REGEX.EMOJIS.flags.concat('g')); return ( - + - {emojisRegex.test(message ?? '') && !containsOnlyEmojis ? ( + {emojisRegex.test(message ?? '') && !doesTextContainOnlyEmojis ? ( ) : ( <> {' '} From a5dc7396df27740b17545fd284c2492828a04050 Mon Sep 17 00:00:00 2001 From: VickyStash Date: Fri, 26 Jul 2024 12:17:33 +0200 Subject: [PATCH 26/28] Minor improvement --- src/pages/home/report/comment/TextCommentFragment.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/comment/TextCommentFragment.tsx b/src/pages/home/report/comment/TextCommentFragment.tsx index 8ceedde9ca6d..ab1c099bfe9e 100644 --- a/src/pages/home/report/comment/TextCommentFragment.tsx +++ b/src/pages/home/report/comment/TextCommentFragment.tsx @@ -105,7 +105,7 @@ function TextCommentFragment({fragment, styleAsDeleted, styleAsMuted = false, so > {convertToLTR(message ?? '')} - {fragment?.isEdited && ( + {!!fragment?.isEdited && ( <> Date: Fri, 26 Jul 2024 12:39:52 +0200 Subject: [PATCH 27/28] Minor update after merging main --- src/pages/home/report/comment/TextWithEmojiFragment.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/home/report/comment/TextWithEmojiFragment.tsx b/src/pages/home/report/comment/TextWithEmojiFragment.tsx index cda59c7515a9..cc0da8f81fb0 100644 --- a/src/pages/home/report/comment/TextWithEmojiFragment.tsx +++ b/src/pages/home/report/comment/TextWithEmojiFragment.tsx @@ -2,9 +2,9 @@ import React, {useMemo} from 'react'; import type {StyleProp, TextStyle} from 'react-native'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; +import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import useWindowDimensions from '@hooks/useWindowDimensions'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import * as EmojiUtils from '@libs/EmojiUtils'; import variables from '@styles/variables'; @@ -34,7 +34,7 @@ function TextWithEmojiFragment({message, passedStyles, styleAsDeleted, styleAsMu const styles = useThemeStyles(); const {translate} = useLocalize(); const theme = useTheme(); - const {isSmallScreenWidth} = useWindowDimensions(); + const {shouldUseNarrowLayout} = useResponsiveLayout(); const processedTextArray = useMemo(() => EmojiUtils.splitTextWithEmojis(message), [message]); @@ -50,7 +50,7 @@ function TextWithEmojiFragment({message, passedStyles, styleAsDeleted, styleAsMu passedStyles, styleAsDeleted ? styles.offlineFeedback.deleted : undefined, styleAsMuted ? styles.colorMuted : undefined, - !DeviceCapabilities.canUseTouchScreen() || !isSmallScreenWidth ? styles.userSelectText : styles.userSelectNone, + !DeviceCapabilities.canUseTouchScreen() || !shouldUseNarrowLayout ? styles.userSelectText : styles.userSelectNone, hasEmojisOnly ? styles.onlyEmojisText : styles.enhancedLineHeight, ]} > From 08e72ff4dcf5cf05aea094f44b7a81fe2d9a4beb Mon Sep 17 00:00:00 2001 From: VickyStash Date: Fri, 26 Jul 2024 17:16:23 +0200 Subject: [PATCH 28/28] Add ReportActionItemMessageHeaderSender display name --- src/pages/home/report/ReportActionItemMessageHeaderSender.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pages/home/report/ReportActionItemMessageHeaderSender.tsx b/src/pages/home/report/ReportActionItemMessageHeaderSender.tsx index 0923c7625285..e31f8c55947f 100644 --- a/src/pages/home/report/ReportActionItemMessageHeaderSender.tsx +++ b/src/pages/home/report/ReportActionItemMessageHeaderSender.tsx @@ -53,4 +53,6 @@ function ReportActionItemMessageHeaderSender({fragmentText, accountID, delegateA ); } +ReportActionItemMessageHeaderSender.displayName = 'ReportActionItemMessageHeaderSender'; + export default ReportActionItemMessageHeaderSender;