diff --git a/src/components/EmojiSuggestions.js b/src/components/EmojiSuggestions/BaseEmojiSuggestions.js similarity index 65% rename from src/components/EmojiSuggestions.js rename to src/components/EmojiSuggestions/BaseEmojiSuggestions.js index 24f0203b7577..6491b2c0d085 100644 --- a/src/components/EmojiSuggestions.js +++ b/src/components/EmojiSuggestions/BaseEmojiSuggestions.js @@ -1,51 +1,16 @@ import React from 'react'; import {View, Pressable} from 'react-native'; -import PropTypes from 'prop-types'; import _ from 'underscore'; // We take FlatList from this package to properly handle the scrolling of EmojiSuggestions in chats since one scroll is nested inside another import {FlatList} from 'react-native-gesture-handler'; -import styles from '../styles/styles'; -import * as StyleUtils from '../styles/StyleUtils'; -import * as EmojiUtils from '../libs/EmojiUtils'; -import Text from './Text'; -import CONST from '../CONST'; -import getStyledTextArray from '../libs/GetStyledTextArray'; - -const propTypes = { - /** The index of the highlighted emoji */ - highlightedEmojiIndex: PropTypes.number, - - /** Array of suggested emoji */ - emojis: PropTypes.arrayOf(PropTypes.shape({ - /** The emoji code */ - code: PropTypes.string, - - /** The name of the emoji */ - name: PropTypes.string, - })).isRequired, - - /** Fired when the user selects an emoji */ - onSelect: PropTypes.func.isRequired, - - /** Emoji prefix that follows the colon */ - prefix: PropTypes.string.isRequired, - - /** 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, - - /** Show that we should include ReportRecipientLocalTime view height */ - shouldIncludeReportRecipientLocalTimeHeight: PropTypes.bool.isRequired, - - /** Stores user's preferred skin tone */ - preferredSkinToneIndex: PropTypes.number.isRequired, -}; - -const defaultProps = { - highlightedEmojiIndex: 0, -}; +import styles from '../../styles/styles'; +import * as StyleUtils from '../../styles/StyleUtils'; +import * as EmojiUtils from '../../libs/EmojiUtils'; +import Text from '../Text'; +import CONST from '../../CONST'; +import getStyledTextArray from '../../libs/GetStyledTextArray'; +import {propTypes, defaultProps} from './emojiSuggestionsPropTypes'; /** * @param {Number} numRows @@ -71,7 +36,7 @@ const measureHeightOfEmojiRows = (numRows, isEmojiPickerLarge) => { */ const keyExtractor = (item, index) => `${item.name}+${index}}`; -const EmojiSuggestions = (props) => { +const BaseEmojiSuggestions = (props) => { /** * Render a suggestion menu item component. * @param {Object} params.item @@ -91,6 +56,7 @@ const EmojiSuggestions = (props) => { )} onMouseDown={e => e.preventDefault()} onPress={() => props.onSelect(index)} + onLongPress={() => {}} > {EmojiUtils.getEmojiCodeWithSkinColor(item, props.preferredSkinToneIndex)} @@ -115,6 +81,7 @@ const EmojiSuggestions = (props) => { return ( { ); }; -EmojiSuggestions.propTypes = propTypes; -EmojiSuggestions.defaultProps = defaultProps; -EmojiSuggestions.displayName = 'EmojiSuggestions'; +BaseEmojiSuggestions.propTypes = propTypes; +BaseEmojiSuggestions.defaultProps = defaultProps; +BaseEmojiSuggestions.displayName = 'BaseEmojiSuggestions'; -export default EmojiSuggestions; +export default React.forwardRef((props, ref) => ( + // eslint-disable-next-line react/jsx-props-no-spreading + +)); diff --git a/src/components/EmojiSuggestions/emojiSuggestionsPropTypes.js b/src/components/EmojiSuggestions/emojiSuggestionsPropTypes.js new file mode 100644 index 000000000000..58344bc00d78 --- /dev/null +++ b/src/components/EmojiSuggestions/emojiSuggestionsPropTypes.js @@ -0,0 +1,41 @@ +import PropTypes from 'prop-types'; + +const propTypes = { + /** The index of the highlighted emoji */ + highlightedEmojiIndex: PropTypes.number, + + /** Array of suggested emoji */ + emojis: PropTypes.arrayOf(PropTypes.shape({ + /** The emoji code */ + code: PropTypes.string, + + /** The name of the emoji */ + name: PropTypes.string, + })).isRequired, + + /** Fired when the user selects an emoji */ + onSelect: PropTypes.func.isRequired, + + /** Emoji prefix that follows the colon */ + prefix: PropTypes.string.isRequired, + + /** 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, + + /** Show that we should include ReportRecipientLocalTime view height */ + shouldIncludeReportRecipientLocalTimeHeight: PropTypes.bool.isRequired, + + /** Stores user's preferred skin tone */ + preferredSkinToneIndex: PropTypes.number.isRequired, + + /** A ref to forward to the suggestion container */ + forwardedRef: PropTypes.object, +}; + +const defaultProps = { + highlightedEmojiIndex: 0, +}; + +export {propTypes, defaultProps}; diff --git a/src/components/EmojiSuggestions/index.js b/src/components/EmojiSuggestions/index.js new file mode 100644 index 000000000000..6a3b52f27dd9 --- /dev/null +++ b/src/components/EmojiSuggestions/index.js @@ -0,0 +1,39 @@ +import React from 'react'; +import BaseEmojiSuggestions from './BaseEmojiSuggestions'; +import * as DeviceCapabilities from '../../libs/DeviceCapabilities'; +import {propTypes, defaultProps} from './emojiSuggestionsPropTypes'; + +/** + * On the mobile-web platform, when long-pressing on emoji suggestions, + * we need to prevent focus shifting to avoid blurring the main input which makes the emoji picker close and adds the emoji to the composer. + * The desired pattern for all platforms is to do nothing on long-press. + * On the native platform, tapping on emoji suggestions will not blur the main input. + */ + +const EmojiSuggestions = (props) => { + const containerRef = React.useRef(null); + React.useEffect(() => { + const container = containerRef.current; + if (!container) { + return; + } + container.onpointerdown = (e) => { + if (DeviceCapabilities.hasHoverSupport()) { + return; + } + e.preventDefault(); + }; + return () => container.onpointerdown = null; + }, []); + + return ( + // eslint-disable-next-line react/jsx-props-no-spreading + + ); +}; + +EmojiSuggestions.propTypes = propTypes; +EmojiSuggestions.defaultProps = defaultProps; +EmojiSuggestions.displayName = 'EmojiSuggestions'; + +export default EmojiSuggestions; diff --git a/src/components/EmojiSuggestions/index.native.js b/src/components/EmojiSuggestions/index.native.js new file mode 100644 index 000000000000..30cc58b8c675 --- /dev/null +++ b/src/components/EmojiSuggestions/index.native.js @@ -0,0 +1,14 @@ +import React from 'react'; +import BaseEmojiSuggestions from './BaseEmojiSuggestions'; +import {propTypes, defaultProps} from './emojiSuggestionsPropTypes'; + +const EmojiSuggestions = props => ( + // eslint-disable-next-line react/jsx-props-no-spreading + +); + +EmojiSuggestions.propTypes = propTypes; +EmojiSuggestions.defaultProps = defaultProps; +EmojiSuggestions.displayName = 'EmojiSuggestions'; + +export default EmojiSuggestions;