Skip to content

Commit

Permalink
fix: open emoji picker on mobile
Browse files Browse the repository at this point in the history
  • Loading branch information
hannojg committed Feb 17, 2023
1 parent 2f61782 commit 85357b1
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 40 deletions.
57 changes: 41 additions & 16 deletions src/components/Reactions/AddReactionBubble.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
3 changes: 0 additions & 3 deletions src/components/Reactions/MiniQuickEmojiReactions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: () => {},
};

Expand All @@ -39,7 +37,6 @@ const MiniQuickEmojiReactions = (props) => {
}
},
ref.current,
props.onEmojiPickerWillShow,
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
<View style={{
gap: 12,
flexDirection: 'row',
Expand All @@ -40,13 +44,19 @@ const QuickEmojiReactions = props => (
<AddReactionBubble
iconSizeScale={1.2}
sizeScale={EMOJI_BUBBLE_SCALE}
onSelectEmoji={props.onEmojiSelected}
onPressOpenPicker={props.onPressOpenPicker}
onWillShowPicker={props.onWillShowPicker}
onSelectEmoji={props.onEmojiSelected}
/>
</View>
);

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,
};
28 changes: 28 additions & 0 deletions src/components/Reactions/QuickEmojiReactions/index.js
Original file line number Diff line number Diff line change
@@ -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 (
<BaseQuickEmojiReactions
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
onPressOpenPicker={onPressOpenPicker}
onWillShowPicker={props.closeContextMenu}
/>
);
};

QuickEmojiReactions.displayName = 'QuickEmojiReactions';
QuickEmojiReactions.propTypes = propTypes;
QuickEmojiReactions.defaultProps = baseQuickEmojiReactionsDefaultProps;
export default QuickEmojiReactions;
29 changes: 29 additions & 0 deletions src/components/Reactions/QuickEmojiReactions/index.native.js
Original file line number Diff line number Diff line change
@@ -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
<BaseQuickEmojiReactions {...props} onPressOpenPicker={onPressOpenPicker} />
);
};

QuickEmojiReactions.displayName = 'QuickEmojiReactions';
QuickEmojiReactions.propTypes = propTypes;
QuickEmojiReactions.defaultProps = baseQuickEmojiReactionsDefaultProps;
export default QuickEmojiReactions;
21 changes: 12 additions & 9 deletions src/pages/home/report/ContextMenu/ContextMenuActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<MiniQuickEmojiReactions
Expand All @@ -72,8 +75,8 @@ export default [

return (
<QuickEmojiReactions
key="QuickEmojiReactions"
onPressOpenPicker={close}
key="BaseQuickEmojiReactions"
closeContextMenu={closeContextMenu}
onEmojiSelected={onEmojiSelected}
/>
);
Expand Down

0 comments on commit 85357b1

Please sign in to comment.