diff --git a/src/components/EmojiPicker/EmojiPickerMenu/index.js b/src/components/EmojiPicker/EmojiPickerMenu/index.js index e7af97145347..3dfc5f59bb38 100755 --- a/src/components/EmojiPicker/EmojiPickerMenu/index.js +++ b/src/components/EmojiPicker/EmojiPickerMenu/index.js @@ -195,7 +195,7 @@ class EmojiPickerMenu extends Component { return; } const emoji = lodashGet(item, ['types', this.props.preferredSkinTone], item.code); - this.addToFrequentAndSelectEmoji(emoji, item); + this.props.onEmojiSelected(emoji, item); return; } @@ -258,16 +258,6 @@ class EmojiPickerMenu extends Component { document.removeEventListener('mousemove', this.mouseMoveHandler); } - /** - * @param {String} emoji - * @param {Object} emojiObject - */ - addToFrequentAndSelectEmoji(emoji, emojiObject) { - const frequentEmojiList = EmojiUtils.getFrequentlyUsedEmojis(emojiObject); - User.updateFrequentlyUsedEmojis(frequentEmojiList); - this.props.onEmojiSelected(emoji, emojiObject); - } - /** * Focuses the search Input and has the text selected */ @@ -466,7 +456,7 @@ class EmojiPickerMenu extends Component { return ( this.addToFrequentAndSelectEmoji(emoji, item)} + onPress={(emoji) => this.props.onEmojiSelected(emoji, item)} onHoverIn={() => this.setState({highlightedIndex: index, isUsingKeyboardMovement: false})} onHoverOut={() => { if (this.state.arePointerEventsDisabled) { diff --git a/src/components/EmojiPicker/EmojiPickerMenu/index.native.js b/src/components/EmojiPicker/EmojiPickerMenu/index.native.js index dd5c18439cc1..fe8c3e275ad2 100644 --- a/src/components/EmojiPicker/EmojiPickerMenu/index.native.js +++ b/src/components/EmojiPicker/EmojiPickerMenu/index.native.js @@ -86,16 +86,6 @@ function EmojiPickerMenu({preferredLocale, onEmojiSelected, preferredSkinTone, t setHeaderIndices(undefined); }, 300); - /** - * @param {String} emoji - * @param {Object} emojiObject - */ - const addToFrequentAndSelectEmoji = (emoji, emojiObject) => { - const frequentEmojiList = EmojiUtils.getFrequentlyUsedEmojis(emojiObject); - User.updateFrequentlyUsedEmojis(frequentEmojiList); - onEmojiSelected(emoji, emojiObject); - }; - /** * @param {Number} skinTone */ @@ -152,7 +142,7 @@ function EmojiPickerMenu({preferredLocale, onEmojiSelected, preferredSkinTone, t return ( addToFrequentAndSelectEmoji(emoji, item))} + onPress={singleExecution((emoji) => onEmojiSelected(emoji, item))} emoji={emojiCode} /> ); diff --git a/src/libs/EmojiUtils.js b/src/libs/EmojiUtils.js index 136eee5a4116..af498831f4a4 100644 --- a/src/libs/EmojiUtils.js +++ b/src/libs/EmojiUtils.js @@ -278,17 +278,10 @@ function extractEmojis(text) { } const emojis = []; - - // Text can contain similar emojis as well as their skin tone variants. Create a Set to remove duplicate emojis from the search. - const foundEmojiCodes = new Set(); - for (let i = 0; i < parsedEmojis.length; i++) { const character = parsedEmojis[i]; const emoji = Emojis.emojiCodeTableWithSkinTones[character]; - - // Add the parsed emoji to the final emojis if not already present. - if (emoji && !foundEmojiCodes.has(emoji.code)) { - foundEmojiCodes.add(emoji.code); + if (emoji) { emojis.push(emoji); } } @@ -296,6 +289,24 @@ function extractEmojis(text) { return emojis; } +/** + * Take the current emojis and the former emojis and return the emojis that were added, if we add an already existing emoji, we also return it + * @param {Object[]} currentEmojis The array of current emojis + * @param {Object[]} formerEmojis The array of former emojis + * @returns {Object[]} The array of added emojis + */ +function getAddedEmojis(currentEmojis, formerEmojis) { + const newEmojis = [...currentEmojis]; + // We are removing the emojis from the newEmojis array if they were already present before. + formerEmojis.forEach((formerEmoji) => { + const indexOfAlreadyPresentEmoji = _.findIndex(newEmojis, (newEmoji) => newEmoji.code === formerEmoji.code); + if (indexOfAlreadyPresentEmoji >= 0) { + newEmojis.splice(indexOfAlreadyPresentEmoji, 1); + } + }); + return newEmojis; +} + /** * Replace any emoji name in a text with the emoji icon. * If we're on mobile, we also add a space after the emoji granted there's no text after it. @@ -484,4 +495,6 @@ export { getPreferredEmojiCode, getUniqueEmojiCodes, replaceAndExtractEmojis, + extractEmojis, + getAddedEmojis, }; diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js index a4777556dda7..faa710d2cd6b 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js @@ -102,8 +102,14 @@ function ComposerWithSuggestions({ const {preferredLocale} = useLocalize(); const isFocused = useIsFocused(); const navigation = useNavigation(); - - const [value, setValue] = useState(() => getDraftComment(reportID) || ''); + const emojisPresentBefore = useRef([]); + const [value, setValue] = useState(() => { + const draft = getDraftComment(reportID) || ''; + if (draft) { + emojisPresentBefore.current = EmojiUtils.extractEmojis(draft); + } + return draft; + }); const commentRef = useRef(value); const {isSmallScreenWidth} = useWindowDimensions(); @@ -154,14 +160,6 @@ function ComposerWithSuggestions({ debouncedLowerIsScrollLikelyLayoutTriggered(); }, [debouncedLowerIsScrollLikelyLayoutTriggered]); - const onInsertedEmoji = useCallback( - (emojiObject) => { - insertedEmojisRef.current = [...insertedEmojisRef.current, emojiObject]; - debouncedUpdateFrequentlyUsedEmojis(emojiObject); - }, - [debouncedUpdateFrequentlyUsedEmojis], - ); - /** * Set the TextInput Ref * @@ -206,10 +204,13 @@ function ComposerWithSuggestions({ const {text: newComment, emojis} = EmojiUtils.replaceAndExtractEmojis(commentValue, preferredSkinTone, preferredLocale); if (!_.isEmpty(emojis)) { - insertedEmojisRef.current = [...insertedEmojisRef.current, ...emojis]; - debouncedUpdateFrequentlyUsedEmojis(); + const newEmojis = EmojiUtils.getAddedEmojis(emojis, emojisPresentBefore.current); + if (!_.isEmpty(newEmojis)) { + insertedEmojisRef.current = [...insertedEmojisRef.current, ...newEmojis]; + debouncedUpdateFrequentlyUsedEmojis(); + } } - + emojisPresentBefore.current = emojis; setIsCommentEmpty(!!newComment.match(/^(\s)*$/)); setValue(newComment); if (commentValue !== newComment) { @@ -550,7 +551,6 @@ function ComposerWithSuggestions({ isComposerFullSize={isComposerFullSize} updateComment={updateComment} composerHeight={composerHeight} - onInsertedEmoji={onInsertedEmoji} measureParentContainer={measureParentContainer} // Input value={value} diff --git a/src/pages/home/report/ReportActionCompose/SuggestionEmoji.js b/src/pages/home/report/ReportActionCompose/SuggestionEmoji.js index 910a338c83b6..8ffed01f1068 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionEmoji.js +++ b/src/pages/home/report/ReportActionCompose/SuggestionEmoji.js @@ -39,9 +39,6 @@ const propTypes = { /** Function to clear the input */ resetKeyboardInput: PropTypes.func.isRequired, - /** Callback when a emoji was inserted */ - onInsertedEmoji: PropTypes.func.isRequired, - ...SuggestionProps.baseProps, }; @@ -61,7 +58,6 @@ function SuggestionEmoji({ isAutoSuggestionPickerLarge, forwardedRef, resetKeyboardInput, - onInsertedEmoji, measureParentContainer, }) { const [suggestionValues, setSuggestionValues] = useState(defaultSuggestionsValues); @@ -102,10 +98,8 @@ function SuggestionEmoji({ end: suggestionValues.colonIndex + emojiCode.length + CONST.SPACE_LENGTH, }); setSuggestionValues((prevState) => ({...prevState, suggestedEmojis: []})); - - onInsertedEmoji(emojiObject); }, - [onInsertedEmoji, preferredSkinTone, resetKeyboardInput, selection.end, setSelection, suggestionValues.colonIndex, suggestionValues.suggestedEmojis, updateComment, value], + [preferredSkinTone, resetKeyboardInput, selection.end, setSelection, suggestionValues.colonIndex, suggestionValues.suggestedEmojis, updateComment, value], ); /** diff --git a/src/pages/home/report/ReportActionCompose/Suggestions.js b/src/pages/home/report/ReportActionCompose/Suggestions.js index a00bd342b17d..0e98e69d31d1 100644 --- a/src/pages/home/report/ReportActionCompose/Suggestions.js +++ b/src/pages/home/report/ReportActionCompose/Suggestions.js @@ -9,9 +9,6 @@ const propTypes = { /** A ref to this component */ forwardedRef: PropTypes.shape({current: PropTypes.shape({})}), - /** Callback when a emoji was inserted */ - onInsertedEmoji: PropTypes.func.isRequired, - /** Function to clear the input */ resetKeyboardInput: PropTypes.func.isRequired, @@ -28,19 +25,7 @@ const defaultProps = { * * @returns {React.Component} */ -function Suggestions({ - isComposerFullSize, - value, - setValue, - selection, - setSelection, - updateComment, - composerHeight, - forwardedRef, - onInsertedEmoji, - resetKeyboardInput, - measureParentContainer, -}) { +function Suggestions({isComposerFullSize, value, setValue, selection, setSelection, updateComment, composerHeight, forwardedRef, resetKeyboardInput, measureParentContainer}) { const suggestionEmojiRef = useRef(null); const suggestionMentionRef = useRef(null); @@ -114,7 +99,6 @@ function Suggestions({ ref={suggestionEmojiRef} // eslint-disable-next-line react/jsx-props-no-spreading {...baseProps} - onInsertedEmoji={onInsertedEmoji} resetKeyboardInput={resetKeyboardInput} /> getInitialDraft()); + const emojisPresentBefore = useRef([]); + const [draft, setDraft] = useState(() => { + const initialDraft = getInitialDraft(); + if (initialDraft) { + emojisPresentBefore.current = EmojiUtils.extractEmojis(initialDraft); + } + return initialDraft; + }); const [selection, setSelection] = useState(getInitialSelection()); const [isFocused, setIsFocused] = useState(false); const [hasExceededMaxCommentLength, setHasExceededMaxCommentLength] = useState(false); @@ -200,9 +206,13 @@ function ReportActionItemMessageEdit(props) { const {text: newDraft, emojis} = EmojiUtils.replaceAndExtractEmojis(newDraftInput, props.preferredSkinTone, preferredLocale); if (!_.isEmpty(emojis)) { - insertedEmojis.current = [...insertedEmojis.current, ...emojis]; - debouncedUpdateFrequentlyUsedEmojis(); + const newEmojis = EmojiUtils.getAddedEmojis(emojis, emojisPresentBefore.current); + if (!_.isEmpty(newEmojis)) { + insertedEmojis.current = [...insertedEmojis.current, ...newEmojis]; + debouncedUpdateFrequentlyUsedEmojis(); + } } + emojisPresentBefore.current = emojis; setDraft((prevDraft) => { if (newDraftInput !== newDraft) { const remainder = ComposerUtils.getCommonSuffixLength(prevDraft, newDraft);