Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
16be069
add Unicode custom emojis
c3024 Mar 26, 2025
24b31a4
add custom emoji to Android
c3024 Mar 27, 2025
9d64927
apply Unicode Emojis for Native apps
c3024 Mar 28, 2025
46fc170
update missing files
c3024 Mar 28, 2025
209a1c3
fix LHN subtitles and custom emojis on Safari
c3024 Mar 28, 2025
a8abe7a
fix native app emoji only style, update constants
c3024 Mar 31, 2025
f80ca12
update android,ios,unicode,diff fonts for native
c3024 Apr 6, 2025
6784b82
add a temporary patch to testing custom emojis
c3024 Apr 6, 2025
80dc51b
remove ununsed font files
c3024 Apr 6, 2025
e3f4a27
add back accidentally removed xcshareddata files
c3024 Apr 6, 2025
0aa1640
revert the removed lock files
c3024 Apr 6, 2025
4b80502
prettier
c3024 Apr 6, 2025
cb0be92
add back plist and xcsettings files
c3024 Apr 6, 2025
ca49619
remove link assets dev dependency
c3024 Apr 6, 2025
e542e3b
remove linking changes in react-native.config.js
c3024 Apr 6, 2025
0f6ae66
update package-lock.json
c3024 Apr 6, 2025
104eab2
move emoji Unicode to CONST
c3024 Apr 6, 2025
ce37b14
remove comment from CONST
c3024 Apr 6, 2025
4d23b90
remove direct passing of font to suggestions
c3024 Apr 6, 2025
9c7cca1
remove console log comment
c3024 Apr 6, 2025
605096e
fix pending alignment issue with custom emojis
c3024 Apr 7, 2025
df03015
Merge branch 'main' into add-Unicode-custom-emojis
c3024 Apr 7, 2025
d91c534
prettier
c3024 Apr 7, 2025
46507c4
remove expensify-common+2.0.124.patch
c3024 Apr 7, 2025
b4f41ec
lint
c3024 Apr 7, 2025
0aedfa4
fix removeInvisibleCharacters for custom emoji
c3024 Apr 7, 2025
9dd2d81
update isEmptyString considering custom emoji
c3024 Apr 7, 2025
966fff8
lint
c3024 Apr 7, 2025
6bcc151
fix isEmptyString function
c3024 Apr 7, 2025
8175385
use assets ReactFontManager
c3024 Apr 8, 2025
e39dbab
remove separate category for custom emojis
c3024 Apr 8, 2025
ba38136
Merge branch 'main' into add-Unicode-custom-emojis
c3024 Apr 10, 2025
18ee1f8
use latest expensify-common
c3024 Apr 10, 2025
e3e1db2
merge main with signed commit
c3024 Apr 15, 2025
9c18694
Merge branch 'main' into add-Unicode-custom-emojis
c3024 Apr 15, 2025
ce472ff
use textwithcomment only for text and custom emoji
c3024 Apr 15, 2025
02d4280
pass font to emoji in useMarkdownStyle
c3024 Apr 16, 2025
f7c73c7
use markdown PR branch for testing temporarily
c3024 Apr 16, 2025
7797419
Merge branch 'main' into add-Unicode-custom-emojis
c3024 Apr 16, 2025
2f8b8b6
Resolve submodule conflict by using main's Mobile-Expensify
c3024 Apr 16, 2025
991ab92
review comments
c3024 Apr 16, 2025
11bc7db
Merge branch 'main' into add-Unicode-custom-emojis
c3024 Apr 23, 2025
13c8f22
Merge branch 'main' into add-Unicode-custom-emojis
c3024 May 20, 2025
d8d82b9
Merge branch 'main' into add-Unicode-custom-emojis
c3024 May 20, 2025
5db9422
Merge branch 'main' into add-Unicode-custom-emojis
c3024 Jun 17, 2025
1ed61be
fix spellcheck
c3024 Jun 17, 2025
aa4273e
remove unused text in en and es language files
c3024 Jun 17, 2025
b41203f
fix spellcheck
c3024 Jun 17, 2025
41d0588
bump up react native live markdown package version
c3024 Jun 17, 2025
961e93e
update package-lock.json
c3024 Jun 17, 2025
f76a4ce
fix LHN alternate text style
c3024 Jun 19, 2025
2b28f82
lint
c3024 Jun 19, 2025
4243911
prettier
c3024 Jun 19, 2025
ff1ccc9
Merge branch 'main' into add-Unicode-custom-emojis
c3024 Jun 19, 2025
a3e7590
Merge branch 'main' into add-Unicode-custom-emojis
c3024 Jul 1, 2025
3dacab7
spacing
c3024 Jul 3, 2025
6a9d9c8
spacing
c3024 Jul 3, 2025
2fff160
review comments
c3024 Jul 3, 2025
a7567e3
review comments
c3024 Jul 3, 2025
c58a766
Merge branch 'main' into add-Unicode-custom-emojis
c3024 Jul 3, 2025
61646d2
Merge branch 'main' into add-Unicode-custom-emojis
c3024 Jul 3, 2025
5d4f562
revert to the file on main
c3024 Jul 3, 2025
db9da50
indent
c3024 Jul 3, 2025
8788c9e
Merge branch 'main' into add-Unicode-custom-emojis
c3024 Jul 3, 2025
00382d2
Merge branch 'main' into add-Unicode-custom-emojis
c3024 Jul 4, 2025
55f7488
memoize custom emoji function uses
c3024 Jul 9, 2025
ce87f43
Merge branch 'main' into add-Unicode-custom-emojis
c3024 Jul 9, 2025
5e142be
lint fix
c3024 Jul 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,10 @@ class MainApplication : MultiDexApplication(), ReactApplication {

override fun onCreate() {
super.onCreate()
ReactFontManager.getInstance().addCustomFont(this, "Custom Emoji Font", R.font.custom_emoji_font)
ReactFontManager.getInstance().addCustomFont(this, "Expensify New Kansas", R.font.expensify_new_kansas)
ReactFontManager.getInstance().addCustomFont(this, "Expensify Neue", R.font.expensify_neue)
ReactFontManager.getInstance().addCustomFont(this, "Expensify Mono", R.font.expensify_mono)

RNPerformance.getInstance().mark("appCreationStart", false);

if (isOnfidoProcess()) {
Expand Down
4 changes: 4 additions & 0 deletions android/app/src/main/res/font/custom_emoji_font.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:app="http://schemas.android.com/apk/res-auto">
<font app:fontStyle="normal" app:fontWeight="400" app:font="@font/custom_emoji_native_font"/>
</font-family>
Binary file not shown.
4 changes: 4 additions & 0 deletions android/link-assets-manifest.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
{
"migIndex": 2,
"data": [
{
"path": "assets/fonts/native/CustomEmojiNativeFont.ttf",
"sha1": "83acdb6db84b418c51037af51f96ff9c18273a62"
},
{
"path": "assets/fonts/native/ExpensifyMono-Bold.otf",
"sha1": "d70e12540200613e9e6ac9068bed57e4bf477bfe"
Expand Down
5 changes: 5 additions & 0 deletions assets/css/fonts.css
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@
src: url('/fonts/seguiemj.ttf');
}

@font-face {
font-family: Custom Emoji Font;
src: url('/fonts/CustomEmojiWebFont.ttf') format('truetype');
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why a .ttf here when all of our other fonts use woff?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is another font seguiemj.ttf there and AFAIK I have not found any issue with woff/otf/ttf. Nanoemoji gives ttf as default and I found converting it to other fonts unreliable.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I have a feeling that Segui is an artifact - is it even being used? I think at one point a contributor added custom emoji files and we reverted those changes because we wanted to rely on system emoji fonts for everything. So I bet we aren't even using that. Either way, can you please try to generate .woff and see what happens?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried but it did not work for Safari.

}

* {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
Expand Down
5 changes: 5 additions & 0 deletions assets/emojis/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import TravelAndPlaces from '@assets/images/emojiCategoryIcons/plane.svg';
import AnimalsAndNature from '@assets/images/emojiCategoryIcons/plant.svg';
import Activities from '@assets/images/emojiCategoryIcons/soccer-ball.svg';
import FrequentlyUsed from '@assets/images/history.svg';
import CONST from '@src/CONST';
import type {HeaderEmoji, PickerEmojis} from './types';

const skinTones = [
Expand Down Expand Up @@ -7862,6 +7863,10 @@ const emojis: PickerEmojis = [
name: 'wales',
code: '🏴󠁧󠁢󠁷󠁬󠁳󠁿',
},
{
name: 'global_create',
code: CONST.CUSTOM_EMOJIS.GLOBAL_CREATE,
},
];

const categoryFrequentlyUsed: HeaderEmoji = {
Expand Down
3 changes: 3 additions & 0 deletions assets/emojis/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5613,6 +5613,9 @@ const enEmojis: EmojisList = {
'🏴󠁧󠁢󠁷󠁬󠁳󠁿': {
keywords: ['flag'],
},
'\uE100': {
keywords: ['fab', 'floating', 'action', 'button', 'green', 'plus'],
},
};

export default enEmojis;
4 changes: 4 additions & 0 deletions assets/emojis/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7483,6 +7483,10 @@ const esEmojis: EmojisList = {
name: 'gales',
keywords: ['bandera', 'bandera-gales'],
},
'\uE100': {
name: 'crear-global',
keywords: ['fab', 'flotante', 'acción', 'botón', 'verde', 'más'],
},
};

export default esEmojis;
Binary file added assets/fonts/native/CustomEmojiNativeFont.ttf
Binary file not shown.
Binary file added assets/fonts/web/CustomEmojiWebFont.ttf
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file was not configured properly, which caused #84449

Binary file not shown.
4 changes: 4 additions & 0 deletions ios/NewExpensify.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
ED222ED90E074A5481A854FA /* ExpensifyNeue-BoldItalic.otf in Resources */ = {isa = PBXBuildFile; fileRef = 8B28D84EF339436DBD42A203 /* ExpensifyNeue-BoldItalic.otf */; };
F0C450EA2705020500FD2970 /* colors.json in Resources */ = {isa = PBXBuildFile; fileRef = F0C450E92705020500FD2970 /* colors.json */; };
FF941A8D48F849269AB85C9A /* ExpensifyNewKansas-Medium.otf in Resources */ = {isa = PBXBuildFile; fileRef = 44BF435285B94E5B95F90994 /* ExpensifyNewKansas-Medium.otf */; };
59164B2F48344A53975791A9 /* CustomEmojiNativeFont.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B42BBCC5FB8E4E40B1A9202C /* CustomEmojiNativeFont.ttf */; };
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why .ttf for this font and not .otf like the others?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could not make an otf with nanoemoji.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm did you confirm that .ttf works on native devices?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -158,6 +159,7 @@
F0C450E92705020500FD2970 /* colors.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = colors.json; path = ../colors.json; sourceTree = "<group>"; };
F4F8A052A22040339996324B /* ExpensifyNeue-Regular.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "ExpensifyNeue-Regular.otf"; path = "../assets/fonts/native/ExpensifyNeue-Regular.otf"; sourceTree = "<group>"; };
F8839E9820F4C312BD1C9339 /* Pods-NewExpensify.releasedevelopment.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NewExpensify.releasedevelopment.xcconfig"; path = "Target Support Files/Pods-NewExpensify/Pods-NewExpensify.releasedevelopment.xcconfig"; sourceTree = "<group>"; };
B42BBCC5FB8E4E40B1A9202C /* CustomEmojiNativeFont.ttf */ = {isa = PBXFileReference; name = "CustomEmojiNativeFont.ttf"; path = "../assets/fonts/native/CustomEmojiNativeFont.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -319,6 +321,7 @@
8B28D84EF339436DBD42A203 /* ExpensifyNeue-BoldItalic.otf */,
BF6A4C5167244B9FB8E4D4E3 /* ExpensifyNeue-Italic.otf */,
F4F8A052A22040339996324B /* ExpensifyNeue-Regular.otf */,
B42BBCC5FB8E4E40B1A9202C /* CustomEmojiNativeFont.ttf */,
);
name = Resources;
sourceTree = "<group>";
Expand Down Expand Up @@ -491,6 +494,7 @@
D27CE6B77196EF3EF450EEAC /* PrivacyInfo.xcprivacy in Resources */,
524F95D57E75496EBD14B0AA /* ExpensifyMono-BoldItalic.otf in Resources */,
0F749C2B3B8F4562B816DEAB /* BuildFile in Resources */,
59164B2F48344A53975791A9 /* CustomEmojiNativeFont.ttf in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
1 change: 1 addition & 0 deletions ios/NewExpensify/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
<string>ExpensifyNeue-BoldItalic.otf</string>
<string>ExpensifyNeue-Italic.otf</string>
<string>ExpensifyNeue-Regular.otf</string>
<string>CustomEmojiNativeFont.ttf</string>
</array>
<key>UIBackgroundModes</key>
<array>
Expand Down
4 changes: 4 additions & 0 deletions ios/link-assets-manifest.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
{
"migIndex": 2,
"data": [
{
"path": "assets/fonts/native/CustomEmojiNativeFont.ttf",
"sha1": "83acdb6db84b418c51037af51f96ff9c18273a62"
},
{
"path": "assets/fonts/native/ExpensifyMono-Bold.otf",
"sha1": "d70e12540200613e9e6ac9068bed57e4bf477bfe"
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
"@expensify/nitro-utils": "file:./modules/ExpensifyNitroUtils",
"@expensify/react-native-background-task": "file:./modules/background-task",
"@expensify/react-native-hybrid-app": "file:./modules/hybrid-app",
"@expensify/react-native-live-markdown": "0.1.288",
"@expensify/react-native-live-markdown": "0.1.289",
"@expensify/react-native-wallet": "^0.1.5",
"@expo/metro-runtime": "^5.0.4",
"@firebase/app": "^0.10.10",
Expand Down
10 changes: 9 additions & 1 deletion src/CONST/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1727,6 +1727,10 @@ const CONST = {
// Amount of emojis to render ahead at the end of the update cycle
EMOJI_DRAW_AMOUNT: 100,

CUSTOM_EMOJIS: {
GLOBAL_CREATE: '\uE100',
},

INVISIBLE_CODEPOINTS: ['fe0f', '200d', '2066'],

UNICODE: {
Expand Down Expand Up @@ -3429,10 +3433,14 @@ 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, 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,
EMOJIS: /[\p{Extended_Pictographic}\uE000-\uF8FF\u{F0000}-\u{FFFFD}\u{100000}-\u{10FFFD}](\u200D[\p{Extended_Pictographic}\uE000-\uF8FF\u{F0000}-\u{FFFFD}\u{100000}-\u{10FFFD}]|[\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,

PRIVATE_USER_AREA: /[\uE000-\uF8FF\u{F0000}-\u{FFFFD}\u{100000}-\u{10FFFD}]/u,

ONLY_PRIVATE_USER_AREA: /^[\uE000-\uF8FF\u{F0000}-\u{FFFFD}\u{100000}-\u{10FFFD}]+$/u,

TAX_ID: /^\d{9}$/,
NON_NUMERIC: /\D/g,
ANY_SPACE: /\s/g,
Expand Down
7 changes: 6 additions & 1 deletion src/components/EmojiWithTooltip/index.android.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import {useMemo} from 'react';
import Text from '@components/Text';
import useThemeStyles from '@hooks/useThemeStyles';
import {containsCustomEmoji} from '@libs/EmojiUtils';
import type EmojiWithTooltipProps from './types';

function EmojiWithTooltip({emojiCode, style = {}}: EmojiWithTooltipProps) {
return <Text style={style}>{emojiCode}</Text>;
const isCustomEmoji = useMemo(() => containsCustomEmoji(emojiCode), [emojiCode]);
const styles = useThemeStyles();
return <Text style={[style, isCustomEmoji && styles.customEmojiFont]}>{emojiCode}</Text>;
}

EmojiWithTooltip.displayName = 'EmojiWithTooltip';
Expand Down
22 changes: 18 additions & 4 deletions src/components/LHNOptionsList/OptionRowLHN.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import DateUtils from '@libs/DateUtils';
import DomUtils from '@libs/DomUtils';
import {containsCustomEmoji as containsCustomEmojiUtils, containsOnlyCustomEmoji} from '@libs/EmojiUtils';
import {shouldOptionShowTooltip, shouldUseBoldText} from '@libs/OptionsListUtils';
import Parser from '@libs/Parser';
import Performance from '@libs/Performance';
Expand All @@ -36,6 +37,7 @@ import {
isSystemChat,
isThread,
} from '@libs/ReportUtils';
import TextWithEmojiFragment from '@pages/home/report/comment/TextWithEmojiFragment';
import {showContextMenu} from '@pages/home/report/ContextMenu/ReportActionContextMenu';
import FreeTrial from '@pages/settings/Subscription/FreeTrial';
import variables from '@styles/variables';
Expand Down Expand Up @@ -106,6 +108,11 @@ function OptionRowLHN({
: [styles.chatLinkRowPressable, styles.flexGrow1, styles.optionItemAvatarNameWrapper, styles.optionRow, styles.justifyContentCenter],
);

const alternateTextContainsCustomEmojiWithText = useMemo(
() => containsCustomEmojiUtils(optionItem?.alternateText) && !containsOnlyCustomEmoji(optionItem?.alternateText),
[optionItem?.alternateText],
);

if (!optionItem && !isOptionFocused) {
// rendering null as a render item causes the FlashList to render all
// its children and consume significant memory on the first render. We can avoid this by
Expand Down Expand Up @@ -187,7 +194,6 @@ function OptionRowLHN({
hideProductTrainingTooltip();
onSelectRow(optionItem, popoverAnchor);
};

return (
<OfflineWithFeedback
pendingAction={optionItem.pendingAction}
Expand Down Expand Up @@ -307,15 +313,23 @@ function OptionRowLHN({
</Tooltip>
)}
</View>
{optionItem.alternateText ? (
{!!optionItem.alternateText && (
<Text
style={alternateTextStyle}
numberOfLines={1}
accessibilityLabel={translate('accessibilityHints.lastChatMessagePreview')}
>
{Parser.htmlToText(optionItem.alternateText)}
{alternateTextContainsCustomEmojiWithText ? (
<TextWithEmojiFragment
message={Parser.htmlToText(optionItem.alternateText)}
style={[alternateTextStyle, styles.mh0]}
isAlternateText
/>
) : (
Parser.htmlToText(optionItem.alternateText)
)}
</Text>
) : null}
)}
</View>
{optionItem?.descriptiveText ? (
<View style={[styles.flexWrap]}>
Expand Down
11 changes: 10 additions & 1 deletion src/components/Text.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import type {ForwardedRef} from 'react';
import React, {useContext} from 'react';
import React, {useContext, useMemo} from 'react';
// eslint-disable-next-line no-restricted-imports
import {Text as RNText, StyleSheet} from 'react-native';
import type {TextProps as RNTextProps, TextStyle} from 'react-native';
import useTheme from '@hooks/useTheme';
import {containsOnlyCustomEmoji} from '@libs/EmojiUtils';
import type {FontUtilsType} from '@styles/utils/FontUtils';
import FontUtils from '@styles/utils/FontUtils';
import variables from '@styles/variables';
Expand Down Expand Up @@ -51,6 +52,14 @@ function Text(
componentStyle.lineHeight = variables.fontSizeNormalHeight;
}

const isOnlyCustomEmoji = useMemo(() => {
return typeof children === 'string' ? containsOnlyCustomEmoji(children) : false;
}, [children]);

if (isOnlyCustomEmoji) {
componentStyle.fontFamily = FontUtils.fontFamily.single.CUSTOM_EMOJI_FONT?.fontFamily;
}

return (
<RNText
allowFontScaling={false}
Expand Down
1 change: 1 addition & 0 deletions src/hooks/useMarkdownStyle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ function useMarkdownStyle(hasMessageOnlyEmojis: boolean, excludeStyles: Array<ke
fontSize: variables.fontSizeLarge,
},
emoji: {
...FontUtils.fontFamily.platform.CUSTOM_EMOJI_FONT,
fontSize: emojiFontSize,
lineHeight: variables.lineHeightXLarge,
},
Expand Down
20 changes: 20 additions & 0 deletions src/libs/EmojiUtils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
const sortByName = (emoji: Emoji, emojiData: RegExpMatchArray) => !emoji.name.includes(emojiData[0].toLowerCase().slice(1));

let frequentlyUsedEmojis: FrequentlyUsedEmoji[] = [];
Onyx.connect({

Check warning on line 37 in src/libs/EmojiUtils.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.FREQUENTLY_USED_EMOJIS,
callback: (val) => {
if (!val) {
Expand Down Expand Up @@ -661,6 +661,24 @@
);
}

function containsCustomEmoji(text?: string): boolean {
if (!text) {
return false;
}

const privateUseAreaRegex = CONST.REGEX.PRIVATE_USER_AREA;
return privateUseAreaRegex.test(text);
}

function containsOnlyCustomEmoji(text?: string): boolean {
if (!text) {
return false;
}

const privateUseAreaRegex = CONST.REGEX.ONLY_PRIVATE_USER_AREA;
return privateUseAreaRegex.test(text);
}

export type {HeaderIndices, EmojiPickerList, EmojiPickerListItem};

export {
Expand All @@ -686,4 +704,6 @@
getRemovedSkinToneEmoji,
getSpacersIndexes,
splitTextWithEmojis,
containsCustomEmoji,
containsOnlyCustomEmoji,
};
19 changes: 18 additions & 1 deletion src/libs/StringUtils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,22 @@ function sanitizeString(str: string): string {
* Check if the string would be empty if all invisible characters were removed.
*/
function isEmptyString(value: string): boolean {
// We implemented a custom emoji on this Unicode Private Use Area (PUA) code point
// so we should not remove it.
// Temporarily replace \uE100 with a placeholder
const PLACEHOLDER = '<<KEEP_E100>>';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is defined twice (here and in removeInvisibleCharacters), we can move it into CONST or just to upper scope

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just a placeholder and can be anything. I think this is fine here within the function scope.

let transformed = value.replace(/\uE100/g, PLACEHOLDER);

// \p{C} matches all 'Other' characters
// \p{Z} matches all separators (spaces etc.)
// Source: http://www.unicode.org/reports/tr18/#General_Category_Property
let transformed = value.replace(CONST.REGEX.INVISIBLE_CHARACTERS_GROUPS, '');
transformed = transformed.replace(CONST.REGEX.INVISIBLE_CHARACTERS_GROUPS, '');

// Remove other invisible characters that are not in the above unicode categories
transformed = transformed.replace(CONST.REGEX.OTHER_INVISIBLE_CHARACTERS, '');

transformed = transformed.replace(new RegExp(PLACEHOLDER, 'g'), '\uE100');

// Check if after removing invisible characters the string is empty
return transformed === '';
}
Expand All @@ -36,6 +44,12 @@ function isEmptyString(value: string): boolean {
function removeInvisibleCharacters(value: string): string {
let result = value;

// We implemented a custom emoji on this Unicode Private Use Area (PUA) code point
// so we should not remove it.
// Temporarily replace \uE100 with a placeholder
const PLACEHOLDER = '<<KEEP_E100>>';
result = result.replace(/\uE100/g, PLACEHOLDER);

// Remove spaces:
// - \u200B: zero-width space
// - \u2060: word joiner
Expand All @@ -60,6 +74,9 @@ function removeInvisibleCharacters(value: string): string {
// Remove all characters from the 'Separator' (Z) category except for Space Separator (Zs)
result = result.replace(/[\p{Zl}\p{Zp}]/gu, '');

// Restore \uE100 from placeholder
result = result.replace(new RegExp(PLACEHOLDER, 'g'), '\uE100');

// If the result consist of only invisible characters, return an empty string
if (isEmptyString(result)) {
return '';
Expand Down
Loading
Loading