Skip to content

Commit

Permalink
Merge pull request #7578 from mananjadhav/feat/emoji-picker-singleton
Browse files Browse the repository at this point in the history
PR 2: Made EmojiPicker a Singleton Component
  • Loading branch information
puneetlath authored Feb 21, 2022
2 parents 6ef2e51 + 93771c8 commit 58f0168
Show file tree
Hide file tree
Showing 10 changed files with 254 additions and 148 deletions.
5 changes: 4 additions & 1 deletion src/CONST/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,10 @@ const CONST = {

ADD_PAYMENT_MENU_POSITION_Y: 226,
ADD_PAYMENT_MENU_POSITION_X: 356,
EMOJI_PICKER_SIZE: 320,
EMOJI_PICKER_SIZE: {
WIDTH: 320,
HEIGHT: 400,
},
NON_NATIVE_EMOJI_PICKER_LIST_HEIGHT: 300,
EMOJI_PICKER_ITEM_HEIGHT: 40,
EMOJI_PICKER_HEADER_HEIGHT: 38,
Expand Down
51 changes: 51 additions & 0 deletions src/components/EmojiPicker/EmojiPickerButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from 'react';
import {Pressable} from 'react-native';
import PropTypes from 'prop-types';
import styles from '../../styles/styles';
import * as StyleUtils from '../../styles/StyleUtils';
import getButtonState from '../../libs/getButtonState';
import * as Expensicons from '../Icon/Expensicons';
import Tooltip from '../Tooltip';
import Icon from '../Icon';
import withLocalize, {withLocalizePropTypes} from '../withLocalize';
import * as EmojiPickerAction from '../../libs/actions/EmojiPickerAction';

const propTypes = {
/** Flag to disable the emoji picker button */
isDisabled: PropTypes.bool,

...withLocalizePropTypes,
};

const defaultProps = {
isDisabled: false,
};

const EmojiPickerButton = (props) => {
let emojiPopoverAnchor = null;
return (
<Pressable
ref={el => emojiPopoverAnchor = el}
style={({hovered, pressed}) => ([
styles.chatItemEmojiButton,
StyleUtils.getButtonBackgroundColorStyle(getButtonState(hovered, pressed)),
])}
disabled={props.isDisabled}
onPress={() => EmojiPickerAction.showEmojiPicker(props.onModalHide, props.onEmojiSelected, emojiPopoverAnchor)}
>
{({hovered, pressed}) => (
<Tooltip text={props.translate('reportActionCompose.emoji')}>
<Icon
src={Expensicons.Emoji}
fill={StyleUtils.getIconFillColor(getButtonState(hovered, pressed))}
/>
</Tooltip>
)}
</Pressable>
);
};

EmojiPickerButton.propTypes = propTypes;
EmojiPickerButton.defaultProps = defaultProps;
EmojiPickerButton.displayName = 'EmojiPickerButton';
export default withLocalize(EmojiPickerButton);
42 changes: 35 additions & 7 deletions src/components/EmojiPicker/EmojiPickerMenu/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import React, {Component} from 'react';
import {View, FlatList} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import PropTypes from 'prop-types';
import _ from 'underscore';
import lodashGet from 'lodash/get';
import CONST from '../../../CONST';
import ONYXKEYS from '../../../ONYXKEYS';
import styles from '../../../styles/styles';
import * as StyleUtils from '../../../styles/StyleUtils';
import themeColors from '../../../styles/themes/default';
Expand All @@ -15,6 +17,7 @@ import withWindowDimensions, {windowDimensionsPropTypes} from '../../withWindowD
import withLocalize, {withLocalizePropTypes} from '../../withLocalize';
import compose from '../../../libs/compose';
import getOperatingSystem from '../../../libs/getOperatingSystem';
import * as User from '../../../libs/actions/User';
import EmojiSkinToneList from '../EmojiSkinToneList';
import * as EmojiUtils from '../../../libs/EmojiUtils';

Expand All @@ -28,9 +31,6 @@ const propTypes = {
/** Stores user's preferred skin tone */
preferredSkinTone: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,

/** Function to sync the selected skin tone with parent, onyx and nvp */
updatePreferredSkinTone: PropTypes.func,

/** User's frequently used emojis */
frequentlyUsedEmojis: PropTypes.arrayOf(PropTypes.shape({
code: PropTypes.string.isRequired,
Expand All @@ -45,7 +45,6 @@ const propTypes = {

const defaultProps = {
forwardedRef: () => {},
updatePreferredSkinTone: undefined,
};

class EmojiPickerMenu extends Component {
Expand Down Expand Up @@ -86,6 +85,7 @@ class EmojiPickerMenu extends Component {
this.renderItem = this.renderItem.bind(this);
this.isMobileLandscape = this.isMobileLandscape.bind(this);
this.onSelectionChange = this.onSelectionChange.bind(this);
this.updatePreferredSkinTone = this.updatePreferredSkinTone.bind(this);

this.currentScrollOffset = 0;

Expand Down Expand Up @@ -145,7 +145,7 @@ class EmojiPickerMenu extends Component {
if (keyBoardEvent.key === 'Enter' && this.state.highlightedIndex !== -1) {
const item = this.state.filteredEmojis[this.state.highlightedIndex];
const emoji = lodashGet(item, ['types', this.props.preferredSkinTone], item.code);
this.props.onEmojiSelected(emoji, item);
this.addToFrequentAndSelectEmoji(emoji, item);
return;
}

Expand Down Expand Up @@ -187,6 +187,15 @@ class EmojiPickerMenu extends Component {
document.removeEventListener('mousemove', this.mouseMoveHandler);
}

/**
* @param {String} emoji
* @param {Object} emojiObject
*/
addToFrequentAndSelectEmoji(emoji, emojiObject) {
EmojiUtils.addToFrequentlyUsedEmojis(this.props.frequentlyUsedEmojis, emojiObject);
this.props.onEmojiSelected(emoji);
}

/**
* Focuses the search Input and has the text selected
*/
Expand Down Expand Up @@ -368,6 +377,17 @@ class EmojiPickerMenu extends Component {
return this.props.isSmallScreenWidth && this.props.windowWidth >= this.props.windowHeight;
}

/**
* @param {Number} skinTone
*/
updatePreferredSkinTone(skinTone) {
if (this.props.preferredSkinTone === skinTone) {
return;
}

User.setPreferredSkinTone(skinTone);
}

/**
* Given an emoji item object, render a component based on its type.
* Items with the code "SPACER" return nothing and are used to fill rows up to 8
Expand Down Expand Up @@ -398,7 +418,7 @@ class EmojiPickerMenu extends Component {

return (
<EmojiPickerMenuItem
onPress={emoji => this.props.onEmojiSelected(emoji, item)}
onPress={emoji => this.addToFrequentAndSelectEmoji(emoji, item)}
onHover={() => this.setState({highlightedIndex: index})}
emoji={emojiCode}
isHighlighted={index === this.state.highlightedIndex}
Expand Down Expand Up @@ -462,7 +482,7 @@ class EmojiPickerMenu extends Component {
/>
)}
<EmojiSkinToneList
updatePreferredSkinTone={this.props.updatePreferredSkinTone}
updatePreferredSkinTone={this.updatePreferredSkinTone}
preferredSkinTone={this.props.preferredSkinTone}
/>
</View>
Expand All @@ -476,6 +496,14 @@ EmojiPickerMenu.defaultProps = defaultProps;
export default compose(
withWindowDimensions,
withLocalize,
withOnyx({
preferredSkinTone: {
key: ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE,
},
frequentlyUsedEmojis: {
key: ONYXKEYS.FREQUENTLY_USED_EMOJIS,
},
}),
)(React.forwardRef((props, ref) => (
// eslint-disable-next-line react/jsx-props-no-spreading
<EmojiPickerMenu {...props} forwardedRef={ref} />
Expand Down
46 changes: 37 additions & 9 deletions src/components/EmojiPicker/EmojiPickerMenu/index.native.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import React, {Component} from 'react';
import {View, FlatList} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import PropTypes from 'prop-types';
import compose from '../../../libs/compose';
import withWindowDimensions, {windowDimensionsPropTypes} from '../../withWindowDimensions';
import CONST from '../../../CONST';
import ONYXKEYS from '../../../ONYXKEYS';
import styles from '../../../styles/styles';
import emojis from '../../../../assets/emojis';
import EmojiPickerMenuItem from '../EmojiPickerMenuItem';
import Text from '../../Text';
import withLocalize, {withLocalizePropTypes} from '../../withLocalize';
import EmojiSkinToneList from '../EmojiSkinToneList';
import * as EmojiUtils from '../../../libs/EmojiUtils';
import * as User from '../../../libs/actions/User';

const propTypes = {
/** Function to add the selected emoji to the main compose text input */
Expand All @@ -19,9 +22,6 @@ const propTypes = {
/** Stores user's preferred skin tone */
preferredSkinTone: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

/** Function to sync the selected skin tone with parent, onyx and nvp */
updatePreferredSkinTone: PropTypes.func,

/** User's frequently used emojis */
frequentlyUsedEmojis: PropTypes.arrayOf(PropTypes.shape({
code: PropTypes.string.isRequired,
Expand All @@ -36,6 +36,10 @@ const propTypes = {
...withLocalizePropTypes,
};

const defaultProps = {
preferredSkinTone: undefined,
};

class EmojiPickerMenu extends Component {
constructor(props) {
super(props);
Expand All @@ -56,8 +60,17 @@ class EmojiPickerMenu extends Component {

this.renderItem = this.renderItem.bind(this);
this.isMobileLandscape = this.isMobileLandscape.bind(this);
this.updatePreferredSkinTone = this.updatePreferredSkinTone.bind(this);
}

/**
* @param {String} emoji
* @param {Object} emojiObject
*/
addToFrequentAndSelectEmoji(emoji, emojiObject) {
EmojiUtils.addToFrequentlyUsedEmojis(this.props.frequentlyUsedEmojis, emojiObject);
this.props.onEmojiSelected(emoji);
}

/**
* Check if its a landscape mode of mobile device
Expand All @@ -68,6 +81,16 @@ class EmojiPickerMenu extends Component {
return this.props.windowWidth >= this.props.windowHeight;
}

/**
* @param {Number} skinTone
*/
updatePreferredSkinTone(skinTone) {
if (this.props.preferredSkinTone === skinTone) {
return;
}

User.setPreferredSkinTone(skinTone);
}

/**
* Given an emoji item object, render a component based on its type.
Expand Down Expand Up @@ -98,7 +121,7 @@ class EmojiPickerMenu extends Component {

return (
<EmojiPickerMenuItem
onPress={emoji => this.props.onEmojiSelected(emoji, item)}
onPress={emoji => this.addToFrequentAndSelectEmoji(emoji, item)}
emoji={emojiCode}
/>
);
Expand All @@ -120,7 +143,7 @@ class EmojiPickerMenu extends Component {
stickyHeaderIndices={this.unfilteredHeaderIndices}
/>
<EmojiSkinToneList
updatePreferredSkinTone={this.props.updatePreferredSkinTone}
updatePreferredSkinTone={this.updatePreferredSkinTone}
preferredSkinTone={this.props.preferredSkinTone}
/>
</View>
Expand All @@ -129,14 +152,19 @@ class EmojiPickerMenu extends Component {
}

EmojiPickerMenu.propTypes = propTypes;
EmojiPickerMenu.defaultProps = {
preferredSkinTone: undefined,
updatePreferredSkinTone: undefined,
};
EmojiPickerMenu.defaultProps = defaultProps;

export default compose(
withWindowDimensions,
withLocalize,
withOnyx({
preferredSkinTone: {
key: ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE,
},
frequentlyUsedEmojis: {
key: ONYXKEYS.FREQUENTLY_USED_EMOJIS,
},
}),
)(React.forwardRef((props, ref) => (
// eslint-disable-next-line react/jsx-props-no-spreading
<EmojiPickerMenu {...props} forwardedRef={ref} />
Expand Down
Loading

0 comments on commit 58f0168

Please sign in to comment.