diff --git a/src/components/Reactions/ReportActionItemEmojiReactions.js b/src/components/Reactions/ReportActionItemEmojiReactions.js index 7102f9982f52..806e87b4301d 100644 --- a/src/components/Reactions/ReportActionItemEmojiReactions.js +++ b/src/components/Reactions/ReportActionItemEmojiReactions.js @@ -91,7 +91,7 @@ function ReportActionItemEmojiReactions(props) { }; const onReactionListOpen = (event) => { - reactionListRef.current.showReactionList(event, popoverReactionListAnchor.current, reaction); + reactionListRef.current.showReactionList(event, popoverReactionListAnchor.current, reactionEmojiName, props.reportActionID); }; return { diff --git a/src/pages/home/report/ReactionList/PopoverReactionList.js b/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.js similarity index 54% rename from src/pages/home/report/ReactionList/PopoverReactionList.js rename to src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.js index d8c3631d3c33..9303d7a5bc39 100644 --- a/src/pages/home/report/ReactionList/PopoverReactionList.js +++ b/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.js @@ -2,17 +2,35 @@ import React from 'react'; import {Dimensions} from 'react-native'; import lodashGet from 'lodash/get'; import _ from 'underscore'; -import * as Report from '../../../../libs/actions/Report'; -import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize'; -import PopoverWithMeasuredContent from '../../../../components/PopoverWithMeasuredContent'; -import BaseReactionList from './BaseReactionList'; -import compose from '../../../../libs/compose'; -import withCurrentUserPersonalDetails from '../../../../components/withCurrentUserPersonalDetails'; -import * as PersonalDetailsUtils from '../../../../libs/PersonalDetailsUtils'; -import * as EmojiUtils from '../../../../libs/EmojiUtils'; -import CONST from '../../../../CONST'; - -class PopoverReactionList extends React.Component { +import {withOnyx} from 'react-native-onyx'; +import PropTypes from 'prop-types'; +import * as Report from '../../../../../libs/actions/Report'; +import withLocalize, {withLocalizePropTypes} from '../../../../../components/withLocalize'; +import PopoverWithMeasuredContent from '../../../../../components/PopoverWithMeasuredContent'; +import BaseReactionList from '../BaseReactionList'; +import compose from '../../../../../libs/compose'; +import withCurrentUserPersonalDetails from '../../../../../components/withCurrentUserPersonalDetails'; +import * as PersonalDetailsUtils from '../../../../../libs/PersonalDetailsUtils'; +import * as EmojiUtils from '../../../../../libs/EmojiUtils'; +import CONST from '../../../../../CONST'; +import ONYXKEYS from '../../../../../ONYXKEYS'; +import EmojiReactionsPropTypes from '../../../../../components/Reactions/EmojiReactionsPropTypes'; + +const propTypes = { + reportActionID: PropTypes.string, + emojiName: PropTypes.string, + emojiReactions: EmojiReactionsPropTypes, + + ...withLocalizePropTypes, +}; + +const defaultProps = { + reportActionID: '', + emojiName: '', + emojiReactions: {}, +}; + +class BasePopoverReactionList extends React.Component { constructor(props) { super(props); @@ -28,14 +46,8 @@ class PopoverReactionList extends React.Component { horizontal: 0, vertical: 0, }, - users: [], - emojiCodes: [], - emojiName: '', - emojiCount: 0, - hasUserReacted: false, }; - this.onPopoverHideActionCallback = () => {}; this.reactionListAnchor = React.createRef(); this.showReactionList = this.showReactionList.bind(this); this.hideReactionList = this.hideReactionList.bind(this); @@ -43,7 +55,6 @@ class PopoverReactionList extends React.Component { this.getReactionListMeasuredLocation = this.getReactionListMeasuredLocation.bind(this); this.getReactionInformation = this.getReactionInformation.bind(this); this.dimensionsEventListener = null; - this.contentRef = React.createRef(); } componentDidMount() { @@ -51,17 +62,38 @@ class PopoverReactionList extends React.Component { } shouldComponentUpdate(nextProps, nextState) { + if (!this.state.isPopoverVisible && !nextState.isPopoverVisible) { + return false; + } + const previousLocale = lodashGet(this.props, 'preferredLocale', CONST.LOCALES.DEFAULT); const nextLocale = lodashGet(nextProps, 'preferredLocale', CONST.LOCALES.DEFAULT); + const prevReaction = lodashGet(this.props.emojiReactions, this.props.emojiName); + const nextReaction = lodashGet(nextProps.emojiReactions, nextProps.emojiName); return ( - this.state.isPopoverVisible !== nextState.isPopoverVisible || - this.state.popoverAnchorPosition !== nextState.popoverAnchorPosition || - previousLocale !== nextLocale || - (this.state.isPopoverVisible && (this.state.reportActionID !== nextState.reportActionID || this.state.emojiName !== nextState.emojiName)) + this.props.reportActionID !== nextProps.reportActionID || + this.props.emojiName !== nextProps.emojiName || + !_.isEqual(prevReaction, nextReaction) || + !_.isEqual(this.state, nextState) || + previousLocale !== nextLocale ); } + componentDidUpdate() { + if (!this.state.isPopoverVisible) { + return; + } + + // Hide the list when all reactions are removed + const isEmptyList = !_.some(lodashGet(this.props.emojiReactions, [this.props.emojiName, 'users'])); + if (!isEmptyList) { + return; + } + + this.hideReactionList(); + } + componentWillUnmount() { if (!this.dimensionsEventListener) { return; @@ -70,7 +102,7 @@ class PopoverReactionList extends React.Component { } /** - * Get the PopoverReactionList anchor position + * Get the BasePopoverReactionList anchor position * We calculate the achor coordinates from measureInWindow async method * * @returns {Promise} @@ -94,20 +126,23 @@ class PopoverReactionList extends React.Component { getReactionInformation(selectedReaction) { if (!selectedReaction) { return { - users: [], - emojiCodes: [], emojiName: '', emojiCount: 0, + emojiCodes: [], + hasUserReacted: false, + users: [], }; } const reactionUsers = _.pick(selectedReaction.users, _.identity); const emojiCount = _.map(reactionUsers, (user) => user).length; const userAccountIDs = _.map(reactionUsers, (user, accountID) => Number(accountID)); - const emoji = EmojiUtils.findEmojiByName(selectedReaction.emojiName); + const emojiName = selectedReaction.emojiName; + const emoji = EmojiUtils.findEmojiByName(emojiName); const emojiCodes = EmojiUtils.getUniqueEmojiCodes(emoji, selectedReaction.users); const hasUserReacted = Report.hasAccountIDEmojiReacted(this.props.currentUserPersonalDetails.accountID, reactionUsers); const users = PersonalDetailsUtils.getPersonalDetailsByIDs(userAccountIDs, this.props.currentUserPersonalDetails.accountID, true); return { + emojiName, emojiCount, emojiCodes, hasUserReacted, @@ -120,15 +155,10 @@ class PopoverReactionList extends React.Component { * * @param {Object} [event] - A press event. * @param {Element} reactionListAnchor - reactionListAnchor - * @param {Object} emojiReaction - * @param {String} emojiName - Name of emoji */ - showReactionList(event, reactionListAnchor, emojiReaction) { + showReactionList(event, reactionListAnchor) { const nativeEvent = event.nativeEvent || {}; - this.reactionListAnchor = reactionListAnchor; - const selectedReaction = emojiReaction; - const {emojiName} = emojiReaction; - const {emojiCount, emojiCodes, hasUserReacted, users} = this.getReactionInformation(selectedReaction); + this.reactionListAnchor.current = reactionListAnchor; this.getReactionListMeasuredLocation().then(({x, y}) => { this.setState({ cursorRelativePosition: { @@ -139,18 +169,13 @@ class PopoverReactionList extends React.Component { horizontal: nativeEvent.pageX, vertical: nativeEvent.pageY, }, - users, - emojiName, - emojiCodes, - emojiCount, isPopoverVisible: true, - hasUserReacted, }); }); } /** - * This gets called on Dimensions change to find the anchor coordinates for the action PopoverReactionList. + * This gets called on Dimensions change to find the anchor coordinates for the action BasePopoverReactionList. */ measureReactionListPosition() { if (!this.state.isPopoverVisible) { @@ -179,36 +204,46 @@ class PopoverReactionList extends React.Component { } render() { + const selectedReaction = this.state.isPopoverVisible ? lodashGet(this.props.emojiReactions, [this.props.emojiName]) : null; + const {emojiName, emojiCount, emojiCodes, hasUserReacted, users} = this.getReactionInformation(selectedReaction); + return ( - <> - + - - - + hasUserReacted={hasUserReacted} + /> + ); } } -PopoverReactionList.propTypes = withLocalizePropTypes; - -export default compose(withLocalize, withCurrentUserPersonalDetails)(PopoverReactionList); +BasePopoverReactionList.propTypes = propTypes; +BasePopoverReactionList.defaultProps = defaultProps; + +export default compose( + withLocalize, + withCurrentUserPersonalDetails, + withOnyx({ + emojiReactions: { + key: ({reportActionID}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`, + }, + }), +)(BasePopoverReactionList); diff --git a/src/pages/home/report/ReactionList/PopoverReactionList/index.js b/src/pages/home/report/ReactionList/PopoverReactionList/index.js new file mode 100644 index 000000000000..c39eeddb7fd0 --- /dev/null +++ b/src/pages/home/report/ReactionList/PopoverReactionList/index.js @@ -0,0 +1,55 @@ +import React, {forwardRef, useImperativeHandle, useRef, useState} from 'react'; +import PropTypes from 'prop-types'; +import BasePopoverReactionList from './BasePopoverReactionList'; + +const propTypes = { + innerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), +}; + +const defaultProps = { + innerRef: () => {}, +}; + +function PopoverReactionList(props) { + const innerReactionListRef = useRef(); + const [reactionListReportActionID, setReactionListReportActionID] = useState(''); + const [reactionListEmojiName, setReactionListEmojiName] = useState(''); + + /** + * Show the ReactionList modal popover. + * + * @param {Object} [event] - A press event. + * @param {Element} reactionListAnchor - reactionListAnchor + * @param {String} emojiName - Name of emoji + * @param {String} reportActionID - ID of the report action + */ + const showReactionList = (event, reactionListAnchor, emojiName, reportActionID) => { + setReactionListReportActionID(reportActionID); + setReactionListEmojiName(emojiName); + innerReactionListRef.current.showReactionList(event, reactionListAnchor); + }; + + useImperativeHandle(props.innerRef, () => ({showReactionList}), []); + + return ( + + ); +} + +PopoverReactionList.propTypes = propTypes; +PopoverReactionList.defaultProps = defaultProps; +PopoverReactionList.displayName = 'PopoverReactionList'; + +export default React.memo( + forwardRef((props, ref) => ( + + )), +);