Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 20 additions & 8 deletions src/components/EmojiPicker/EmojiPicker.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import PropTypes from 'prop-types';
import React, {forwardRef, useEffect, useImperativeHandle, useRef, useState} from 'react';
import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react';
import {Dimensions} from 'react-native';
import _ from 'underscore';
import PopoverWithMeasuredContent from '@components/PopoverWithMeasuredContent';
Expand Down Expand Up @@ -30,26 +30,37 @@ const EmojiPicker = forwardRef((props, ref) => {
});
const [emojiPopoverAnchorOrigin, setEmojiPopoverAnchorOrigin] = useState(DEFAULT_ANCHOR_ORIGIN);
const [activeID, setActiveID] = useState();
const emojiPopoverAnchor = useRef(null);
const emojiPopoverAnchorRef = useRef(null);
const onModalHide = useRef(() => {});
const onEmojiSelected = useRef(() => {});
const emojiSearchInput = useRef();
const {isSmallScreenWidth, windowHeight} = useWindowDimensions();

/**
* Get the popover anchor ref
*
* emojiPopoverAnchorRef contains either null or the ref object of the anchor element.
* { current: { current: anchorElement } }
*
* Don't directly get the ref from emojiPopoverAnchorRef, instead use getEmojiPopoverAnchor()
*/
const getEmojiPopoverAnchor = useCallback(() => emojiPopoverAnchorRef.current || emojiPopoverAnchorRef, []);

