diff --git a/src/components/EmojiSuggestions.js b/src/components/EmojiSuggestions.tsx similarity index 53% rename from src/components/EmojiSuggestions.js rename to src/components/EmojiSuggestions.tsx index 2a15cfba995e..8ab20cf13ad6 100644 --- a/src/components/EmojiSuggestions.js +++ b/src/components/EmojiSuggestions.tsx @@ -1,7 +1,6 @@ -import PropTypes from 'prop-types'; -import React from 'react'; +import React, {ReactElement} from 'react'; import {View} from 'react-native'; -import _ from 'underscore'; +import type {SimpleEmoji} from '@libs/EmojiTrie'; import * as EmojiUtils from '@libs/EmojiUtils'; import getStyledTextArray from '@libs/GetStyledTextArray'; import styles from '@styles/styles'; @@ -9,77 +8,57 @@ import * as StyleUtils from '@styles/StyleUtils'; import AutoCompleteSuggestions from './AutoCompleteSuggestions'; import Text from './Text'; -const propTypes = { +type MeasureParentContainerCallback = (x: number, y: number, width: number) => void; + +type EmojiSuggestionsProps = { /** The index of the highlighted emoji */ - highlightedEmojiIndex: PropTypes.number, + highlightedEmojiIndex?: number; /** Array of suggested emoji */ - emojis: PropTypes.arrayOf( - PropTypes.shape({ - /** The emoji code */ - code: PropTypes.string.isRequired, - - /** The name of the emoji */ - name: PropTypes.string.isRequired, - - /** Array of different skin tone variants. - * If provided, it will be indexed with props.preferredSkinToneIndex */ - types: PropTypes.arrayOf(PropTypes.string.isRequired), - }), - ).isRequired, + emojis: SimpleEmoji[]; /** Fired when the user selects an emoji */ - onSelect: PropTypes.func.isRequired, + onSelect: (index: number) => void; /** Emoji prefix that follows the colon */ - prefix: PropTypes.string.isRequired, + prefix: string; /** Show that we can use large emoji picker. Depending on available space * and whether the input is expanded, we can have a small or large emoji * suggester. When this value is false, the suggester will have a height of * 2.5 items. When this value is true, the height can be up to 5 items. */ - isEmojiPickerLarge: PropTypes.bool.isRequired, + isEmojiPickerLarge: boolean; /** Stores user's preferred skin tone */ - preferredSkinToneIndex: PropTypes.number.isRequired, + preferredSkinToneIndex: number; /** Meaures the parent container's position and dimensions. */ - measureParentContainer: PropTypes.func, -}; - -const defaultProps = { - highlightedEmojiIndex: 0, - measureParentContainer: () => {}, + measureParentContainer: (callback: MeasureParentContainerCallback) => void; }; /** * Create unique keys for each emoji item - * @param {Object} item - * @param {Number} index - * @returns {String} */ -const keyExtractor = (item, index) => `${item.name}+${index}}`; +const keyExtractor = (item: SimpleEmoji, index: number): string => `${item.name}+${index}}`; -function EmojiSuggestions(props) { +function EmojiSuggestions({emojis, onSelect, prefix, isEmojiPickerLarge, preferredSkinToneIndex, highlightedEmojiIndex = 0, measureParentContainer = () => {}}: EmojiSuggestionsProps) { /** * Render an emoji suggestion menu item component. - * @param {Object} item - * @returns {JSX.Element} */ - const renderSuggestionMenuItem = (item) => { - const styledTextArray = getStyledTextArray(item.name, props.prefix); + const renderSuggestionMenuItem = (item: SimpleEmoji): ReactElement => { + const styledTextArray = getStyledTextArray(item.name, prefix); return ( - {EmojiUtils.getEmojiCodeWithSkinColor(item, props.preferredSkinToneIndex)} + {EmojiUtils.getEmojiCodeWithSkinColor(item, preferredSkinToneIndex)} : - {_.map(styledTextArray, ({text, isColored}, i) => ( + {styledTextArray.map(({text, isColored}) => ( {text} @@ -93,20 +72,18 @@ function EmojiSuggestions(props) { return ( ); } -EmojiSuggestions.propTypes = propTypes; -EmojiSuggestions.defaultProps = defaultProps; EmojiSuggestions.displayName = 'EmojiSuggestions'; export default EmojiSuggestions; diff --git a/src/libs/EmojiTrie.ts b/src/libs/EmojiTrie.ts index a4074f22f835..a45d1bc45b33 100644 --- a/src/libs/EmojiTrie.ts +++ b/src/libs/EmojiTrie.ts @@ -5,14 +5,20 @@ import CONST from '@src/CONST'; import Timing from './actions/Timing'; import Trie from './Trie'; -type Emoji = { +type HeaderEmoji = { code: string; - header?: boolean; - icon?: React.FC; - name?: string; + header: boolean; + icon: React.FC; +}; + +type SimpleEmoji = { + code: string; + name: string; types?: string[]; }; +type Emoji = HeaderEmoji | SimpleEmoji; + type LocalizedEmoji = { name?: string; keywords: string[]; @@ -51,7 +57,7 @@ type EmojiTrie = { * @param name The localized name of the emoji. * @param shouldPrependKeyword Prepend the keyword (instead of append) to the suggestions */ -function addKeywordsToTrie(trie: Trie, keywords: string[], item: Emoji, name: string, shouldPrependKeyword = false) { +function addKeywordsToTrie(trie: Trie, keywords: string[], item: SimpleEmoji, name: string, shouldPrependKeyword = false) { keywords.forEach((keyword) => { const keywordNode = trie.search(keyword); if (!keywordNode) { @@ -84,37 +90,35 @@ function createTrie(lang: SupportedLanguage = CONST.LOCALES.DEFAULT): Trie { - if (!item.name) { - return; - } - - const englishName = item.name; - const localeName = langEmojis?.[item.code]?.name ?? englishName; - - const node = trie.search(localeName); - if (!node) { - trie.add(localeName, {code: item.code, types: item.types, name: localeName, suggestions: []}); - } else { - trie.update(localeName, {code: item.code, types: item.types, name: localeName, suggestions: node.metaData.suggestions}); - } - - const nameParts = getNameParts(localeName).slice(1); // We remove the first part because we already index the full name. - addKeywordsToTrie(trie, nameParts, item, localeName); - - // Add keywords for both the locale language and English to enable users to search using either language. - const keywords = (langEmojis?.[item.code]?.keywords ?? []).concat(isDefaultLocale ? [] : defaultLangEmojis?.[item.code]?.keywords ?? []); - addKeywordsToTrie(trie, keywords, item, localeName); - - /** - * If current language isn't the default, prepend the English name of the emoji in the suggestions as well. - * We do this because when the user types the english name of the emoji, we want to show the emoji in the suggestions before all the others. - */ - if (!isDefaultLocale) { - const englishNameParts = getNameParts(englishName); - addKeywordsToTrie(trie, englishNameParts, item, localeName, true); - } - }); + emojis + .filter((item: Emoji): item is SimpleEmoji => !(item as HeaderEmoji).header) + .forEach((item: SimpleEmoji) => { + const englishName = item.name; + const localeName = langEmojis?.[item.code]?.name ?? englishName; + + const node = trie.search(localeName); + if (!node) { + trie.add(localeName, {code: item.code, types: item.types, name: localeName, suggestions: []}); + } else { + trie.update(localeName, {code: item.code, types: item.types, name: localeName, suggestions: node.metaData.suggestions}); + } + + const nameParts = getNameParts(localeName).slice(1); // We remove the first part because we already index the full name. + addKeywordsToTrie(trie, nameParts, item, localeName); + + // Add keywords for both the locale language and English to enable users to search using either language. + const keywords = (langEmojis?.[item.code]?.keywords ?? []).concat(isDefaultLocale ? [] : defaultLangEmojis?.[item.code]?.keywords ?? []); + addKeywordsToTrie(trie, keywords, item, localeName); + + /** + * If current language isn't the default, prepend the English name of the emoji in the suggestions as well. + * We do this because when the user types the english name of the emoji, we want to show the emoji in the suggestions before all the others. + */ + if (!isDefaultLocale) { + const englishNameParts = getNameParts(englishName); + addKeywordsToTrie(trie, englishNameParts, item, localeName, true); + } + }); return trie; } @@ -124,3 +128,4 @@ const emojiTrie: EmojiTrie = supportedLanguages.reduce((prev, cur) => ({...prev, Timing.end(CONST.TIMING.TRIE_INITIALIZATION); export default emojiTrie; +export type {SimpleEmoji}; diff --git a/src/styles/StyleUtils.ts b/src/styles/StyleUtils.ts index 330aa4344c8f..de936570291f 100644 --- a/src/styles/StyleUtils.ts +++ b/src/styles/StyleUtils.ts @@ -1018,7 +1018,7 @@ function getAutoCompleteSuggestionContainerStyle(itemsHeight: number): ViewStyle /** * Select the correct color for text. */ -function getColoredBackgroundStyle(isColored: boolean): ViewStyle { +function getColoredBackgroundStyle(isColored: boolean): TextStyle { return {backgroundColor: isColored ? themeColors.link : undefined}; }