diff --git a/src/components/Reactions/AddReactionBubble.js b/src/components/Reactions/AddReactionBubble.js index b22c69cf1ad6..69595517c606 100644 --- a/src/components/Reactions/AddReactionBubble.js +++ b/src/components/Reactions/AddReactionBubble.js @@ -11,39 +11,64 @@ import Text from '../Text'; import getButtonState from '../../libs/getButtonState'; import * as EmojiPickerAction from '../../libs/actions/EmojiPickerAction'; import emojis from '../../../assets/emojis'; +import ReportActionComposeFocusManager from '../../libs/ReportActionComposeFocusManager'; const propTypes = { sizeScale: PropTypes.number, iconSizeScale: PropTypes.number, + + /** + * Called when the user presses on the icon button. + * Will have a function as parameter which you can call + * to open the picker. + */ onPressOpenPicker: PropTypes.func, + + /** + * Will get called the moment before the picker opens. + */ + onWillShowPicker: PropTypes.func, + + /** + * Called when the user selects an emoji. + */ onSelectEmoji: PropTypes.func.isRequired, }; const defaultProps = { sizeScale: 1, iconSizeScale: 1, - onPressOpenPicker: () => { - - }, + onWillShowPicker: () => {}, + onPressOpenPicker: undefined, }; const AddReactionBubble = (props) => { const ref = React.createRef(); const onPress = () => { - EmojiPickerAction.showEmojiPicker( - () => {}, - (emojiCode) => { - const emoji = _.find(emojis, e => e.code === emojiCode); - if (emoji != null) { - props.onSelectEmoji(emoji); - } - }, - ref.current, - () => { - props.onPressOpenPicker(); - }, - ); + const openPicker = () => { + EmojiPickerAction.showEmojiPicker( + () => {}, + (emojiCode) => { + const emoji = _.find(emojis, e => e.code === emojiCode); + if (emoji != null) { + props.onSelectEmoji(emoji); + } + }, + + // The ref can become null, if e.g. the AddReactionBubble component + // gets removed before showing the picker. In this case we want to + // default fallback to anchor to the composer. + ref.current || ReportActionComposeFocusManager.composerRef.current, + props.onWillShowPicker, + ); + }; + + if (props.onPressOpenPicker) { + props.onPressOpenPicker(openPicker); + } else { + openPicker(); + } }; return ( diff --git a/src/components/Reactions/MiniQuickEmojiReactions.js b/src/components/Reactions/MiniQuickEmojiReactions.js index bfb2046d152b..43fe5861f420 100644 --- a/src/components/Reactions/MiniQuickEmojiReactions.js +++ b/src/components/Reactions/MiniQuickEmojiReactions.js @@ -16,11 +16,9 @@ import emojis from '../../../assets/emojis'; const propTypes = { onEmojiSelected: PropTypes.func.isRequired, onPressOpenPicker: PropTypes.func, - onEmojiPickerWillShow: PropTypes.func, }; const defaultProps = { - onEmojiPickerWillShow: () => {}, onPressOpenPicker: () => {}, }; @@ -39,7 +37,6 @@ const MiniQuickEmojiReactions = (props) => { } }, ref.current, - props.onEmojiPickerWillShow, ); }; diff --git a/src/components/Reactions/QuickEmojiReactions.js b/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js similarity index 56% rename from src/components/Reactions/QuickEmojiReactions.js rename to src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js index 112af9bf0b61..b8e415b5adb4 100644 --- a/src/components/Reactions/QuickEmojiReactions.js +++ b/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js @@ -2,22 +2,26 @@ import React from 'react'; import {View} from 'react-native'; import _ from 'underscore'; import PropTypes from 'prop-types'; -import EmojiReactionBubble from './EmojiReactionBubble'; -import AddReactionBubble from './AddReactionBubble'; -import CONST from '../../CONST'; +import EmojiReactionBubble from '../EmojiReactionBubble'; +import AddReactionBubble from '../AddReactionBubble'; +import CONST from '../../../CONST'; const EMOJI_BUBBLE_SCALE = 1.5; -const propTypes = { +const baseQuickEmojiReactionsPropTypes = { onEmojiSelected: PropTypes.func.isRequired, + onWillShowPicker: PropTypes.func, onPressOpenPicker: PropTypes.func, }; -const defaultProps = { - onPressOpenPicker: () => {}, +const baseQuickEmojiReactionsDefaultProps = { + onWillShowPicker: undefined, + onPressOpenPicker: undefined, }; -const QuickEmojiReactions = props => ( +const BaseQuickEmojiReactions = props => ( + + // TODO: move this to styles file ( ); -QuickEmojiReactions.displayName = 'QuickEmojiReactions'; -QuickEmojiReactions.propTypes = propTypes; -QuickEmojiReactions.defaultProps = defaultProps; -export default QuickEmojiReactions; +BaseQuickEmojiReactions.displayName = 'BaseQuickEmojiReactions'; +BaseQuickEmojiReactions.propTypes = baseQuickEmojiReactionsPropTypes; +BaseQuickEmojiReactions.defaultProps = baseQuickEmojiReactionsDefaultProps; +export default BaseQuickEmojiReactions; + +export { + baseQuickEmojiReactionsPropTypes, + baseQuickEmojiReactionsDefaultProps, +}; diff --git a/src/components/Reactions/QuickEmojiReactions/index.js b/src/components/Reactions/QuickEmojiReactions/index.js new file mode 100644 index 000000000000..5ba1dee5f171 --- /dev/null +++ b/src/components/Reactions/QuickEmojiReactions/index.js @@ -0,0 +1,28 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import BaseQuickEmojiReactions, {baseQuickEmojiReactionsDefaultProps, baseQuickEmojiReactionsPropTypes} from './BaseQuickEmojiReactions'; + +const propTypes = { + ...baseQuickEmojiReactionsPropTypes, + closeContextMenu: PropTypes.func.isRequired, +}; + +const QuickEmojiReactions = (props) => { + const onPressOpenPicker = (openPicker) => { + openPicker(); + }; + + return ( + + ); +}; + +QuickEmojiReactions.displayName = 'QuickEmojiReactions'; +QuickEmojiReactions.propTypes = propTypes; +QuickEmojiReactions.defaultProps = baseQuickEmojiReactionsDefaultProps; +export default QuickEmojiReactions; diff --git a/src/components/Reactions/QuickEmojiReactions/index.native.js b/src/components/Reactions/QuickEmojiReactions/index.native.js new file mode 100644 index 000000000000..b3c3bed6800f --- /dev/null +++ b/src/components/Reactions/QuickEmojiReactions/index.native.js @@ -0,0 +1,29 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import BaseQuickEmojiReactions, {baseQuickEmojiReactionsDefaultProps, baseQuickEmojiReactionsPropTypes} from './BaseQuickEmojiReactions'; + +const propTypes = { + ...baseQuickEmojiReactionsPropTypes, + closeContextMenu: PropTypes.func.isRequired, +}; + +const QuickEmojiReactions = (props) => { + const onPressOpenPicker = (openPicker) => { + // We first need to close the menu as it's a popover. + // The picker is a popover as well and on mobile there can only + // be one active popover at a time. + props.closeContextMenu(() => { + requestAnimationFrame(openPicker); + }); + }; + + return ( + // eslint-disable-next-line react/jsx-props-no-spreading + + ); +}; + +QuickEmojiReactions.displayName = 'QuickEmojiReactions'; +QuickEmojiReactions.propTypes = propTypes; +QuickEmojiReactions.defaultProps = baseQuickEmojiReactionsDefaultProps; +export default QuickEmojiReactions; diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.js b/src/pages/home/report/ContextMenu/ContextMenuActions.js index 2aa3dc058f5a..e32e017c899a 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.js +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.js @@ -44,22 +44,25 @@ export default [ renderContent: (closePopover, { reportID, reportAction, close: closeManually, keepOpen, }) => { - const close = () => { - if (!closePopover) { + const isMini = !closePopover; + + const closeContextMenu = (onHideCallback) => { + if (isMini) { closeManually(); - return; + if (onHideCallback) { + onHideCallback(); + } + } else { + hideContextMenu(false, onHideCallback); } - hideContextMenu(false); }; const onEmojiSelected = (emoji) => { // TODO: we need to add the preferred skin tone here as well somehow Report.toggleReaction(reportID, reportAction, emoji); - close(); + closeContextMenu(); }; - const isMini = !closePopover; - if (isMini) { return ( );