/**
* Show the emoji picker menu.
*
* @param {Function} [onModalHideValue=() => {}] - Run a callback when Modal hides.
* @param {Function} [onEmojiSelectedValue=() => {}] - Run a callback when Emoji selected.
* @param {Element} emojiPopoverAnchorValue - Element to which Popover is anchored
* @param {React.MutableRefObject} emojiPopoverAnchorValue - Element to which Popover is anchored
* @param {Object} [anchorOrigin=DEFAULT_ANCHOR_ORIGIN] - Anchor origin for Popover
* @param {Function} [onWillShow=() => {}] - Run a callback when Popover will show
* @param {String} id - Unique id for EmojiPicker
*/
const showEmojiPicker = (onModalHideValue, onEmojiSelectedValue, emojiPopoverAnchorValue, anchorOrigin, onWillShow = () => {}, id) => {
onModalHide.current = onModalHideValue;
onEmojiSelected.current = onEmojiSelectedValue;
emojiPopoverAnchor.current = emojiPopoverAnchorValue;
emojiPopoverAnchorRef.current = emojiPopoverAnchorValue;
const emojiPopoverAnchor = getEmojiPopoverAnchor();
if (emojiPopoverAnchor.current && emojiPopoverAnchor.current.blur) {
// Drop focus to avoid blue focus ring.
emojiPopoverAnchor.current.blur();
Expand All @@ -75,7 +86,7 @@ const EmojiPicker = forwardRef((props, ref) => {
if (isNavigating) {
onModalHide.current = () => {};
}
emojiPopoverAnchor.current = null;
emojiPopoverAnchorRef.current = null;
setIsEmojiPickerVisible(false);
};

Expand Down Expand Up @@ -118,12 +129,13 @@ const EmojiPicker = forwardRef((props, ref) => {

const clearActive = () => setActiveID(null);

const resetEmojiPopoverAnchor = () => (emojiPopoverAnchor.current = null);
const resetEmojiPopoverAnchor = () => (emojiPopoverAnchorRef.current = null);

useImperativeHandle(ref, () => ({showEmojiPicker, isActive, clearActive, hideEmojiPicker, isEmojiPickerVisible, resetEmojiPopoverAnchor}));

useEffect(() => {
const emojiPopoverDimensionListener = Dimensions.addEventListener('change', () => {
const emojiPopoverAnchor = getEmojiPopoverAnchor();
if (!emojiPopoverAnchor.current) {
// In small screen width, the window size change might be due to keyboard open/hide, we should avoid hide EmojiPicker in those cases
if (isEmojiPickerVisible && !isSmallScreenWidth) {
Expand All @@ -141,7 +153,7 @@ const EmojiPicker = forwardRef((props, ref) => {
}
emojiPopoverDimensionListener.remove();
};
}, [isEmojiPickerVisible, isSmallScreenWidth, emojiPopoverAnchorOrigin]);
}, [isEmojiPickerVisible, isSmallScreenWidth, emojiPopoverAnchorOrigin, getEmojiPopoverAnchor]);

// There is no way to disable animations, and they are really laggy, because there are so many
// emojis. The best alternative is to set it to 1ms so it just "pops" in and out
Expand All @@ -159,7 +171,7 @@ const EmojiPicker = forwardRef((props, ref) => {
vertical: emojiPopoverAnchorPosition.vertical,
horizontal: emojiPopoverAnchorPosition.horizontal,
}}
anchorRef={emojiPopoverAnchor}
anchorRef={getEmojiPopoverAnchor()}
withoutOverlay
popoverDimensions={{
width: CONST.EMOJI_PICKER_SIZE.WIDTH,
Expand Down
2 changes: 1 addition & 1 deletion src/components/EmojiPicker/EmojiPickerButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ function EmojiPickerButton(props) {
return;
}
if (!EmojiPickerAction.emojiPickerRef.current.isEmojiPickerVisible) {
EmojiPickerAction.showEmojiPicker(props.onModalHide, props.onEmojiSelected, emojiPopoverAnchor.current, undefined, () => {}, props.emojiPickerID);
EmojiPickerAction.showEmojiPicker(props.onModalHide, props.onEmojiSelected, emojiPopoverAnchor, undefined, () => {}, props.emojiPickerID);
} else {
EmojiPickerAction.emojiPickerRef.current.hideEmojiPicker();
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/EmojiPicker/EmojiPickerButtonDropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ function EmojiPickerButtonDropdown(props) {
return;
}

EmojiPickerAction.showEmojiPicker(props.onModalHide, (emoji) => props.onInputChange(emoji), emojiPopoverAnchor.current, {
EmojiPickerAction.showEmojiPicker(props.onModalHide, (emoji) => props.onInputChange(emoji), emojiPopoverAnchor, {
horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT,
vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP,
shiftVertical: 4,
Expand Down
2 changes: 1 addition & 1 deletion src/components/Reactions/AddReactionBubble.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ function AddReactionBubble(props) {
(emojiCode, emojiObject) => {
props.onSelectEmoji(emojiObject);
},
refParam || ref.current,
refParam || ref,
anchorOrigin,
props.onWillShowPicker,
props.reportAction.reportActionID,
Expand Down
2 changes: 1 addition & 1 deletion src/components/Reactions/MiniQuickEmojiReactions.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ function MiniQuickEmojiReactions(props) {
(emojiCode, emojiObject) => {
props.onEmojiSelected(emojiObject, props.emojiReactions);
},
ref.current,
ref,
undefined,
() => {},
props.reportAction.reportActionID,
Expand Down
2 changes: 1 addition & 1 deletion src/components/Reactions/QuickEmojiReactions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const propTypes = {

function QuickEmojiReactions(props) {
const onPressOpenPicker = (openPicker) => {
openPicker(contextMenuRef.current.contentRef.current, {
openPicker(contextMenuRef.current.contentRef, {
horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT,
vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ function QuickEmojiReactions(props) {
// As the menu which includes the button to open the emoji picker
// gets closed, before the picker actually opens, we pass the composer
// ref as anchor for the emoji picker popover.
openPicker(ReportActionComposeFocusManager.composerRef.current);
openPicker(ReportActionComposeFocusManager.composerRef);
});
};

Expand Down
9 changes: 8 additions & 1 deletion src/libs/actions/EmojiPickerAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,14 @@ type AnchorOrigin = {

// TODO: Move this type to src/components/EmojiPicker/EmojiPicker.js once it is converted to TS
type EmojiPickerRef = {
showEmojiPicker: (onModalHideValue?: () => void, onEmojiSelectedValue?: () => void, emojiPopoverAnchor?: View, anchorOrigin?: AnchorOrigin, onWillShow?: () => void, id?: string) => void;
showEmojiPicker: (
onModalHideValue?: () => void,
onEmojiSelectedValue?: () => void,
emojiPopoverAnchor?: React.MutableRefObject<View | HTMLElement | null>,
anchorOrigin?: AnchorOrigin,
onWillShow?: () => void,
id?: string,
) => void;
isActive: (id: string) => boolean;
clearActive: () => void;
hideEmojiPicker: (isNavigating: boolean) => void;
Expand Down