Skip to content

Commit

Permalink
Merge pull request #23965 from huzaifa-99/23800-update-frequently-use…
Browse files Browse the repository at this point in the history
…d-emojis-in-emoji-popover-menu

Update frequently used emojis in state whenever its onyx key changes
  • Loading branch information
puneetlath authored Aug 2, 2023
2 parents bee8914 + 5949859 commit 6d56956
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 20 deletions.
68 changes: 51 additions & 17 deletions src/components/EmojiPicker/EmojiPickerMenu/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ const propTypes = {
/** Stores user's preferred skin tone */
preferredSkinTone: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

/** Stores user's frequently used emojis */
// eslint-disable-next-line react/forbid-prop-types
frequentlyUsedEmojis: PropTypes.arrayOf(PropTypes.object),

/** Props related to the dimensions of the window */
...windowDimensionsPropTypes,

Expand All @@ -42,6 +46,7 @@ const propTypes = {
const defaultProps = {
forwardedRef: () => {},
preferredSkinTone: CONST.EMOJI_DEFAULT_SKIN_TONE,
frequentlyUsedEmojis: [],
};

class EmojiPickerMenu extends Component {
Expand All @@ -54,23 +59,6 @@ class EmojiPickerMenu extends Component {
// Ref for emoji FlatList
this.emojiList = undefined;

// If we're on Windows, don't display the flag emojis (the last category),
// since Windows doesn't support them
const flagHeaderIndex = _.findIndex(emojis, (emoji) => emoji.header && emoji.code === 'flags');
this.emojis =
getOperatingSystem() === CONST.OS.WINDOWS
? EmojiUtils.mergeEmojisWithFrequentlyUsedEmojis(emojis.slice(0, flagHeaderIndex))
: EmojiUtils.mergeEmojisWithFrequentlyUsedEmojis(emojis);

// Get the header emojis along with the code, index and icon.
// index is the actual header index starting at the first emoji and counting each one
this.headerEmojis = EmojiUtils.getHeaderEmojis(this.emojis);

// This is the indices of each header's Row
// The positions are static, and are calculated as index/numColumns (8 in our case)
// This is because each row of 8 emojis counts as one index to the flatlist
this.headerRowIndices = _.map(this.headerEmojis, (headerEmoji) => Math.floor(headerEmoji.index / CONST.EMOJI_NUM_PER_ROW));

// We want consistent auto focus behavior on input between native and mWeb so we have some auto focus management code that will
// prevent auto focus when open picker for mobile device
this.shouldFocusInputOnScreenFocus = canFocusInputOnScreenFocus();
Expand All @@ -91,6 +79,11 @@ class EmojiPickerMenu extends Component {
this.currentScrollOffset = 0;
this.firstNonHeaderIndex = 0;

const {filteredEmojis, headerEmojis, headerRowIndices} = this.getEmojisAndHeaderRowIndices();
this.emojis = filteredEmojis;
this.headerEmojis = headerEmojis;
this.headerRowIndices = headerRowIndices;

this.state = {
filteredEmojis: this.emojis,
headerIndices: this.headerRowIndices,
Expand All @@ -117,6 +110,19 @@ class EmojiPickerMenu extends Component {
this.setFirstNonHeaderIndex(this.emojis);
}

componentDidUpdate(prevProps) {
if (prevProps.frequentlyUsedEmojis === this.props.frequentlyUsedEmojis) return;

const {filteredEmojis, headerEmojis, headerRowIndices} = this.getEmojisAndHeaderRowIndices();
this.emojis = filteredEmojis;
this.headerEmojis = headerEmojis;
this.headerRowIndices = headerRowIndices;
this.setState({
filteredEmojis: this.emojis,
headerIndices: this.headerRowIndices,
});
}

componentWillUnmount() {
this.cleanupEventHandlers();
}
Expand All @@ -130,6 +136,31 @@ class EmojiPickerMenu extends Component {
this.setState({selection: event.nativeEvent.selection});
}

/**
* Calculate the filtered + header emojis and header row indices
* @returns {Object}
*/
getEmojisAndHeaderRowIndices() {
// If we're on Windows, don't display the flag emojis (the last category),
// since Windows doesn't support them
const flagHeaderIndex = _.findIndex(emojis, (emoji) => emoji.header && emoji.code === 'flags');
const filteredEmojis =
getOperatingSystem() === CONST.OS.WINDOWS
? EmojiUtils.mergeEmojisWithFrequentlyUsedEmojis(emojis.slice(0, flagHeaderIndex))
: EmojiUtils.mergeEmojisWithFrequentlyUsedEmojis(emojis);

// Get the header emojis along with the code, index and icon.
// index is the actual header index starting at the first emoji and counting each one
const headerEmojis = EmojiUtils.getHeaderEmojis(filteredEmojis);

// This is the indices of each header's Row
// The positions are static, and are calculated as index/numColumns (8 in our case)
// This is because each row of 8 emojis counts as one index to the flatlist
const headerRowIndices = _.map(headerEmojis, (headerEmoji) => Math.floor(headerEmoji.index / CONST.EMOJI_NUM_PER_ROW));

return {filteredEmojis, headerEmojis, headerRowIndices};
}

/**
* Find and store index of the first emoji item
* @param {Array} filteredEmojis
Expand Down Expand Up @@ -553,6 +584,9 @@ export default compose(
preferredSkinTone: {
key: ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE,
},
frequentlyUsedEmojis: {
key: ONYXKEYS.FREQUENTLY_USED_EMOJIS,
},
}),
)(
React.forwardRef((props, ref) => (
Expand Down
23 changes: 20 additions & 3 deletions src/components/EmojiPicker/EmojiPickerMenu/index.native.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {useState, useMemo} from 'react';
import React, {useState, useMemo, useEffect} from 'react';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import PropTypes from 'prop-types';
Expand Down Expand Up @@ -27,23 +27,37 @@ const propTypes = {
/** Stores user's preferred skin tone */
preferredSkinTone: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

/** Stores user's frequently used emojis */
// eslint-disable-next-line react/forbid-prop-types
frequentlyUsedEmojis: PropTypes.arrayOf(PropTypes.object),

/** Props related to translation */
...withLocalizePropTypes,
};

const defaultProps = {
preferredSkinTone: CONST.EMOJI_DEFAULT_SKIN_TONE,
frequentlyUsedEmojis: [],
};

function EmojiPickerMenu({preferredLocale, onEmojiSelected, preferredSkinTone, translate}) {
function EmojiPickerMenu({preferredLocale, onEmojiSelected, preferredSkinTone, translate, frequentlyUsedEmojis}) {
const emojiList = useAnimatedRef();
const allEmojis = useMemo(() => EmojiUtils.mergeEmojisWithFrequentlyUsedEmojis(emojis), []);
// eslint-disable-next-line react-hooks/exhaustive-deps
const allEmojis = useMemo(() => EmojiUtils.mergeEmojisWithFrequentlyUsedEmojis(emojis), [frequentlyUsedEmojis]);
const headerEmojis = useMemo(() => EmojiUtils.getHeaderEmojis(allEmojis), [allEmojis]);
const headerRowIndices = useMemo(() => _.map(headerEmojis, (headerEmoji) => Math.floor(headerEmoji.index / CONST.EMOJI_NUM_PER_ROW)), [headerEmojis]);
const [filteredEmojis, setFilteredEmojis] = useState(allEmojis);
const [headerIndices, setHeaderIndices] = useState(headerRowIndices);
const {windowWidth} = useWindowDimensions();

useEffect(() => {
setFilteredEmojis(allEmojis);
}, [allEmojis]);

useEffect(() => {
setHeaderIndices(headerRowIndices);
}, [headerRowIndices]);

const getItemLayout = (data, index) => ({length: CONST.EMOJI_PICKER_ITEM_HEIGHT, offset: CONST.EMOJI_PICKER_ITEM_HEIGHT * index, index});

/**
Expand Down Expand Up @@ -200,6 +214,9 @@ export default compose(
preferredSkinTone: {
key: ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE,
},
frequentlyUsedEmojis: {
key: ONYXKEYS.FREQUENTLY_USED_EMOJIS,
},
}),
)(
React.forwardRef((props, ref) => (
Expand Down

0 comments on commit 6d56956

Please sign in to comment.