From 46f1cca2c69e09e4a9cbf1cfa0a5fa1a82efead1 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Tue, 25 Apr 2023 14:56:52 -0600 Subject: [PATCH 01/68] Create a separate collection for reactions --- src/ONYXKEYS.js | 1 + src/libs/actions/Report.js | 82 ++++++++++++++++++++++- src/pages/home/report/ReportActionItem.js | 14 +++- 3 files changed, 93 insertions(+), 4 deletions(-) diff --git a/src/ONYXKEYS.js b/src/ONYXKEYS.js index c9ed8ef43dab..ae6122605747 100755 --- a/src/ONYXKEYS.js +++ b/src/ONYXKEYS.js @@ -108,6 +108,7 @@ export default { REPORT: 'report_', REPORT_ACTIONS: 'reportActions_', REPORT_ACTIONS_DRAFTS: 'reportActionsDrafts_', + REPORT_ACTIONS_REACTIONS: 'reportActionsReactions_', REPORT_DRAFT_COMMENT: 'reportDraftComment_', REPORT_DRAFT_COMMENT_NUMBER_OF_LINES: 'reportDraftCommentNumberOfLines_', REPORT_IS_COMPOSER_FULL_SIZE: 'reportIsComposerFullSize_', diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 0890b6e5720e..18f5d8402ced 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1418,24 +1418,98 @@ function removeEmojiReaction(reportID, originalReportAction, emoji) { API.write('RemoveEmojiReaction', parameters, {optimisticData}); } +/** + * Adds a reaction to the report action. + * @param {String} reportID + * @param {String} reportActionID + * @param {{ name: string, code: string, types: string[] }} emoji + * @param {number} [skinTone] Optional. + */ +function addEmojiReaction2(reportID, reportActionID, emoji, skinTone = preferredSkinTone) { + const optimisticData = [ + { + onyxMethod: CONST.ONYX.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportID}${reportActionID}`, + value: { + [emoji.name]: { + emoji: emoji.name, + users: { + [currentUserAccountID]: {accountID: currentUserAccountID, skinTone}, + }, + }, + }, + }, + ]; + const parameters = { + reportID, + skinTone, + emojiCode: emoji.name, + reportActionID, + }; + API.write('AddEmojiReaction2', parameters, {optimisticData}); +} + +/** + * Removes a reaction to the report action. + * @param {String} reportID + * @param {String} reportActionID + * @param {{ name: string, code: string, types: string[] }} emoji + * @param {Object} existingReactions + */ +function removeEmojiReaction2(reportID, reportActionID, emoji, existingReactions) { + const reactionObject = existingReactions[emoji.name]; + if (!reactionObject) { + return; + } + + // Make a copy of the original reaction object so that we don't update anything in existingReactions which could be unexpected + let updatedReactionObject = {...reactionObject}; + + // Remove the current user from the list of users who have used this emoji + updatedReactionObject.users = _.omit(reactionObject.users, currentUserAccountID); + + // If there are no more users, then the entire reactions needs to be set to null so that it can be removed from Onyx + if (!_.size(updatedReactionObject.users)) { + updatedReactionObject = null; + } + + const optimisticData = [ + { + onyxMethod: CONST.ONYX.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportID}${reportActionID}`, + value: { + [emoji.name]: updatedReactionObject, + }, + }, + ]; + + const parameters = { + reportID, + reportActionID, + emojiCode: emoji.name, + }; + API.write('RemoveEmojiReaction2', parameters, {optimisticData}); +} + /** * Calls either addEmojiReaction or removeEmojiReaction depending on if the current user has reacted to the report action. * @param {String} reportID * @param {Object} reportAction * @param {Object} emoji * @param {number} paramSkinTone + * @param {Object} existingReactions * @returns {Promise} */ -function toggleEmojiReaction(reportID, reportAction, emoji, paramSkinTone = preferredSkinTone) { +function toggleEmojiReaction(reportID, reportAction, emoji, paramSkinTone = preferredSkinTone, existingReactions) { const message = reportAction.message[0]; const reactionObject = message.reactions && _.find(message.reactions, reaction => reaction.emoji === emoji.name); const skinTone = emoji.types === undefined ? null : paramSkinTone; // only use skin tone if emoji supports it if (reactionObject) { if (hasAccountIDReacted(currentUserAccountID, reactionObject.users, skinTone)) { - return removeEmojiReaction(reportID, reportAction, emoji, skinTone); + return removeEmojiReaction2(reportID, reportAction.reportActionID, emoji, skinTone, existingReactions); } } - return addEmojiReaction(reportID, reportAction, emoji, skinTone); + return addEmojiReaction2(reportID, reportAction.reportActionID, emoji, skinTone); } /** @@ -1495,7 +1569,9 @@ export { subscribeToNewActionEvent, showReportActionNotification, addEmojiReaction, + addEmojiReaction2, removeEmojiReaction, + removeEmojiReaction2, toggleEmojiReaction, hasAccountIDReacted, getCurrentUserAccountID, diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index a47ff6d4de4e..c3f91e17216e 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -82,6 +82,14 @@ const propTypes = { /** All of the personalDetails */ personalDetails: PropTypes.objectOf(personalDetailsPropType), + reactions: PropTypes.objectOf(PropTypes.shape({ + emoji: PropTypes.string, + users: PropTypes.objectOf(PropTypes.shape({ + accountID: PropTypes.number, + skinTone: PropTypes.string, + })), + })), + ...windowDimensionsPropTypes, }; @@ -90,6 +98,7 @@ const defaultProps = { hasOutstandingIOU: false, preferredSkinTone: CONST.EMOJI_DEFAULT_SKIN_TONE, personalDetails: {}, + reactions: {}, }; class ReportActionItem extends Component { @@ -163,7 +172,7 @@ class ReportActionItem extends Component { } toggleReaction(emoji) { - Report.toggleEmojiReaction(this.props.report.reportID, this.props.action, emoji); + Report.toggleEmojiReaction(this.props.report.reportID, this.props.action, emoji, this.props.reactions); } /** @@ -379,5 +388,8 @@ export default compose( preferredSkinTone: { key: ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, }, + reactions: { + key: ({report, action}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${report.reportID}${action.reportActionID}`, + }, }), )(ReportActionItem); From f5de44fded34d0053afe20e2f756a4501c83ff2b Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Tue, 25 Apr 2023 15:07:04 -0600 Subject: [PATCH 02/68] Call the new toggle method --- src/components/Reactions/MiniQuickEmojiReactions.js | 5 ++++- .../QuickEmojiReactions/BaseQuickEmojiReactions.js | 5 ++++- src/libs/actions/Report.js | 4 ++++ src/pages/home/report/ContextMenu/ContextMenuActions.js | 8 ++++++-- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/components/Reactions/MiniQuickEmojiReactions.js b/src/components/Reactions/MiniQuickEmojiReactions.js index ca11824639d5..69051d6147ae 100644 --- a/src/components/Reactions/MiniQuickEmojiReactions.js +++ b/src/components/Reactions/MiniQuickEmojiReactions.js @@ -55,7 +55,7 @@ const MiniQuickEmojiReactions = (props) => { EmojiPickerAction.showEmojiPicker( props.onEmojiPickerClosed, (emojiCode, emojiObject) => { - props.onEmojiSelected(emojiObject); + props.onEmojiSelected(emojiObject, props.reactions); }, ref.current, ); @@ -106,5 +106,8 @@ export default compose( preferredSkinTone: { key: ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, }, + reactions: { + key: ({reportID, reportActionID}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportID}${reportActionID}`, + }, }), )(MiniQuickEmojiReactions); diff --git a/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js b/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js index c0618a7bc577..602faef8bd08 100644 --- a/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js +++ b/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js @@ -55,7 +55,7 @@ const BaseQuickEmojiReactions = props => ( emojiCodes={[getPreferredEmojiCode(emoji, props.preferredSkinTone)]} isContextMenu onPress={() => { - props.onEmojiSelected(emoji); + props.onEmojiSelected(emoji, props.reactions); }} /> @@ -76,6 +76,9 @@ export default withOnyx({ preferredSkinTone: { key: ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, }, + reactions: { + key: ({reportID, reportActionID}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportID}${reportActionID}`, + }, })(BaseQuickEmojiReactions); export { diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 18f5d8402ced..e40761750f11 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1440,6 +1440,8 @@ function addEmojiReaction2(reportID, reportActionID, emoji, skinTone = preferred }, }, ]; + Onyx.update(optimisticData); + return; const parameters = { reportID, skinTone, @@ -1483,6 +1485,8 @@ function removeEmojiReaction2(reportID, reportActionID, emoji, existingReactions }, ]; + Onyx.update(optimisticData); + return; const parameters = { reportID, reportActionID, diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.js b/src/pages/home/report/ContextMenu/ContextMenuActions.js index 385fee477f7b..760da8f3f294 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.js +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.js @@ -57,8 +57,8 @@ export default [ } }; - const onEmojiSelected = (emoji) => { - Report.toggleEmojiReaction(reportID, reportAction, emoji); + const onEmojiSelected = (emoji, existingReactions) => { + Report.toggleEmojiReaction(reportID, reportAction, emoji, existingReactions); closeContextMenu(); }; @@ -69,6 +69,8 @@ export default [ onEmojiSelected={onEmojiSelected} onPressOpenPicker={openContextMenu} onEmojiPickerClosed={closeContextMenu} + reportID={reportID} + reportActionID={reportAction.reportActionID} /> ); } @@ -78,6 +80,8 @@ export default [ key="BaseQuickEmojiReactions" closeContextMenu={closeContextMenu} onEmojiSelected={onEmojiSelected} + reportID={reportID} + reportActionID={reportAction.reportActionID} /> ); }, From a7945e9e66d8aa3b1a03bac3ed73f014be02a0b8 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Tue, 25 Apr 2023 15:12:51 -0600 Subject: [PATCH 03/68] Display reactions in new format --- src/components/Reactions/ReportActionItemReactions.js | 5 +++-- src/pages/home/report/ReportActionItem.js | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/Reactions/ReportActionItemReactions.js b/src/components/Reactions/ReportActionItemReactions.js index 66198e3b7cf2..2c65881eddd4 100644 --- a/src/components/Reactions/ReportActionItemReactions.js +++ b/src/components/Reactions/ReportActionItemReactions.js @@ -55,12 +55,13 @@ const propTypes = { }; const ReportActionItemReactions = (props) => { - const reactionsWithCount = _.filter(props.reactions, reaction => reaction.users.length > 0); + const reactionsWithCount = _.omit(props.reactions, reaction => _.size(reaction.users) === 0); + console.log(reactionsWithCount) return ( {_.map(reactionsWithCount, (reaction) => { - const reactionCount = reaction.users.length; + const reactionCount = _.size(reaction.users); const reactionUsers = _.map(reaction.users, sender => sender.accountID.toString()); const emoji = _.find(emojis, e => e.name === reaction.emoji); const emojiCodes = getUniqueEmojiCodes(emoji, reaction.users); diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index c3f91e17216e..68325b70c005 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -237,8 +237,8 @@ class ReportActionItem extends Component { ); } - const reactions = _.get(this.props, ['action', 'message', 0, 'reactions'], []); - const hasReactions = reactions.length > 0; + const reactions = _.get(this.props, ['reactions'], {}); + const hasReactions = _.size(reactions) > 0; return ( <> From 1b9f15f78b2faba604db4e71c4df8ac0b5211ec8 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Tue, 25 Apr 2023 15:48:42 -0600 Subject: [PATCH 04/68] Make sure toggling reactions works locally in onyx --- .../Reactions/MiniQuickEmojiReactions.js | 2 +- .../BaseQuickEmojiReactions.js | 2 +- .../Reactions/ReportActionItemReactions.js | 5 ++--- src/libs/actions/Report.js | 21 +++++-------------- src/pages/home/report/ReportActionItem.js | 1 + 5 files changed, 10 insertions(+), 21 deletions(-) diff --git a/src/components/Reactions/MiniQuickEmojiReactions.js b/src/components/Reactions/MiniQuickEmojiReactions.js index 69051d6147ae..309d546c0ab0 100644 --- a/src/components/Reactions/MiniQuickEmojiReactions.js +++ b/src/components/Reactions/MiniQuickEmojiReactions.js @@ -68,7 +68,7 @@ const MiniQuickEmojiReactions = (props) => { key={emoji.name} isDelayButtonStateComplete={false} tooltipText={`:${emoji.name}:`} - onPress={() => props.onEmojiSelected(emoji)} + onPress={() => props.onEmojiSelected(emoji, props.reactions)} > ( isContextMenu onPressOpenPicker={props.onPressOpenPicker} onWillShowPicker={props.onWillShowPicker} - onSelectEmoji={props.onEmojiSelected} + onSelectEmoji={emoji => props.onEmojiSelected(emoji, props.reactions)} /> ); diff --git a/src/components/Reactions/ReportActionItemReactions.js b/src/components/Reactions/ReportActionItemReactions.js index 2c65881eddd4..3676c103b09b 100644 --- a/src/components/Reactions/ReportActionItemReactions.js +++ b/src/components/Reactions/ReportActionItemReactions.js @@ -62,7 +62,6 @@ const ReportActionItemReactions = (props) => { {_.map(reactionsWithCount, (reaction) => { const reactionCount = _.size(reaction.users); - const reactionUsers = _.map(reaction.users, sender => sender.accountID.toString()); const emoji = _.find(emojis, e => e.name === reaction.emoji); const emojiCodes = getUniqueEmojiCodes(emoji, reaction.users); @@ -76,7 +75,7 @@ const ReportActionItemReactions = (props) => { )} key={reaction.emoji} @@ -85,7 +84,7 @@ const ReportActionItemReactions = (props) => { count={reactionCount} emojiCodes={emojiCodes} onPress={onPress} - reactionUsers={reactionUsers} + reactionUsers={reaction.users} /> ); diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index e40761750f11..affe6ae6e5b2 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1304,16 +1304,7 @@ function getOptimisticDataForReportActionUpdate(originalReportAction, message, r * @returns {boolean} */ function hasAccountIDReacted(accountID, users, skinTone) { - return _.find(users, (user) => { - let userAccountID; - if (typeof user === 'object') { - userAccountID = `${user.accountID}`; - } else { - userAccountID = `${user}`; - } - - return userAccountID === `${accountID}` && (skinTone == null ? true : user.skinTone === skinTone); - }) !== undefined; + return Boolean(users[accountID] && (skinTone == null ? true : users[accountID].skinTone === skinTone)); } /** @@ -1500,17 +1491,15 @@ function removeEmojiReaction2(reportID, reportActionID, emoji, existingReactions * @param {String} reportID * @param {Object} reportAction * @param {Object} emoji - * @param {number} paramSkinTone * @param {Object} existingReactions * @returns {Promise} */ -function toggleEmojiReaction(reportID, reportAction, emoji, paramSkinTone = preferredSkinTone, existingReactions) { - const message = reportAction.message[0]; - const reactionObject = message.reactions && _.find(message.reactions, reaction => reaction.emoji === emoji.name); - const skinTone = emoji.types === undefined ? null : paramSkinTone; // only use skin tone if emoji supports it +function toggleEmojiReaction(reportID, reportAction, emoji, existingReactions) { + const reactionObject = existingReactions[emoji.name]; + const skinTone = emoji.types === undefined ? null : preferredSkinTone; // only use skin tone if emoji supports it if (reactionObject) { if (hasAccountIDReacted(currentUserAccountID, reactionObject.users, skinTone)) { - return removeEmojiReaction2(reportID, reportAction.reportActionID, emoji, skinTone, existingReactions); + return removeEmojiReaction2(reportID, reportAction.reportActionID, emoji, existingReactions); } } return addEmojiReaction2(reportID, reportAction.reportActionID, emoji, skinTone); diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 68325b70c005..b830303cc6c5 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -121,6 +121,7 @@ class ReportActionItem extends Component { || this.props.hasOutstandingIOU !== nextProps.hasOutstandingIOU || this.props.shouldDisplayNewMarker !== nextProps.shouldDisplayNewMarker || !_.isEqual(this.props.action, nextProps.action) + || !_.isEqual(this.props.reactions, nextProps.reactions) || this.state.isContextMenuActive !== nextState.isContextMenuActive || lodashGet(this.props.report, 'statusNum') !== lodashGet(nextProps.report, 'statusNum') || lodashGet(this.props.report, 'stateNum') !== lodashGet(nextProps.report, 'stateNum') From af0fed27de7c8ee23e1ad6cc9b38adb44f04cb8f Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Tue, 25 Apr 2023 15:56:54 -0600 Subject: [PATCH 05/68] Fix a few references to user account IDs and clean up code --- .../Reactions/ReportActionItemReactions.js | 65 +++++++++---------- 1 file changed, 30 insertions(+), 35 deletions(-) diff --git a/src/components/Reactions/ReportActionItemReactions.js b/src/components/Reactions/ReportActionItemReactions.js index 3676c103b09b..12f9122019c4 100644 --- a/src/components/Reactions/ReportActionItemReactions.js +++ b/src/components/Reactions/ReportActionItemReactions.js @@ -54,45 +54,40 @@ const propTypes = { toggleReaction: PropTypes.func.isRequired, }; -const ReportActionItemReactions = (props) => { - const reactionsWithCount = _.omit(props.reactions, reaction => _.size(reaction.users) === 0); - console.log(reactionsWithCount) +const ReportActionItemReactions = props => ( + + {_.map(props.reactions, (reaction) => { + const reactionCount = _.size(reaction.users); + const emoji = _.find(emojis, e => e.name === reaction.emoji); + const emojiCodes = getUniqueEmojiCodes(emoji, reaction.users); - return ( - - {_.map(reactionsWithCount, (reaction) => { - const reactionCount = _.size(reaction.users); - const emoji = _.find(emojis, e => e.name === reaction.emoji); - const emojiCodes = getUniqueEmojiCodes(emoji, reaction.users); + const onPress = () => { + props.toggleReaction(emoji); + }; - const onPress = () => { - props.toggleReaction(emoji); - }; - - return ( - ( - - )} - key={reaction.emoji} - > - ( + - - ); - })} - {reactionsWithCount.length > 0 && } - - ); -}; + )} + key={reaction.emoji} + > + + + ); + })} + {_.size(props.reactions) > 0 && } + +); ReportActionItemReactions.displayName = 'ReportActionItemReactions'; ReportActionItemReactions.propTypes = propTypes; From b80558673bfc6889258d25ecdc75dad6d3efc4ea Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 26 Apr 2023 15:50:36 -0600 Subject: [PATCH 06/68] Protect against report actions that don't have any reactions yet --- src/libs/actions/Report.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index affe6ae6e5b2..593392a94cf9 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1495,7 +1495,7 @@ function removeEmojiReaction2(reportID, reportActionID, emoji, existingReactions * @returns {Promise} */ function toggleEmojiReaction(reportID, reportAction, emoji, existingReactions) { - const reactionObject = existingReactions[emoji.name]; + const reactionObject = existingReactions && existingReactions[emoji.name]; const skinTone = emoji.types === undefined ? null : preferredSkinTone; // only use skin tone if emoji supports it if (reactionObject) { if (hasAccountIDReacted(currentUserAccountID, reactionObject.users, skinTone)) { From 4d70bbd7261c4d0707e6e85462e01e78b5dd5988 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Thu, 27 Apr 2023 09:32:11 -0600 Subject: [PATCH 07/68] Turn on API requests --- src/libs/actions/Report.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 593392a94cf9..0ca9f1c57a78 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1431,15 +1431,14 @@ function addEmojiReaction2(reportID, reportActionID, emoji, skinTone = preferred }, }, ]; - Onyx.update(optimisticData); - return; + const parameters = { reportID, skinTone, emojiCode: emoji.name, reportActionID, }; - API.write('AddEmojiReaction2', parameters, {optimisticData}); + API.write('AddEmojiReaction', parameters, {optimisticData}); } /** @@ -1476,14 +1475,12 @@ function removeEmojiReaction2(reportID, reportActionID, emoji, existingReactions }, ]; - Onyx.update(optimisticData); - return; const parameters = { reportID, reportActionID, emojiCode: emoji.name, }; - API.write('RemoveEmojiReaction2', parameters, {optimisticData}); + API.write('RemoveEmojiReaction', parameters, {optimisticData}); } /** From 8aacb9f30610bf285bdcd7284212e03841574d37 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Thu, 27 Apr 2023 15:45:15 -0600 Subject: [PATCH 08/68] Simplify the reaction data --- src/libs/actions/Report.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 0ca9f1c57a78..a5d4a06af09f 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1423,9 +1423,8 @@ function addEmojiReaction2(reportID, reportActionID, emoji, skinTone = preferred key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportID}${reportActionID}`, value: { [emoji.name]: { - emoji: emoji.name, users: { - [currentUserAccountID]: {accountID: currentUserAccountID, skinTone}, + [currentUserAccountID]: {skinTone}, }, }, }, From cf4a396a44a907f6976c668bff875db1aff541c7 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Thu, 27 Apr 2023 15:58:02 -0600 Subject: [PATCH 09/68] Use the updated data structure --- src/components/Reactions/ReportActionItemReactions.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/Reactions/ReportActionItemReactions.js b/src/components/Reactions/ReportActionItemReactions.js index 12f9122019c4..acec923fbaa4 100644 --- a/src/components/Reactions/ReportActionItemReactions.js +++ b/src/components/Reactions/ReportActionItemReactions.js @@ -56,9 +56,9 @@ const propTypes = { const ReportActionItemReactions = props => ( - {_.map(props.reactions, (reaction) => { + {_.map(props.reactions, (reaction, reactionEmoji) => { const reactionCount = _.size(reaction.users); - const emoji = _.find(emojis, e => e.name === reaction.emoji); + const emoji = _.find(emojis, e => e.name === reactionEmoji); const emojiCodes = getUniqueEmojiCodes(emoji, reaction.users); const onPress = () => { @@ -69,7 +69,7 @@ const ReportActionItemReactions = props => ( ( @@ -80,7 +80,7 @@ const ReportActionItemReactions = props => ( count={reactionCount} emojiCodes={emojiCodes} onPress={onPress} - reactionUsers={reaction.users} + reactionUsers={_.keys(reaction.users)} /> ); From f88e62c2c4f5d594d5247e07ee1625056101abd8 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Thu, 11 May 2023 13:49:37 -0600 Subject: [PATCH 10/68] style --- .../Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js b/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js index 0db3ef2ed475..759cc5a2b0ae 100644 --- a/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js +++ b/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js @@ -67,7 +67,7 @@ const BaseQuickEmojiReactions = (props) => ( isContextMenu onPressOpenPicker={props.onPressOpenPicker} onWillShowPicker={props.onWillShowPicker} - onSelectEmoji={emoji => props.onEmojiSelected(emoji, props.reactions)} + onSelectEmoji={(emoji) => props.onEmojiSelected(emoji, props.reactions)} /> ); From ea49f29c6cb208145db85d11322f563d64f09276 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Thu, 11 May 2023 13:51:59 -0600 Subject: [PATCH 11/68] style --- .../Reactions/ReportActionItemReactions.js | 4 ++-- src/pages/home/report/ReportActionItem.js | 18 +++++++++++------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/components/Reactions/ReportActionItemReactions.js b/src/components/Reactions/ReportActionItemReactions.js index dfb5ea5de881..441117ce47ff 100644 --- a/src/components/Reactions/ReportActionItemReactions.js +++ b/src/components/Reactions/ReportActionItemReactions.js @@ -64,11 +64,11 @@ const defaultProps = { ...withCurrentUserPersonalDetailsDefaultProps, }; -const ReportActionItemReactions = props => ( +const ReportActionItemReactions = (props) => ( {_.map(props.reactions, (reaction, reactionEmoji) => { const reactionCount = _.size(reaction.users); - const emoji = _.find(emojis, e => e.name === reactionEmoji); + const emoji = _.find(emojis, (e) => e.name === reactionEmoji); const emojiCodes = getUniqueEmojiCodes(emoji, reaction.users); const onPress = () => { diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 1a69f7b44b37..21b8d914b83f 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -83,13 +83,17 @@ const propTypes = { /** All of the personalDetails */ personalDetails: PropTypes.objectOf(personalDetailsPropType), - reactions: PropTypes.objectOf(PropTypes.shape({ - emoji: PropTypes.string, - users: PropTypes.objectOf(PropTypes.shape({ - accountID: PropTypes.number, - skinTone: PropTypes.string, - })), - })), + reactions: PropTypes.objectOf( + PropTypes.shape({ + emoji: PropTypes.string, + users: PropTypes.objectOf( + PropTypes.shape({ + accountID: PropTypes.number, + skinTone: PropTypes.string, + }), + ), + }), + ), ...windowDimensionsPropTypes, }; From 36568e2526503c0b24d0801f15df21a9e9332571 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Thu, 11 May 2023 16:33:14 -0600 Subject: [PATCH 12/68] Get new emoji reactions displaying properly --- .../Reactions/ReactionTooltipContent.js | 1 + .../ReportActionItemEmojiReactions.js | 139 ++++++++++++++++++ .../Reactions/ReportActionItemReactions.js | 79 ++++++---- src/libs/actions/Report.js | 3 +- src/pages/home/report/ReportActionItem.js | 28 +++- 5 files changed, 214 insertions(+), 36 deletions(-) create mode 100644 src/components/Reactions/ReportActionItemEmojiReactions.js diff --git a/src/components/Reactions/ReactionTooltipContent.js b/src/components/Reactions/ReactionTooltipContent.js index 42c2a1f0fa61..f605f3fe7384 100644 --- a/src/components/Reactions/ReactionTooltipContent.js +++ b/src/components/Reactions/ReactionTooltipContent.js @@ -32,6 +32,7 @@ const defaultProps = { }; const ReactionTooltipContent = (props) => { + console.log(props); const users = useMemo( () => PersonalDetailsUtils.getPersonalDetailsByIDs(props.accountIDs, props.currentUserPersonalDetails.accountID, true), [props.currentUserPersonalDetails.accountID, props.accountIDs], diff --git a/src/components/Reactions/ReportActionItemEmojiReactions.js b/src/components/Reactions/ReportActionItemEmojiReactions.js new file mode 100644 index 000000000000..dbc997ab8816 --- /dev/null +++ b/src/components/Reactions/ReportActionItemEmojiReactions.js @@ -0,0 +1,139 @@ +import React, {useRef} from 'react'; +import _ from 'underscore'; +import {View} from 'react-native'; +import PropTypes from 'prop-types'; +import styles from '../../styles/styles'; +import EmojiReactionBubble from './EmojiReactionBubble'; +import emojis from '../../../assets/emojis'; +import AddReactionBubble from './AddReactionBubble'; +import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '../withCurrentUserPersonalDetails'; +import getPreferredEmojiCode from './getPreferredEmojiCode'; +import Tooltip from '../Tooltip'; +import ReactionTooltipContent from './ReactionTooltipContent'; +import * as Report from '../../libs/actions/Report'; +import * as PersonalDetailsUtils from '../../libs/PersonalDetailsUtils'; +import * as ReactionList from '../../pages/home/report/ReactionList/ReactionList'; + +/** + * Given an emoji object and a list of senders it will return an + * array of emoji codes, that represents all used variations of the + * emoji. + * @param {{ name: string, code: string, types: string[] }} emojiAsset + * @param {Object} users + * @return {string[]} + * */ +const getUniqueEmojiCodes = (emojiAsset, users) => { + const uniqueEmojiCodes = []; + _.each(users, (userSkinTones) => { + _.each(userSkinTones.skinTones, (createdAt, skinTone) => { + const emojiCode = getPreferredEmojiCode(emojiAsset, skinTone); + if (emojiCode && !uniqueEmojiCodes.includes(emojiCode)) { + uniqueEmojiCodes.push(emojiCode); + } + }); + }); + return uniqueEmojiCodes; +}; + +const propTypes = { + /** All the emoji reactions for the report action. An object that looks like this: + "emojiReactions": { + "+1": { // The emoji added to the action + "createdAt": "2021-01-01 00:00:00", + "users": { + 2352342: { // The accountID of the user who added this emoji + "skinTones": { + "1": "2021-01-01 00:00:00", + "2": "2021-01-01 00:00:00", + }, + }, + }, + }, + }, + */ + emojiReactions: PropTypes.objectOf( + PropTypes.shape({ + /** The time the emoji was added */ + createdAt: PropTypes.string, + + /** All the users who have added this emoji */ + users: PropTypes.objectOf( + PropTypes.shape({ + /** The skin tone which was used and also the timestamp of when it was added */ + skinTones: PropTypes.objectOf(PropTypes.string), + }), + ), + }), + ), + + /** + * Function to call when the user presses on an emoji. + * This can also be an emoji the user already reacted with, + * hence this function asks to toggle the reaction by emoji. + */ + toggleReaction: PropTypes.func.isRequired, + + ...withCurrentUserPersonalDetailsPropTypes, +}; + +const defaultProps = { + ...withCurrentUserPersonalDetailsDefaultProps, + + emojiReactions: {}, +}; + +const ReportActionItemEmojiReactions = (props) => { + const popoverReactionListAnchor = useRef(null); + return ( + + {_.map(props.emojiReactions, (reaction, reactionEmoji) => { + // @TODO: need to sort everything so that emojis and users are in the order they were added + const reactionCount = _.size(reaction.users); + const emojiAsset = _.find(emojis, (emoji) => emoji.name === reactionEmoji); + const emojiCodes = getUniqueEmojiCodes(emojiAsset, reaction.users); + const reactionUsers = _.keys(reaction.users); + const hasUserReacted = Report.hasAccountIDReacted(props.currentUserPersonalDetails.accountID, reactionUsers); + + const onPress = () => { + props.toggleReaction(emojiAsset); + }; + const onReactionListOpen = (event) => { + const users = PersonalDetailsUtils.getPersonalDetailsByIDs(reactionUsers, props.currentUserPersonalDetails.accountID); + ReactionList.showReactionList(event, popoverReactionListAnchor.current, users, reaction.emoji, emojiCodes, reactionCount, hasUserReacted); + }; + + return ( + ( + + )} + key={reactionEmoji} + > + + + ); + })} + {_.size(props.reactions) > 0 && } + + ); +}; + +ReportActionItemEmojiReactions.displayName = 'ReportActionItemEmojiReactions'; +ReportActionItemEmojiReactions.propTypes = propTypes; +ReportActionItemEmojiReactions.defaultProps = defaultProps; +export default withCurrentUserPersonalDetails(ReportActionItemEmojiReactions); diff --git a/src/components/Reactions/ReportActionItemReactions.js b/src/components/Reactions/ReportActionItemReactions.js index 441117ce47ff..314f00c68280 100644 --- a/src/components/Reactions/ReportActionItemReactions.js +++ b/src/components/Reactions/ReportActionItemReactions.js @@ -64,40 +64,59 @@ const defaultProps = { ...withCurrentUserPersonalDetailsDefaultProps, }; -const ReportActionItemReactions = (props) => ( - - {_.map(props.reactions, (reaction, reactionEmoji) => { - const reactionCount = _.size(reaction.users); - const emoji = _.find(emojis, (e) => e.name === reactionEmoji); - const emojiCodes = getUniqueEmojiCodes(emoji, reaction.users); +const ReportActionItemReactions = (props) => { + const popoverReactionListAnchor = useRef(null); + const reactionsWithCount = _.filter(props.reactions, (reaction) => reaction.users.length > 0); - const onPress = () => { - props.toggleReaction(emoji); - }; + return ( + + {_.map(reactionsWithCount, (reaction) => { + const reactionCount = reaction.users.length; + const reactionUsers = _.map(reaction.users, (sender) => sender.accountID.toString()); + const emoji = _.find(emojis, (e) => e.name === reaction.emoji); + const emojiCodes = getUniqueEmojiCodes(emoji, reaction.users); + const hasUserReacted = Report.hasAccountIDReacted(props.currentUserPersonalDetails.accountID, reactionUsers); - return ( - ( - { + props.toggleReaction(emoji); + }; + const onReactionListOpen = (event) => { + const users = PersonalDetailsUtils.getPersonalDetailsByIDs(reactionUsers, props.currentUserPersonalDetails.accountID); + ReactionList.showReactionList(event, popoverReactionListAnchor.current, users, reaction.emoji, emojiCodes, reactionCount, hasUserReacted); + }; + + return ( + ( + + )} + renderTooltipContentKey={[...reactionUsers, ...emojiCodes]} + key={reaction.emoji} + > + - )} - key={reaction.emoji} - > - - - ); - })} - {_.size(props.reactions) > 0 && } - -); + + ); + })} + {reactionsWithCount.length > 0 && } + + ); +}; ReportActionItemReactions.displayName = 'ReportActionItemReactions'; ReportActionItemReactions.propTypes = propTypes; diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index da03aa3cc9e6..7cfd125416d6 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1578,9 +1578,10 @@ function removeEmojiReaction2(reportID, reportActionID, emoji, existingReactions * @param {Object} reportAction * @param {Object} emoji * @param {Object} existingReactions + * @param {Number} [paramSkinTone] * @returns {Promise} */ -function toggleEmojiReaction(reportID, reportAction, emoji, paramSkinTone = preferredSkinTone) { +function toggleEmojiReaction(reportID, reportAction, emoji, existingReactions, paramSkinTone = preferredSkinTone) { const message = reportAction.message[0]; const reactionObject = message.reactions && _.find(message.reactions, (reaction) => reaction.emoji === emoji.name); const skinTone = emoji.types === undefined ? null : paramSkinTone; // only use skin tone if emoji supports it diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 21b8d914b83f..b378fe3b96ac 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -38,6 +38,7 @@ import {ShowContextMenuContext} from '../../../components/ShowContextMenuContext import focusTextInputAfterAnimation from '../../../libs/focusTextInputAfterAnimation'; import ChronosOOOListActions from '../../../components/ReportActionItem/ChronosOOOListActions'; import ReportActionItemReactions from '../../../components/Reactions/ReportActionItemReactions'; +import ReportActionItemEmojiReactions from '../../../components/Reactions/ReportActionItemEmojiReactions'; import * as Report from '../../../libs/actions/Report'; import withLocalize from '../../../components/withLocalize'; import Icon from '../../../components/Icon'; @@ -83,7 +84,7 @@ const propTypes = { /** All of the personalDetails */ personalDetails: PropTypes.objectOf(personalDetailsPropType), - reactions: PropTypes.objectOf( + emojiReactions: PropTypes.objectOf( PropTypes.shape({ emoji: PropTypes.string, users: PropTypes.objectOf( @@ -103,7 +104,7 @@ const defaultProps = { hasOutstandingIOU: false, preferredSkinTone: CONST.EMOJI_DEFAULT_SKIN_TONE, personalDetails: {}, - reactions: {}, + emojiReactions: {}, shouldShowSubscriptAvatar: false, }; @@ -128,7 +129,7 @@ class ReportActionItem extends Component { this.props.hasOutstandingIOU !== nextProps.hasOutstandingIOU || this.props.shouldDisplayNewMarker !== nextProps.shouldDisplayNewMarker || !_.isEqual(this.props.action, nextProps.action) || - !_.isEqual(this.props.reactions, nextProps.reactions) || + !_.isEqual(this.props.emojiReactions, nextProps.emojiReactions) || this.state.isContextMenuActive !== nextState.isContextMenuActive || lodashGet(this.props.report, 'statusNum') !== lodashGet(nextProps.report, 'statusNum') || lodashGet(this.props.report, 'stateNum') !== lodashGet(nextProps.report, 'stateNum') || @@ -254,12 +255,29 @@ class ReportActionItem extends Component { ); } + // This will get cleaned up as part of https://github.com/Expensify/App/issues/16506 once the old emoji + // format is no longer being used + + // Support NEW FORMAT "emojiReactions" in their own collection + const emojiReactions = _.get(this.props, ['emojiReactions'], {}); + const hasEmojiReactions = _.size(emojiReactions) > 0; + + // Support OLD FORMAT "reactions" on the report action's message const reactions = _.get(this.props, ['action', 'message', 0, 'reactions'], []); const hasReactions = reactions.length > 0; + return ( <> {children} - {hasReactions && ( + {hasEmojiReactions && ( + + + + )} + {!hasEmojiReactions && hasReactions && ( `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${report.reportID}${action.reportActionID}`, }, }), From 62f83fe13882c748b2ce8f140a5774339c8c8b95 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Fri, 12 May 2023 08:56:52 -0600 Subject: [PATCH 13/68] Add timestamp to requests when adding reactions --- src/CONST.js | 1 + src/components/Reactions/ReactionTooltipContent.js | 1 - .../Reactions/ReportActionItemEmojiReactions.js | 2 +- src/libs/actions/Report.js | 14 +++++++++++--- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/CONST.js b/src/CONST.js index 614ec00917c3..56184455c09d 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -113,6 +113,7 @@ const CONST = { }, DATE: { MOMENT_FORMAT_STRING: 'YYYY-MM-DD', + SQL_DATE_TIME: 'YYYY-MM-DD HH:mm:ss', UNIX_EPOCH: '1970-01-01 00:00:00.000', MAX_DATE: '9999-12-31', MIN_DATE: '0001-01-01', diff --git a/src/components/Reactions/ReactionTooltipContent.js b/src/components/Reactions/ReactionTooltipContent.js index f605f3fe7384..42c2a1f0fa61 100644 --- a/src/components/Reactions/ReactionTooltipContent.js +++ b/src/components/Reactions/ReactionTooltipContent.js @@ -32,7 +32,6 @@ const defaultProps = { }; const ReactionTooltipContent = (props) => { - console.log(props); const users = useMemo( () => PersonalDetailsUtils.getPersonalDetailsByIDs(props.accountIDs, props.currentUserPersonalDetails.accountID, true), [props.currentUserPersonalDetails.accountID, props.accountIDs], diff --git a/src/components/Reactions/ReportActionItemEmojiReactions.js b/src/components/Reactions/ReportActionItemEmojiReactions.js index dbc997ab8816..26c64af6b250 100644 --- a/src/components/Reactions/ReportActionItemEmojiReactions.js +++ b/src/components/Reactions/ReportActionItemEmojiReactions.js @@ -84,13 +84,13 @@ const defaultProps = { const ReportActionItemEmojiReactions = (props) => { const popoverReactionListAnchor = useRef(null); + // @TODO: need to sort everything so that emojis and users are in the order they were added return ( {_.map(props.emojiReactions, (reaction, reactionEmoji) => { - // @TODO: need to sort everything so that emojis and users are in the order they were added const reactionCount = _.size(reaction.users); const emojiAsset = _.find(emojis, (emoji) => emoji.name === reactionEmoji); const emojiCodes = getUniqueEmojiCodes(emojiAsset, reaction.users); diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 7cfd125416d6..bcfec7b08163 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -4,6 +4,7 @@ import lodashGet from 'lodash/get'; import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import Onyx from 'react-native-onyx'; import Str from 'expensify-common/lib/str'; +import moment from 'moment'; import ONYXKEYS from '../../ONYXKEYS'; import * as Pusher from '../Pusher/pusher'; import LocalNotification from '../Notification/LocalNotification'; @@ -1507,14 +1508,20 @@ function removeEmojiReaction(reportID, originalReportAction, emoji) { * @param {number} [skinTone] Optional. */ function addEmojiReaction2(reportID, reportActionID, emoji, skinTone = preferredSkinTone) { + const createdAt = moment().utc().format(CONST.DATE.SQL_DATE_TIME); const optimisticData = [ { - onyxMethod: CONST.ONYX.METHOD.MERGE, + onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportID}${reportActionID}`, value: { [emoji.name]: { + createdAt, users: { - [currentUserAccountID]: {skinTone}, + [currentUserAccountID]: { + skinTones: { + [skinTone]: createdAt, + }, + }, }, }, }, @@ -1526,6 +1533,7 @@ function addEmojiReaction2(reportID, reportActionID, emoji, skinTone = preferred skinTone, emojiCode: emoji.name, reportActionID, + createdAt, }; API.write('AddEmojiReaction', parameters, {optimisticData}); } @@ -1556,7 +1564,7 @@ function removeEmojiReaction2(reportID, reportActionID, emoji, existingReactions const optimisticData = [ { - onyxMethod: CONST.ONYX.METHOD.MERGE, + onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportID}${reportActionID}`, value: { [emoji.name]: updatedReactionObject, From 8c31f4f0e8551ae6d54ef0121ba6ba539a5352e1 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Fri, 12 May 2023 09:29:27 -0600 Subject: [PATCH 14/68] Add API parameter to use new data format --- src/libs/actions/Report.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index bcfec7b08163..2bfa9323cd49 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1534,6 +1534,7 @@ function addEmojiReaction2(reportID, reportActionID, emoji, skinTone = preferred emojiCode: emoji.name, reportActionID, createdAt, + useEmojiReactions: true, }; API.write('AddEmojiReaction', parameters, {optimisticData}); } @@ -1576,6 +1577,7 @@ function removeEmojiReaction2(reportID, reportActionID, emoji, existingReactions reportID, reportActionID, emojiCode: emoji.name, + useEmojiReactions: true, }; API.write('RemoveEmojiReaction', parameters, {optimisticData}); } From f93266ee1d67b97ea3aa60a7688e33f4f712e066 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Fri, 12 May 2023 09:39:23 -0600 Subject: [PATCH 15/68] Rename methods --- src/libs/actions/Report.js | 22 +++++++++++----------- tests/actions/ReportTest.js | 6 +++--- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 2bfa9323cd49..cc17b44e84b4 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1401,12 +1401,13 @@ function hasAccountIDReacted(accountID, users, skinTone) { /** * Adds a reaction to the report action. + * Uses the OLD FORMAT for "reactions" * @param {String} reportID * @param {Object} originalReportAction * @param {{ name: string, code: string, types: string[] }} emoji * @param {number} [skinTone] Optional. */ -function addEmojiReaction(reportID, originalReportAction, emoji, skinTone = preferredSkinTone) { +function addReaction(reportID, originalReportAction, emoji, skinTone = preferredSkinTone) { const message = originalReportAction.message[0]; let reactionObject = message.reactions && _.find(message.reactions, (reaction) => reaction.emoji === emoji.name); const needToInsertReactionObject = !reactionObject; @@ -1452,11 +1453,12 @@ function addEmojiReaction(reportID, originalReportAction, emoji, skinTone = pref /** * Removes a reaction to the report action. + * Uses the OLD FORMAT for "reactions" * @param {String} reportID * @param {Object} originalReportAction * @param {{ name: string, code: string, types: string[] }} emoji */ -function removeEmojiReaction(reportID, originalReportAction, emoji) { +function removeReaction(reportID, originalReportAction, emoji) { const message = originalReportAction.message[0]; const reactionObject = message.reactions && _.find(message.reactions, (reaction) => reaction.emoji === emoji.name); if (!reactionObject) { @@ -1502,12 +1504,13 @@ function removeEmojiReaction(reportID, originalReportAction, emoji) { /** * Adds a reaction to the report action. + * Uses the NEW FORMAT for "emojiReactions" * @param {String} reportID * @param {String} reportActionID * @param {{ name: string, code: string, types: string[] }} emoji * @param {number} [skinTone] Optional. */ -function addEmojiReaction2(reportID, reportActionID, emoji, skinTone = preferredSkinTone) { +function addEmojiReaction(reportID, reportActionID, emoji, skinTone = preferredSkinTone) { const createdAt = moment().utc().format(CONST.DATE.SQL_DATE_TIME); const optimisticData = [ { @@ -1541,12 +1544,13 @@ function addEmojiReaction2(reportID, reportActionID, emoji, skinTone = preferred /** * Removes a reaction to the report action. + * Uses the NEW FORMAT for "emojiReactions" * @param {String} reportID * @param {String} reportActionID * @param {{ name: string, code: string, types: string[] }} emoji * @param {Object} existingReactions */ -function removeEmojiReaction2(reportID, reportActionID, emoji, existingReactions) { +function removeEmojiReaction(reportID, reportActionID, emoji, existingReactions) { const reactionObject = existingReactions[emoji.name]; if (!reactionObject) { return; @@ -1583,7 +1587,7 @@ function removeEmojiReaction2(reportID, reportActionID, emoji, existingReactions } /** - * Calls either addEmojiReaction or removeEmojiReaction depending on if the current user has reacted to the report action. + * Calls either addReaction or removeEmojiReaction depending on if the current user has reacted to the report action. * @param {String} reportID * @param {Object} reportAction * @param {Object} emoji @@ -1597,10 +1601,10 @@ function toggleEmojiReaction(reportID, reportAction, emoji, existingReactions, p const skinTone = emoji.types === undefined ? null : paramSkinTone; // only use skin tone if emoji supports it if (reactionObject) { if (hasAccountIDReacted(currentUserAccountID, reactionObject.users, skinTone)) { - return removeEmojiReaction2(reportID, reportAction.reportActionID, emoji, existingReactions); + return removeEmojiReaction(reportID, reportAction.reportActionID, emoji, existingReactions); } } - return addEmojiReaction2(reportID, reportAction.reportActionID, emoji, skinTone); + return addEmojiReaction(reportID, reportAction.reportActionID, emoji, skinTone); } /** @@ -1656,10 +1660,6 @@ export { clearIOUError, subscribeToNewActionEvent, showReportActionNotification, - addEmojiReaction, - addEmojiReaction2, - removeEmojiReaction, - removeEmojiReaction2, toggleEmojiReaction, hasAccountIDReacted, shouldShowReportActionNotification, diff --git a/tests/actions/ReportTest.js b/tests/actions/ReportTest.js index 219eddc18876..91159498db6c 100644 --- a/tests/actions/ReportTest.js +++ b/tests/actions/ReportTest.js @@ -555,7 +555,7 @@ describe('actions/Report', () => { const resultAction = _.first(_.values(reportActions)); // Add a reaction to the comment - Report.addEmojiReaction(REPORT_ID, resultAction, EMOJI); + Report.toggleEmojiReaction(REPORT_ID, resultAction, EMOJI); return waitForPromisesToResolve(); }) .then(() => { @@ -585,11 +585,11 @@ describe('actions/Report', () => { const resultAction = _.first(_.values(reportActions)); // Add the reaction to the comment, but two times with different variations - Report.addEmojiReaction(REPORT_ID, resultAction, EMOJI); + Report.toggleEmojiReaction(REPORT_ID, resultAction, EMOJI); return waitForPromisesToResolve() .then(() => { const updatedResultAction = _.first(_.values(reportActions)); - Report.addEmojiReaction(REPORT_ID, updatedResultAction, EMOJI, 2); + Report.toggleEmojiReaction(REPORT_ID, updatedResultAction, EMOJI, 2); return waitForPromisesToResolve(); }) .then(() => { From 44d426a6f65bdc88e0feb48d7e1684fa362388de Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Fri, 12 May 2023 09:48:55 -0600 Subject: [PATCH 16/68] Rename old toggle method --- src/libs/actions/Report.js | 10 ++++++---- .../home/report/ContextMenu/ContextMenuActions.js | 2 +- src/pages/home/report/ReportActionItem.js | 2 +- tests/actions/ReportTest.js | 10 +++++----- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index cc17b44e84b4..96264c0db04a 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1595,16 +1595,18 @@ function removeEmojiReaction(reportID, reportActionID, emoji, existingReactions) * @param {Number} [paramSkinTone] * @returns {Promise} */ -function toggleEmojiReaction(reportID, reportAction, emoji, existingReactions, paramSkinTone = preferredSkinTone) { +function toggleReaction(reportID, reportAction, emoji, existingReactions, paramSkinTone = preferredSkinTone) { + // This will get cleaned up as part of https://github.com/Expensify/App/issues/16506 once the old emoji + // format is no longer being used const message = reportAction.message[0]; const reactionObject = message.reactions && _.find(message.reactions, (reaction) => reaction.emoji === emoji.name); const skinTone = emoji.types === undefined ? null : paramSkinTone; // only use skin tone if emoji supports it if (reactionObject) { if (hasAccountIDReacted(currentUserAccountID, reactionObject.users, skinTone)) { - return removeEmojiReaction(reportID, reportAction.reportActionID, emoji, existingReactions); + return addReaction(reportID, reportAction.reportActionID, emoji, existingReactions); } } - return addEmojiReaction(reportID, reportAction.reportActionID, emoji, skinTone); + return removeReaction(reportID, reportAction.reportActionID, emoji, skinTone); } /** @@ -1660,7 +1662,7 @@ export { clearIOUError, subscribeToNewActionEvent, showReportActionNotification, - toggleEmojiReaction, + toggleReaction, hasAccountIDReacted, shouldShowReportActionNotification, }; diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.js b/src/pages/home/report/ContextMenu/ContextMenuActions.js index cc83f588c766..6b2c58a6cc07 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.js +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.js @@ -56,7 +56,7 @@ export default [ }; const onEmojiSelected = (emoji, existingReactions) => { - Report.toggleEmojiReaction(reportID, reportAction, emoji, existingReactions); + Report.toggleReaction(reportID, reportAction, emoji, existingReactions); closeContextMenu(); }; diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index b378fe3b96ac..3171390a3d94 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -182,7 +182,7 @@ class ReportActionItem extends Component { } toggleReaction(emoji) { - Report.toggleEmojiReaction(this.props.report.reportID, this.props.action, emoji, this.props.reactions); + Report.toggleReaction(this.props.report.reportID, this.props.action, emoji, this.props.reactions); } /** diff --git a/tests/actions/ReportTest.js b/tests/actions/ReportTest.js index 91159498db6c..11e73b232a0f 100644 --- a/tests/actions/ReportTest.js +++ b/tests/actions/ReportTest.js @@ -555,7 +555,7 @@ describe('actions/Report', () => { const resultAction = _.first(_.values(reportActions)); // Add a reaction to the comment - Report.toggleEmojiReaction(REPORT_ID, resultAction, EMOJI); + Report.toggleReaction(REPORT_ID, resultAction, EMOJI); return waitForPromisesToResolve(); }) .then(() => { @@ -585,11 +585,11 @@ describe('actions/Report', () => { const resultAction = _.first(_.values(reportActions)); // Add the reaction to the comment, but two times with different variations - Report.toggleEmojiReaction(REPORT_ID, resultAction, EMOJI); + Report.toggleReaction(REPORT_ID, resultAction, EMOJI); return waitForPromisesToResolve() .then(() => { const updatedResultAction = _.first(_.values(reportActions)); - Report.toggleEmojiReaction(REPORT_ID, updatedResultAction, EMOJI, 2); + Report.toggleReaction(REPORT_ID, updatedResultAction, EMOJI, 2); return waitForPromisesToResolve(); }) .then(() => { @@ -663,7 +663,7 @@ describe('actions/Report', () => { const resultAction = _.first(_.values(reportActions)); // Add a reaction to the comment - Report.toggleEmojiReaction(REPORT_ID, resultAction, EMOJI); + Report.toggleReaction(REPORT_ID, resultAction, EMOJI); return waitForPromisesToResolve(); }) .then(() => { @@ -672,7 +672,7 @@ describe('actions/Report', () => { // Now we toggle the reaction while the skin tone has changed. // As the emoji doesn't support skin tones, the emoji // should get removed instead of added again. - Report.toggleEmojiReaction(REPORT_ID, resultAction, EMOJI, 2); + Report.toggleReaction(REPORT_ID, resultAction, EMOJI, 2); return waitForPromisesToResolve(); }) .then(() => { From 5b99716ed584278cad65219713f8939977c92fb9 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Fri, 12 May 2023 10:10:16 -0600 Subject: [PATCH 17/68] Provide a second toggle method for emojiReactions --- src/libs/actions/Report.js | 29 +++++++++++++++++-- .../report/ContextMenu/ContextMenuActions.js | 16 +++++++++- src/pages/home/report/ReportActionItem.js | 16 +++++++++- 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 96264c0db04a..064b273a682a 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1591,11 +1591,10 @@ function removeEmojiReaction(reportID, reportActionID, emoji, existingReactions) * @param {String} reportID * @param {Object} reportAction * @param {Object} emoji - * @param {Object} existingReactions * @param {Number} [paramSkinTone] * @returns {Promise} */ -function toggleReaction(reportID, reportAction, emoji, existingReactions, paramSkinTone = preferredSkinTone) { +function toggleReaction(reportID, reportAction, emoji, paramSkinTone = preferredSkinTone) { // This will get cleaned up as part of https://github.com/Expensify/App/issues/16506 once the old emoji // format is no longer being used const message = reportAction.message[0]; @@ -1603,12 +1602,35 @@ function toggleReaction(reportID, reportAction, emoji, existingReactions, paramS const skinTone = emoji.types === undefined ? null : paramSkinTone; // only use skin tone if emoji supports it if (reactionObject) { if (hasAccountIDReacted(currentUserAccountID, reactionObject.users, skinTone)) { - return addReaction(reportID, reportAction.reportActionID, emoji, existingReactions); + return addReaction(reportID, reportAction.reportActionID, emoji, skinTone); } } return removeReaction(reportID, reportAction.reportActionID, emoji, skinTone); } +/** + * Calls either addReaction or removeEmojiReaction depending on if the current user has reacted to the report action. + * @param {String} reportID + * @param {Object} reportAction + * @param {Object} emoji + * @param {Object} existingReactions + * @param {Number} [paramSkinTone] + * @returns {Promise} + */ +function toggleEmojiReaction(reportID, reportAction, emoji, existingReactions, paramSkinTone = preferredSkinTone) { + // This will get cleaned up as part of https://github.com/Expensify/App/issues/16506 once the old emoji + // format is no longer being used + const message = reportAction.message[0]; + const reactionObject = message.reactions && _.find(message.reactions, (reaction) => reaction.emoji === emoji.name); + const skinTone = emoji.types === undefined ? null : paramSkinTone; // only use skin tone if emoji supports it + if (reactionObject) { + if (hasAccountIDReacted(currentUserAccountID, reactionObject.users, skinTone)) { + return addEmojiReaction(reportID, reportAction.reportActionID, emoji, skinTone); + } + } + return removeEmojiReaction(reportID, reportAction.reportActionID, emoji, skinTone, existingReactions); +} + /** * @param {String|null} url */ @@ -1663,6 +1685,7 @@ export { subscribeToNewActionEvent, showReportActionNotification, toggleReaction, + toggleEmojiReaction, hasAccountIDReacted, shouldShowReportActionNotification, }; diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.js b/src/pages/home/report/ContextMenu/ContextMenuActions.js index 6b2c58a6cc07..5d7a00dba7ef 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.js +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.js @@ -56,7 +56,21 @@ export default [ }; const onEmojiSelected = (emoji, existingReactions) => { - Report.toggleReaction(reportID, reportAction, emoji, existingReactions); + // This will get cleaned up as part of https://github.com/Expensify/App/issues/16506 once the old emoji + // format is no longer being used + + // Look for OLD FORMAT "reactions" on the report action's message + const reactions = _.get(reportAction, ['message', 0, 'reactions'], []); + const hasReactions = reactions.length > 0; + + // When there are no reactions in the OLD FORMAT, always use the NEW FORMAT + if (!hasReactions) { + Report.toggleEmojiReaction(reportID, reportAction, emoji, existingReactions); + } else { + // Only use the OLD when there are existing reactions in that format + Report.toggleReaction(reportID, reportAction, emoji); + } + closeContextMenu(); }; diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 3171390a3d94..21f62c1875bc 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -182,7 +182,21 @@ class ReportActionItem extends Component { } toggleReaction(emoji) { - Report.toggleReaction(this.props.report.reportID, this.props.action, emoji, this.props.reactions); + // This will get cleaned up as part of https://github.com/Expensify/App/issues/16506 once the old emoji + // format is no longer being used + + // Look for OLD FORMAT "reactions" on the report action's message + const reactions = _.get(this.props, ['action', 'message', 0, 'reactions'], []); + const hasReactions = reactions.length > 0; + + // When there are no reactions in the OLD FORMAT, always use the NEW FORMAT + if (!hasReactions) { + Report.toggleEmojiReaction(this.props.report.reportID, this.props.action, emoji, this.props.emojiReactions); + return; + } + + // Only use the OLD FORMAT when there are existing reactions in that format + Report.toggleReaction(this.props.report.reportID, this.props.action, emoji); } /** From b2781c9bd415096485889b7ea9903619054a1355 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Fri, 12 May 2023 10:42:20 -0600 Subject: [PATCH 18/68] Update for checking to see if someone already reacted --- src/libs/actions/Report.js | 56 ++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 064b273a682a..daa6ca2e24a2 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1379,6 +1379,7 @@ function getOptimisticDataForReportActionUpdate(originalReportAction, message, r /** * Returns true if the accountID has reacted to the report action (with the given skin tone). + * Uses the OLD FORMAT for "reactions" * @param {String} accountID * @param {Array} users * @param {Number} [skinTone] @@ -1399,6 +1400,23 @@ function hasAccountIDReacted(accountID, users, skinTone) { ); } +/** + * Returns true if the accountID has reacted to the report action (with the given skin tone). + * Uses the NEW FORMAT for "emojiReactions" + * @param {String} accountID + * @param {Array} users + * @param {Number} [skinTone] + * @returns {boolean} + */ +function hasAccountIDEmojiReacted(accountID, users, skinTone) { + return ( + _.find(users, (userData, userAccountID) => { + const userReactedWithSkinTone = _.isNull(skinTone) ? true : lodashGet(userData, ['skinTones', skinTone]); + return userAccountID === accountID && userReactedWithSkinTone; + }) !== undefined + ); +} + /** * Adds a reaction to the report action. * Uses the OLD FORMAT for "reactions" @@ -1588,47 +1606,49 @@ function removeEmojiReaction(reportID, reportActionID, emoji, existingReactions) /** * Calls either addReaction or removeEmojiReaction depending on if the current user has reacted to the report action. + * Uses the OLD FORMAT for "reactions" * @param {String} reportID * @param {Object} reportAction - * @param {Object} emoji + * @param {{ name: string, code: string, types: string[] }} emoji * @param {Number} [paramSkinTone] - * @returns {Promise} */ function toggleReaction(reportID, reportAction, emoji, paramSkinTone = preferredSkinTone) { // This will get cleaned up as part of https://github.com/Expensify/App/issues/16506 once the old emoji // format is no longer being used const message = reportAction.message[0]; const reactionObject = message.reactions && _.find(message.reactions, (reaction) => reaction.emoji === emoji.name); - const skinTone = emoji.types === undefined ? null : paramSkinTone; // only use skin tone if emoji supports it - if (reactionObject) { - if (hasAccountIDReacted(currentUserAccountID, reactionObject.users, skinTone)) { - return addReaction(reportID, reportAction.reportActionID, emoji, skinTone); - } + + // Only use skin tone if emoji supports it + const skinTone = emoji.types === undefined ? null : paramSkinTone; + if (reactionObject && hasAccountIDReacted(currentUserAccountID, reactionObject.users, skinTone)) { + removeReaction(reportID, reportAction.reportActionID, emoji, skinTone); + return; } - return removeReaction(reportID, reportAction.reportActionID, emoji, skinTone); + addReaction(reportID, reportAction.reportActionID, emoji, skinTone); } /** * Calls either addReaction or removeEmojiReaction depending on if the current user has reacted to the report action. + * Uses the NEW FORMAT for "emojiReactions" * @param {String} reportID * @param {Object} reportAction - * @param {Object} emoji + * @param {{ name: string, code: string, types: string[] }} emoji * @param {Object} existingReactions * @param {Number} [paramSkinTone] - * @returns {Promise} */ function toggleEmojiReaction(reportID, reportAction, emoji, existingReactions, paramSkinTone = preferredSkinTone) { // This will get cleaned up as part of https://github.com/Expensify/App/issues/16506 once the old emoji // format is no longer being used - const message = reportAction.message[0]; - const reactionObject = message.reactions && _.find(message.reactions, (reaction) => reaction.emoji === emoji.name); - const skinTone = emoji.types === undefined ? null : paramSkinTone; // only use skin tone if emoji supports it - if (reactionObject) { - if (hasAccountIDReacted(currentUserAccountID, reactionObject.users, skinTone)) { - return addEmojiReaction(reportID, reportAction.reportActionID, emoji, skinTone); - } + const reactionObject = lodashGet(existingReactions, [emoji.name]); + + // Only use skin tone if emoji supports it + const skinTone = emoji.types === undefined ? null : paramSkinTone; + + if (reactionObject && hasAccountIDEmojiReacted(currentUserAccountID, reactionObject.users, skinTone)) { + removeEmojiReaction(reportID, reportAction.reportActionID, emoji, skinTone, existingReactions); + return; } - return removeEmojiReaction(reportID, reportAction.reportActionID, emoji, skinTone, existingReactions); + addEmojiReaction(reportID, reportAction.reportActionID, emoji, skinTone); } /** From c8369e5e2a1ea47f4413a8771ee55e16a0c9325d Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Mon, 15 May 2023 09:00:03 -0600 Subject: [PATCH 19/68] style --- src/components/MagicCodeInput.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/components/MagicCodeInput.js b/src/components/MagicCodeInput.js index a9505e829c1e..7248e069a7d7 100644 --- a/src/components/MagicCodeInput.js +++ b/src/components/MagicCodeInput.js @@ -267,11 +267,7 @@ function MagicCodeInput(props) { key={index} style={[styles.w15]} > - + {decomposeString(props.value)[index] || ''} From d7221f69e05a064dac058c3d9ceac1d684a1bffb Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Mon, 15 May 2023 12:53:38 -0600 Subject: [PATCH 20/68] Remove code that still uses old format --- src/libs/actions/Report.js | 24 +++++---- .../report/ContextMenu/ContextMenuActions.js | 15 +----- src/pages/home/report/ReportActionItem.js | 50 +++---------------- 3 files changed, 24 insertions(+), 65 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index f1ee8aaf845b..91bc9398813d 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1448,12 +1448,11 @@ function hasAccountIDReacted(accountID, users, skinTone) { * @returns {boolean} */ function hasAccountIDEmojiReacted(accountID, users, skinTone) { - return ( - _.find(users, (userData, userAccountID) => { - const userReactedWithSkinTone = _.isNull(skinTone) ? true : lodashGet(userData, ['skinTones', skinTone]); - return userAccountID === accountID && userReactedWithSkinTone; - }) !== undefined - ); + const usersReaction = users[accountID]; + if (!usersReaction || !usersReaction.skinTones || !_.size(usersReaction.skinTones)) { + return false; + } + return Boolean(usersReaction.skinTones[skinTone]); } /** @@ -1579,7 +1578,7 @@ function addEmojiReaction(reportID, reportActionID, emoji, skinTone = preferredS users: { [currentUserAccountID]: { skinTones: { - [skinTone]: createdAt, + [skinTone || 'nothing']: createdAt, }, }, }, @@ -1596,6 +1595,7 @@ function addEmojiReaction(reportID, reportActionID, emoji, skinTone = preferredS createdAt, useEmojiReactions: true, }; + console.log('API.write() AddEmojiReaction', parameters, {optimisticData}); API.write('AddEmojiReaction', parameters, {optimisticData}); } @@ -1640,6 +1640,7 @@ function removeEmojiReaction(reportID, reportActionID, emoji, existingReactions) emojiCode: emoji.name, useEmojiReactions: true, }; + console.log('API.write() RemoveEmojiReaction', parameters, {optimisticData}); API.write('RemoveEmojiReaction', parameters, {optimisticData}); } @@ -1652,6 +1653,7 @@ function removeEmojiReaction(reportID, reportActionID, emoji, existingReactions) * @param {Number} [paramSkinTone] */ function toggleReaction(reportID, reportAction, emoji, paramSkinTone = preferredSkinTone) { + console.log('TIM toggleReaction(0)'); // This will get cleaned up as part of https://github.com/Expensify/App/issues/16506 once the old emoji // format is no longer being used const message = reportAction.message[0]; @@ -1676,17 +1678,21 @@ function toggleReaction(reportID, reportAction, emoji, paramSkinTone = preferred * @param {Number} [paramSkinTone] */ function toggleEmojiReaction(reportID, reportAction, emoji, existingReactions, paramSkinTone = preferredSkinTone) { + console.log('TIM toggleEmojiReaction(0)'); // This will get cleaned up as part of https://github.com/Expensify/App/issues/16506 once the old emoji // format is no longer being used const reactionObject = lodashGet(existingReactions, [emoji.name]); + // console.log('TIM toggleEmojiReaction', emoji.name, existingReactions, reactionObject); // Only use skin tone if emoji supports it - const skinTone = emoji.types === undefined ? null : paramSkinTone; + const skinTone = emoji.types === undefined ? 'nothing' : paramSkinTone; if (reactionObject && hasAccountIDEmojiReacted(currentUserAccountID, reactionObject.users, skinTone)) { - removeEmojiReaction(reportID, reportAction.reportActionID, emoji, skinTone, existingReactions); + // console.log('TIM removing emoji reaction', existingReactions); + removeEmojiReaction(reportID, reportAction.reportActionID, emoji, existingReactions); return; } + // console.log('TIM adding emoji reaction'); addEmojiReaction(reportID, reportAction.reportActionID, emoji, skinTone); } diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.js b/src/pages/home/report/ContextMenu/ContextMenuActions.js index 51e85b91de19..381e6c3d3404 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.js +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.js @@ -56,20 +56,7 @@ export default [ }; const onEmojiSelected = (emoji, existingReactions) => { - // This will get cleaned up as part of https://github.com/Expensify/App/issues/16506 once the old emoji - // format is no longer being used - - // Look for OLD FORMAT "reactions" on the report action's message - const reactions = _.get(reportAction, ['message', 0, 'reactions'], []); - const hasReactions = reactions.length > 0; - - // When there are no reactions in the OLD FORMAT, always use the NEW FORMAT - if (!hasReactions) { - Report.toggleEmojiReaction(reportID, reportAction, emoji, existingReactions); - } else { - // Only use the OLD when there are existing reactions in that format - Report.toggleReaction(reportID, reportAction, emoji); - } + Report.toggleEmojiReaction(reportID, reportAction, emoji, existingReactions); closeContextMenu(); }; diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 6c4944493cb9..4f1ef2a21f82 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -189,21 +189,7 @@ class ReportActionItem extends Component { } toggleReaction(emoji) { - // This will get cleaned up as part of https://github.com/Expensify/App/issues/16506 once the old emoji - // format is no longer being used - - // Look for OLD FORMAT "reactions" on the report action's message - const reactions = _.get(this.props, ['action', 'message', 0, 'reactions'], []); - const hasReactions = reactions.length > 0; - - // When there are no reactions in the OLD FORMAT, always use the NEW FORMAT - if (!hasReactions) { - Report.toggleEmojiReaction(this.props.report.reportID, this.props.action, emoji, this.props.emojiReactions); - return; - } - - // Only use the OLD FORMAT when there are existing reactions in that format - Report.toggleReaction(this.props.report.reportID, this.props.action, emoji); + Report.toggleEmojiReaction(this.props.report.reportID, this.props.action, emoji, this.props.emojiReactions); } /** @@ -279,17 +265,7 @@ class ReportActionItem extends Component { ); } - - // This will get cleaned up as part of https://github.com/Expensify/App/issues/16506 once the old emoji - // format is no longer being used - - // Support NEW FORMAT "emojiReactions" in their own collection - const emojiReactions = _.get(this.props, ['emojiReactions'], {}); - const hasEmojiReactions = _.size(emojiReactions) > 0; - - // Support OLD FORMAT "reactions" on the report action's message - const reactions = _.get(this.props, ['action', 'message', 0, 'reactions'], []); - const hasReactions = reactions.length > 0; + const emojiReactions = lodashGet(this.props, ['emojiReactions'], {}); const shouldDisplayThreadReplies = this.props.action.childCommenterCount && Permissions.canUseThreads(this.props.betas) && !ReportUtils.isThreadFirstChat(this.props.action, this.props.report.reportID); const oldestFourEmails = lodashGet(this.props.action, 'childOldestFourEmails', '').split(','); @@ -297,22 +273,12 @@ class ReportActionItem extends Component { return ( <> {children} - {hasEmojiReactions && ( - - - - )} - {!hasEmojiReactions && hasReactions && ( - - - - )} + + + {shouldDisplayThreadReplies && ( Date: Mon, 15 May 2023 12:55:18 -0600 Subject: [PATCH 21/68] Remove more code referencing old reactions --- src/libs/actions/Report.js | 130 ------------------------------------- 1 file changed, 130 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 91bc9398813d..7dc3246e0a3c 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1455,109 +1455,6 @@ function hasAccountIDEmojiReacted(accountID, users, skinTone) { return Boolean(usersReaction.skinTones[skinTone]); } -/** - * Adds a reaction to the report action. - * Uses the OLD FORMAT for "reactions" - * @param {String} reportID - * @param {Object} originalReportAction - * @param {{ name: string, code: string, types: string[] }} emoji - * @param {number} [skinTone] Optional. - */ -function addReaction(reportID, originalReportAction, emoji, skinTone = preferredSkinTone) { - const message = originalReportAction.message[0]; - let reactionObject = message.reactions && _.find(message.reactions, (reaction) => reaction.emoji === emoji.name); - const needToInsertReactionObject = !reactionObject; - if (needToInsertReactionObject) { - reactionObject = { - emoji: emoji.name, - users: [], - }; - } else { - // Make a copy of the reaction object so that we can modify it without mutating the original - reactionObject = {...reactionObject}; - } - - if (hasAccountIDReacted(currentUserAccountID, reactionObject.users, skinTone)) { - return; - } - - reactionObject.users = [...reactionObject.users, {accountID: currentUserAccountID, skinTone}]; - let updatedReactions = [...(message.reactions || [])]; - if (needToInsertReactionObject) { - updatedReactions = [...updatedReactions, reactionObject]; - } else { - updatedReactions = _.map(updatedReactions, (reaction) => (reaction.emoji === emoji.name ? reactionObject : reaction)); - } - - const updatedMessage = { - ...message, - reactions: updatedReactions, - }; - - // Optimistically update the reportAction with the reaction - const optimisticData = getOptimisticDataForReportActionUpdate(originalReportAction, updatedMessage, reportID); - - const parameters = { - reportID, - skinTone, - emojiCode: emoji.name, - sequenceNumber: originalReportAction.sequenceNumber, - reportActionID: originalReportAction.reportActionID, - }; - API.write('AddEmojiReaction', parameters, {optimisticData}); -} - -/** - * Removes a reaction to the report action. - * Uses the OLD FORMAT for "reactions" - * @param {String} reportID - * @param {Object} originalReportAction - * @param {{ name: string, code: string, types: string[] }} emoji - */ -function removeReaction(reportID, originalReportAction, emoji) { - const message = originalReportAction.message[0]; - const reactionObject = message.reactions && _.find(message.reactions, (reaction) => reaction.emoji === emoji.name); - if (!reactionObject) { - return; - } - - const updatedReactionObject = { - ...reactionObject, - }; - updatedReactionObject.users = _.filter(reactionObject.users, (sender) => sender.accountID !== currentUserAccountID); - const updatedReactions = _.filter( - // Replace the reaction object either with the updated one or null if there are no users - _.map(message.reactions, (reaction) => { - if (reaction.emoji === emoji.name) { - if (updatedReactionObject.users.length === 0) { - return null; - } - return updatedReactionObject; - } - return reaction; - }), - - // Remove any null reactions - (reportObject) => reportObject !== null, - ); - - const updatedMessage = { - ...message, - reactions: updatedReactions, - }; - - // Optimistically update the reportAction with the reaction - const optimisticData = getOptimisticDataForReportActionUpdate(originalReportAction, updatedMessage, reportID); - - const parameters = { - reportID, - sequenceNumber: originalReportAction.sequenceNumber, - reportActionID: originalReportAction.reportActionID, - emojiCode: emoji.name, - }; - API.write('RemoveEmojiReaction', parameters, {optimisticData}); -} - /** * Adds a reaction to the report action. * Uses the NEW FORMAT for "emojiReactions" @@ -1595,7 +1492,6 @@ function addEmojiReaction(reportID, reportActionID, emoji, skinTone = preferredS createdAt, useEmojiReactions: true, }; - console.log('API.write() AddEmojiReaction', parameters, {optimisticData}); API.write('AddEmojiReaction', parameters, {optimisticData}); } @@ -1640,34 +1536,9 @@ function removeEmojiReaction(reportID, reportActionID, emoji, existingReactions) emojiCode: emoji.name, useEmojiReactions: true, }; - console.log('API.write() RemoveEmojiReaction', parameters, {optimisticData}); API.write('RemoveEmojiReaction', parameters, {optimisticData}); } -/** - * Calls either addReaction or removeEmojiReaction depending on if the current user has reacted to the report action. - * Uses the OLD FORMAT for "reactions" - * @param {String} reportID - * @param {Object} reportAction - * @param {{ name: string, code: string, types: string[] }} emoji - * @param {Number} [paramSkinTone] - */ -function toggleReaction(reportID, reportAction, emoji, paramSkinTone = preferredSkinTone) { - console.log('TIM toggleReaction(0)'); - // This will get cleaned up as part of https://github.com/Expensify/App/issues/16506 once the old emoji - // format is no longer being used - const message = reportAction.message[0]; - const reactionObject = message.reactions && _.find(message.reactions, (reaction) => reaction.emoji === emoji.name); - - // Only use skin tone if emoji supports it - const skinTone = emoji.types === undefined ? null : paramSkinTone; - if (reactionObject && hasAccountIDReacted(currentUserAccountID, reactionObject.users, skinTone)) { - removeReaction(reportID, reportAction.reportActionID, emoji, skinTone); - return; - } - addReaction(reportID, reportAction.reportActionID, emoji, skinTone); -} - /** * Calls either addReaction or removeEmojiReaction depending on if the current user has reacted to the report action. * Uses the NEW FORMAT for "emojiReactions" @@ -1750,7 +1621,6 @@ export { clearIOUError, subscribeToNewActionEvent, showReportActionNotification, - toggleReaction, toggleEmojiReaction, hasAccountIDReacted, shouldShowReportActionNotification, From 11c53802fa4e10e770d29b88b71f27e647b964a4 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Mon, 15 May 2023 12:55:57 -0600 Subject: [PATCH 22/68] Remove unused component --- .../Reactions/ReportActionItemReactions.js | 124 ------------------ src/pages/home/report/ReportActionItem.js | 1 - 2 files changed, 125 deletions(-) delete mode 100644 src/components/Reactions/ReportActionItemReactions.js diff --git a/src/components/Reactions/ReportActionItemReactions.js b/src/components/Reactions/ReportActionItemReactions.js deleted file mode 100644 index 314f00c68280..000000000000 --- a/src/components/Reactions/ReportActionItemReactions.js +++ /dev/null @@ -1,124 +0,0 @@ -import React, {useRef} from 'react'; -import _ from 'underscore'; -import {View} from 'react-native'; -import PropTypes from 'prop-types'; -import styles from '../../styles/styles'; -import EmojiReactionBubble from './EmojiReactionBubble'; -import emojis from '../../../assets/emojis'; -import AddReactionBubble from './AddReactionBubble'; -import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '../withCurrentUserPersonalDetails'; -import getPreferredEmojiCode from './getPreferredEmojiCode'; -import * as PersonalDetailsUtils from '../../libs/PersonalDetailsUtils'; -import * as Report from '../../libs/actions/Report'; -import * as ReactionList from '../../pages/home/report/ReactionList/ReactionList'; -import Tooltip from '../Tooltip'; -import ReactionTooltipContent from './ReactionTooltipContent'; - -/** - * Given an emoji object and a list of senders it will return an - * array of emoji codes, that represents all used variations of the - * emoji. - * @param {{ name: string, code: string, types: string[] }} emoji - * @param {Array} users - * @return {string[]} - * */ -const getUniqueEmojiCodes = (emoji, users) => { - const emojiCodes = []; - _.forEach(users, (user) => { - const emojiCode = getPreferredEmojiCode(emoji, user.skinTone); - - if (emojiCode && !emojiCodes.includes(emojiCode)) { - emojiCodes.push(emojiCode); - } - }); - return emojiCodes; -}; - -const propTypes = { - /** - * An array of objects containing the reaction data. - * The shape of a reaction looks like this: - * - * "reactionName": { - * emoji: string, - * users: { - * accountID: string, - * skinTone: number, - * }[] - * } - */ - // eslint-disable-next-line react/forbid-prop-types - reactions: PropTypes.arrayOf(PropTypes.object).isRequired, - - /** - * Function to call when the user presses on an emoji. - * This can also be an emoji the user already reacted with, - * hence this function asks to toggle the reaction by emoji. - */ - toggleReaction: PropTypes.func.isRequired, - - ...withCurrentUserPersonalDetailsPropTypes, -}; - -const defaultProps = { - ...withCurrentUserPersonalDetailsDefaultProps, -}; - -const ReportActionItemReactions = (props) => { - const popoverReactionListAnchor = useRef(null); - const reactionsWithCount = _.filter(props.reactions, (reaction) => reaction.users.length > 0); - - return ( - - {_.map(reactionsWithCount, (reaction) => { - const reactionCount = reaction.users.length; - const reactionUsers = _.map(reaction.users, (sender) => sender.accountID.toString()); - const emoji = _.find(emojis, (e) => e.name === reaction.emoji); - const emojiCodes = getUniqueEmojiCodes(emoji, reaction.users); - const hasUserReacted = Report.hasAccountIDReacted(props.currentUserPersonalDetails.accountID, reactionUsers); - - const onPress = () => { - props.toggleReaction(emoji); - }; - const onReactionListOpen = (event) => { - const users = PersonalDetailsUtils.getPersonalDetailsByIDs(reactionUsers, props.currentUserPersonalDetails.accountID); - ReactionList.showReactionList(event, popoverReactionListAnchor.current, users, reaction.emoji, emojiCodes, reactionCount, hasUserReacted); - }; - - return ( - ( - - )} - renderTooltipContentKey={[...reactionUsers, ...emojiCodes]} - key={reaction.emoji} - > - - - ); - })} - {reactionsWithCount.length > 0 && } - - ); -}; - -ReportActionItemReactions.displayName = 'ReportActionItemReactions'; -ReportActionItemReactions.propTypes = propTypes; -ReportActionItemReactions.defaultProps = defaultProps; -export default withCurrentUserPersonalDetails(ReportActionItemReactions); diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 4f1ef2a21f82..17de967054b9 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -38,7 +38,6 @@ import reportPropTypes from '../../reportPropTypes'; import {ShowContextMenuContext} from '../../../components/ShowContextMenuContext'; import focusTextInputAfterAnimation from '../../../libs/focusTextInputAfterAnimation'; import ChronosOOOListActions from '../../../components/ReportActionItem/ChronosOOOListActions'; -import ReportActionItemReactions from '../../../components/Reactions/ReportActionItemReactions'; import ReportActionItemEmojiReactions from '../../../components/Reactions/ReportActionItemEmojiReactions'; import * as Report from '../../../libs/actions/Report'; import withLocalize from '../../../components/withLocalize'; From 2970f7916e3caa0602965cdfa4f4005128b3ff26 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Mon, 15 May 2023 14:07:16 -0600 Subject: [PATCH 23/68] Get tests working for toggling reactions --- src/libs/actions/Report.js | 32 ++++++----- tests/actions/ReportTest.js | 103 +++++++++++++++++++----------------- 2 files changed, 72 insertions(+), 63 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 7dc3246e0a3c..c68f3831de98 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1520,15 +1520,24 @@ function removeEmojiReaction(reportID, reportActionID, emoji, existingReactions) updatedReactionObject = null; } - const optimisticData = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportID}${reportActionID}`, - value: { - [emoji.name]: updatedReactionObject, - }, - }, - ]; + // If the reaction object is being removed, then Onxy.set() must be used because there is no other way to set the value of a key to {} + const optimisticData = updatedReactionObject + ? [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportID}${reportActionID}`, + value: { + [emoji.name]: updatedReactionObject, + }, + }, + ] + : [ + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportID}${reportActionID}`, + value: null, + }, + ]; const parameters = { reportID, @@ -1549,21 +1558,18 @@ function removeEmojiReaction(reportID, reportActionID, emoji, existingReactions) * @param {Number} [paramSkinTone] */ function toggleEmojiReaction(reportID, reportAction, emoji, existingReactions, paramSkinTone = preferredSkinTone) { - console.log('TIM toggleEmojiReaction(0)'); // This will get cleaned up as part of https://github.com/Expensify/App/issues/16506 once the old emoji // format is no longer being used const reactionObject = lodashGet(existingReactions, [emoji.name]); - // console.log('TIM toggleEmojiReaction', emoji.name, existingReactions, reactionObject); // Only use skin tone if emoji supports it const skinTone = emoji.types === undefined ? 'nothing' : paramSkinTone; if (reactionObject && hasAccountIDEmojiReacted(currentUserAccountID, reactionObject.users, skinTone)) { - // console.log('TIM removing emoji reaction', existingReactions); removeEmojiReaction(reportID, reportAction.reportActionID, emoji, existingReactions); return; } - // console.log('TIM adding emoji reaction'); + addEmojiReaction(reportID, reportAction.reportActionID, emoji, skinTone); } diff --git a/tests/actions/ReportTest.js b/tests/actions/ReportTest.js index 11e73b232a0f..73c4491b71fa 100644 --- a/tests/actions/ReportTest.js +++ b/tests/actions/ReportTest.js @@ -529,14 +529,23 @@ describe('actions/Report', () => { const EMOJI = { code: EMOJI_CODE, name: EMOJI_NAME, + types: ['👍🏿', '👍🏾', '👍🏽', '👍🏼', '👍🏻'], }; let reportActions; - Onyx.connect({ key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${REPORT_ID}`, callback: (val) => (reportActions = val), }); + const reportActionsReactions = {}; + Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS, + callback: (val, key) => { + reportActionsReactions[key] = val; + }, + }); + let reportAction; + let reportActionID; // Set up Onyx with some test user data return TestHelper.signInWithTestUser(TEST_USER_ACCOUNT_ID, TEST_USER_LOGIN) @@ -552,76 +561,70 @@ describe('actions/Report', () => { return waitForPromisesToResolve(); }) .then(() => { - const resultAction = _.first(_.values(reportActions)); + reportAction = _.first(_.values(reportActions)); + reportActionID = reportAction.reportActionID; // Add a reaction to the comment - Report.toggleReaction(REPORT_ID, resultAction, EMOJI); + Report.toggleEmojiReaction(REPORT_ID, reportAction, EMOJI); return waitForPromisesToResolve(); }) .then(() => { - const resultAction = _.first(_.values(reportActions)); + // Expect the reaction to exist in the reportActionsReactions collection + expect(reportActionsReactions).toHaveProperty(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`); - // Expect to have the reaction on the message - expect(resultAction.message[0].reactions).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - emoji: EMOJI_NAME, - users: expect.arrayContaining([expect.objectContaining({accountID: TEST_USER_ACCOUNT_ID})]), - }), - ]), - ); + // Expect the reaction to have the emoji on it + const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`]; + expect(reportActionReaction).toHaveProperty(EMOJI.name); + + // Expect the emoji to have the user accountID + const reportActionReactionEmoji = reportActionReaction[EMOJI.name]; + expect(reportActionReactionEmoji.users).toHaveProperty(`${TEST_USER_ACCOUNT_ID}`); // Now we remove the reaction - Report.removeEmojiReaction(REPORT_ID, resultAction, EMOJI); + Report.toggleEmojiReaction(REPORT_ID, reportAction, EMOJI, reportActionReaction); return waitForPromisesToResolve(); }) .then(() => { - // Expect that the reaction is removed - const resultAction = _.first(_.values(reportActions)); - - expect(resultAction.message[0].reactions).toHaveLength(0); + // Expect the reaction to no longer exist + expect(reportActionsReactions).toHaveProperty(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`); + const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`]; + expect(reportActionReaction).toBeNull(); }) .then(() => { - const resultAction = _.first(_.values(reportActions)); - - // Add the reaction to the comment, but two times with different variations - Report.toggleReaction(REPORT_ID, resultAction, EMOJI); + // Add the same reaction to the same report action with a different skintone + Report.toggleEmojiReaction(REPORT_ID, reportAction, EMOJI); return waitForPromisesToResolve() .then(() => { - const updatedResultAction = _.first(_.values(reportActions)); - Report.toggleReaction(REPORT_ID, updatedResultAction, EMOJI, 2); + const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`]; + Report.toggleEmojiReaction(REPORT_ID, reportAction, EMOJI, reportActionReaction, EMOJI_SKIN_TONE); return waitForPromisesToResolve(); }) .then(() => { - const updatedResultAction = _.first(_.values(reportActions)); - - // Expect to have the reaction on the message - expect(updatedResultAction.message[0].reactions).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - emoji: EMOJI_NAME, - users: expect.arrayContaining([ - expect.objectContaining({ - accountID: TEST_USER_ACCOUNT_ID, - }), - expect.objectContaining({ - accountID: TEST_USER_ACCOUNT_ID, - skinTone: EMOJI_SKIN_TONE, - }), - ]), - }), - ]), - ); + // Expect the reaction to exist in the reportActionsReactions collection + expect(reportActionsReactions).toHaveProperty(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`); + + // Expect the reaction to have the emoji on it + const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`]; + expect(reportActionReaction).toHaveProperty(EMOJI.name); + + // Expect the emoji to have the user accountID + const reportActionReactionEmoji = reportActionReaction[EMOJI.name]; + expect(reportActionReactionEmoji.users).toHaveProperty(`${TEST_USER_ACCOUNT_ID}`); + + // Expect two different skintone reactions + const reportActionReactionEmojiUserSkinTones = reportActionReactionEmoji.users[TEST_USER_ACCOUNT_ID].skinTones; + expect(reportActionReactionEmojiUserSkinTones).toHaveProperty('-1'); + expect(reportActionReactionEmojiUserSkinTones).toHaveProperty('2'); // Now we remove the reaction, and expect that both variations are removed - Report.removeEmojiReaction(REPORT_ID, updatedResultAction, EMOJI); + Report.toggleEmojiReaction(REPORT_ID, reportAction, EMOJI, reportActionReaction); return waitForPromisesToResolve(); }) .then(() => { - // Expect that the reaction is removed - const updatedResultAction = _.first(_.values(reportActions)); - - expect(updatedResultAction.message[0].reactions).toHaveLength(0); + // Expect the reaction to no longer exist + expect(reportActionsReactions).toHaveProperty(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`); + const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`]; + expect(reportActionReaction).toBeNull(); }); }); }); @@ -663,7 +666,7 @@ describe('actions/Report', () => { const resultAction = _.first(_.values(reportActions)); // Add a reaction to the comment - Report.toggleReaction(REPORT_ID, resultAction, EMOJI); + Report.toggleEmojiReaction(REPORT_ID, resultAction, EMOJI); return waitForPromisesToResolve(); }) .then(() => { @@ -672,7 +675,7 @@ describe('actions/Report', () => { // Now we toggle the reaction while the skin tone has changed. // As the emoji doesn't support skin tones, the emoji // should get removed instead of added again. - Report.toggleReaction(REPORT_ID, resultAction, EMOJI, 2); + Report.toggleEmojiReaction(REPORT_ID, resultAction, EMOJI, 2); return waitForPromisesToResolve(); }) .then(() => { From c17e2ecebe6da58be4a482b56153913afc321d67 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Mon, 15 May 2023 14:13:30 -0600 Subject: [PATCH 24/68] Update tests for adding reactions --- tests/actions/ReportTest.js | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/tests/actions/ReportTest.js b/tests/actions/ReportTest.js index 73c4491b71fa..3dcda4217555 100644 --- a/tests/actions/ReportTest.js +++ b/tests/actions/ReportTest.js @@ -643,11 +643,19 @@ describe('actions/Report', () => { }; let reportActions; - Onyx.connect({ key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${REPORT_ID}`, callback: (val) => (reportActions = val), }); + const reportActionsReactions = {}; + Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS, + callback: (val, key) => { + reportActionsReactions[key] = val; + }, + }); + let reportAction; + let reportActionID; // Set up Onyx with some test user data return TestHelper.signInWithTestUser(TEST_USER_ACCOUNT_ID, TEST_USER_LOGIN) @@ -663,26 +671,24 @@ describe('actions/Report', () => { return waitForPromisesToResolve(); }) .then(() => { - const resultAction = _.first(_.values(reportActions)); + reportAction = _.first(_.values(reportActions)); + reportActionID = reportAction.reportActionID; // Add a reaction to the comment - Report.toggleEmojiReaction(REPORT_ID, resultAction, EMOJI); + Report.toggleEmojiReaction(REPORT_ID, reportAction, EMOJI); return waitForPromisesToResolve(); }) .then(() => { - const resultAction = _.first(_.values(reportActions)); - - // Now we toggle the reaction while the skin tone has changed. - // As the emoji doesn't support skin tones, the emoji - // should get removed instead of added again. - Report.toggleEmojiReaction(REPORT_ID, resultAction, EMOJI, 2); + // Now we toggle the reaction while the skin tone has changed. Since the emoji doesn't support skin tones, the emoji should get removed instead of added again. + const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`]; + Report.toggleEmojiReaction(REPORT_ID, reportAction, EMOJI, reportActionReaction, 2); return waitForPromisesToResolve(); }) .then(() => { - const resultAction = _.first(_.values(reportActions)); - - // Expect to have the reaction on the message - expect(resultAction.message[0].reactions).toHaveLength(0); + // Expect the reaction to no longer exist + expect(reportActionsReactions).toHaveProperty(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`); + const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`]; + expect(reportActionReaction).toBeNull(); }); }); }); From b8add232e8a2e12524d3903fc23348d5597da701 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Mon, 15 May 2023 16:37:24 -0600 Subject: [PATCH 25/68] Get emojis removing properly --- .../ReportActionItemEmojiReactions.js | 13 ++++-- src/libs/actions/Report.js | 44 +++++++------------ tests/actions/ReportTest.js | 6 +-- 3 files changed, 27 insertions(+), 36 deletions(-) diff --git a/src/components/Reactions/ReportActionItemEmojiReactions.js b/src/components/Reactions/ReportActionItemEmojiReactions.js index 26c64af6b250..fdfd9cea0317 100644 --- a/src/components/Reactions/ReportActionItemEmojiReactions.js +++ b/src/components/Reactions/ReportActionItemEmojiReactions.js @@ -2,6 +2,7 @@ import React, {useRef} from 'react'; import _ from 'underscore'; import {View} from 'react-native'; import PropTypes from 'prop-types'; +import lodashGet from 'lodash/get'; import styles from '../../styles/styles'; import EmojiReactionBubble from './EmojiReactionBubble'; import emojis from '../../../assets/emojis'; @@ -25,7 +26,7 @@ import * as ReactionList from '../../pages/home/report/ReactionList/ReactionList const getUniqueEmojiCodes = (emojiAsset, users) => { const uniqueEmojiCodes = []; _.each(users, (userSkinTones) => { - _.each(userSkinTones.skinTones, (createdAt, skinTone) => { + _.each(lodashGet(userSkinTones, 'skinTones'), (createdAt, skinTone) => { const emojiCode = getPreferredEmojiCode(emojiAsset, skinTone); if (emojiCode && !uniqueEmojiCodes.includes(emojiCode)) { uniqueEmojiCodes.push(emojiCode); @@ -91,11 +92,15 @@ const ReportActionItemEmojiReactions = (props) => { style={[styles.flexRow, styles.flexWrap, styles.gap1, styles.mt2]} > {_.map(props.emojiReactions, (reaction, reactionEmoji) => { - const reactionCount = _.size(reaction.users); + const usersWithReactions = _.filter(reaction.users, (userData) => userData); + const reactionCount = _.size(usersWithReactions); + if (!reactionCount) { + return null; + } const emojiAsset = _.find(emojis, (emoji) => emoji.name === reactionEmoji); const emojiCodes = getUniqueEmojiCodes(emojiAsset, reaction.users); + const hasUserReacted = Report.hasAccountIDEmojiReacted(props.currentUserPersonalDetails.accountID, reaction.users); const reactionUsers = _.keys(reaction.users); - const hasUserReacted = Report.hasAccountIDReacted(props.currentUserPersonalDetails.accountID, reactionUsers); const onPress = () => { props.toggleReaction(emojiAsset); @@ -128,7 +133,7 @@ const ReportActionItemEmojiReactions = (props) => { ); })} - {_.size(props.reactions) > 0 && } + {_.size(props.emojiReactions) > 0 && } ); }; diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index c68f3831de98..0720a4879eb2 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1448,6 +1448,9 @@ function hasAccountIDReacted(accountID, users, skinTone) { * @returns {boolean} */ function hasAccountIDEmojiReacted(accountID, users, skinTone) { + if (!skinTone) { + return Boolean(users[accountID]); + } const usersReaction = users[accountID]; if (!usersReaction || !usersReaction.skinTones || !_.size(usersReaction.skinTones)) { return false; @@ -1509,35 +1512,17 @@ function removeEmojiReaction(reportID, reportActionID, emoji, existingReactions) return; } - // Make a copy of the original reaction object so that we don't update anything in existingReactions which could be unexpected - let updatedReactionObject = {...reactionObject}; - - // Remove the current user from the list of users who have used this emoji - updatedReactionObject.users = _.omit(reactionObject.users, currentUserAccountID); - - // If there are no more users, then the entire reactions needs to be set to null so that it can be removed from Onyx - if (!_.size(updatedReactionObject.users)) { - updatedReactionObject = null; - } - - // If the reaction object is being removed, then Onxy.set() must be used because there is no other way to set the value of a key to {} - const optimisticData = updatedReactionObject - ? [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportID}${reportActionID}`, - value: { - [emoji.name]: updatedReactionObject, - }, - }, - ] - : [ - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportID}${reportActionID}`, - value: null, - }, - ]; + const optimisticData = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportID}${reportActionID}`, + value: { + [emoji.name]: { + currentUserAccountID: null, + }, + }, + }, + ]; const parameters = { reportID, @@ -1629,5 +1614,6 @@ export { showReportActionNotification, toggleEmojiReaction, hasAccountIDReacted, + hasAccountIDEmojiReacted, shouldShowReportActionNotification, }; diff --git a/tests/actions/ReportTest.js b/tests/actions/ReportTest.js index 3dcda4217555..ab002bb4e4d9 100644 --- a/tests/actions/ReportTest.js +++ b/tests/actions/ReportTest.js @@ -588,7 +588,7 @@ describe('actions/Report', () => { // Expect the reaction to no longer exist expect(reportActionsReactions).toHaveProperty(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`); const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`]; - expect(reportActionReaction).toBeNull(); + expect(reportActionReaction).not.toHaveProperty(EMOJI.name); }) .then(() => { // Add the same reaction to the same report action with a different skintone @@ -624,7 +624,7 @@ describe('actions/Report', () => { // Expect the reaction to no longer exist expect(reportActionsReactions).toHaveProperty(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`); const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`]; - expect(reportActionReaction).toBeNull(); + expect(reportActionReaction).not.toHaveProperty(EMOJI.name); }); }); }); @@ -688,7 +688,7 @@ describe('actions/Report', () => { // Expect the reaction to no longer exist expect(reportActionsReactions).toHaveProperty(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`); const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`]; - expect(reportActionReaction).toBeNull(); + expect(reportActionReaction).not.toHaveProperty(EMOJI.name); }); }); }); From cb9c262d8ae2dc676fbb5b38b75b308b06b1e22a Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Tue, 16 May 2023 13:32:08 -0600 Subject: [PATCH 26/68] Fix optimistic removal of emoji --- .../Reactions/ReportActionItemEmojiReactions.js | 4 +++- src/libs/actions/Report.js | 11 ++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/components/Reactions/ReportActionItemEmojiReactions.js b/src/components/Reactions/ReportActionItemEmojiReactions.js index fdfd9cea0317..74577dd00607 100644 --- a/src/components/Reactions/ReportActionItemEmojiReactions.js +++ b/src/components/Reactions/ReportActionItemEmojiReactions.js @@ -85,6 +85,7 @@ const defaultProps = { const ReportActionItemEmojiReactions = (props) => { const popoverReactionListAnchor = useRef(null); + let totalReactionCount = 0; // @TODO: need to sort everything so that emojis and users are in the order they were added return ( { if (!reactionCount) { return null; } + totalReactionCount += reactionCount; const emojiAsset = _.find(emojis, (emoji) => emoji.name === reactionEmoji); const emojiCodes = getUniqueEmojiCodes(emojiAsset, reaction.users); const hasUserReacted = Report.hasAccountIDEmojiReacted(props.currentUserPersonalDetails.accountID, reaction.users); @@ -133,7 +135,7 @@ const ReportActionItemEmojiReactions = (props) => { ); })} - {_.size(props.emojiReactions) > 0 && } + {totalReactionCount > 0 && } ); }; diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 90d79c3b02b9..a7ae1cee36b1 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1516,18 +1516,15 @@ function addEmojiReaction(reportID, reportActionID, emoji, skinTone = preferredS * @param {Object} existingReactions */ function removeEmojiReaction(reportID, reportActionID, emoji, existingReactions) { - const reactionObject = existingReactions[emoji.name]; - if (!reactionObject) { - return; - } - const optimisticData = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportID}${reportActionID}`, value: { [emoji.name]: { - currentUserAccountID: null, + users: { + [currentUserAccountID]: null, + }, }, }, }, @@ -1560,7 +1557,7 @@ function toggleEmojiReaction(reportID, reportAction, emoji, existingReactions, p const skinTone = emoji.types === undefined ? 'nothing' : paramSkinTone; if (reactionObject && hasAccountIDEmojiReacted(currentUserAccountID, reactionObject.users, skinTone)) { - removeEmojiReaction(reportID, reportAction.reportActionID, emoji, existingReactions); + removeEmojiReaction(reportID, reportAction.reportActionID, emoji); return; } From f35e7bcdc6cbb03227f966e149c5660bb365baaf Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 17 May 2023 09:48:49 -0600 Subject: [PATCH 27/68] WIP on preserving order of reactions --- .../Reactions/ReactionTooltipContent.js | 1 + .../ReportActionItemEmojiReactions.js | 26 ++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/components/Reactions/ReactionTooltipContent.js b/src/components/Reactions/ReactionTooltipContent.js index 42c2a1f0fa61..1d4a04f3ad66 100644 --- a/src/components/Reactions/ReactionTooltipContent.js +++ b/src/components/Reactions/ReactionTooltipContent.js @@ -32,6 +32,7 @@ const defaultProps = { }; const ReactionTooltipContent = (props) => { + console.log(props.accountIDs); const users = useMemo( () => PersonalDetailsUtils.getPersonalDetailsByIDs(props.accountIDs, props.currentUserPersonalDetails.accountID, true), [props.currentUserPersonalDetails.accountID, props.accountIDs], diff --git a/src/components/Reactions/ReportActionItemEmojiReactions.js b/src/components/Reactions/ReportActionItemEmojiReactions.js index 74577dd00607..d993314f9027 100644 --- a/src/components/Reactions/ReportActionItemEmojiReactions.js +++ b/src/components/Reactions/ReportActionItemEmojiReactions.js @@ -87,19 +87,37 @@ const ReportActionItemEmojiReactions = (props) => { const popoverReactionListAnchor = useRef(null); let totalReactionCount = 0; // @TODO: need to sort everything so that emojis and users are in the order they were added + + // each emoji is sorted by the oldest timestamp of user reactions + const sortedReactions = _.sortBy(props.emojiReactions, (emojiReaction, emojiName) => { + emojiReaction.emojiName = emojiName; + return _.reduce( + emojiReaction.users, + (oldestTimestamp, userData) => { + // userData can be null in the case where they have removed all their reactions of this emoji + if (!userData) { + return oldestTimestamp; + } + const oldestUserReaction = _.chain(userData.skinTones).values().flatten().sort().first().value(); + return oldestUserReaction < oldestTimestamp ? oldestUserReaction : oldestTimestamp; + }, + '', + ); + }); return ( - {_.map(props.emojiReactions, (reaction, reactionEmoji) => { + {_.map(sortedReactions, (reaction) => { + const reactionEmojiName = reaction.emojiName; const usersWithReactions = _.filter(reaction.users, (userData) => userData); const reactionCount = _.size(usersWithReactions); if (!reactionCount) { return null; } totalReactionCount += reactionCount; - const emojiAsset = _.find(emojis, (emoji) => emoji.name === reactionEmoji); + const emojiAsset = _.find(emojis, (emoji) => emoji.name === reactionEmojiName); const emojiCodes = getUniqueEmojiCodes(emojiAsset, reaction.users); const hasUserReacted = Report.hasAccountIDEmojiReacted(props.currentUserPersonalDetails.accountID, reaction.users); const reactionUsers = _.keys(reaction.users); @@ -116,13 +134,13 @@ const ReportActionItemEmojiReactions = (props) => { ( )} - key={reactionEmoji} + key={reactionEmojiName} > Date: Wed, 17 May 2023 15:11:36 -0600 Subject: [PATCH 28/68] Apply changes from main --- src/components/Reactions/ReportActionItemEmojiReactions.js | 7 ++++--- src/pages/home/report/ReactionList/PopoverReactionList.js | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/Reactions/ReportActionItemEmojiReactions.js b/src/components/Reactions/ReportActionItemEmojiReactions.js index d993314f9027..44216da5ca74 100644 --- a/src/components/Reactions/ReportActionItemEmojiReactions.js +++ b/src/components/Reactions/ReportActionItemEmojiReactions.js @@ -12,7 +12,6 @@ import getPreferredEmojiCode from './getPreferredEmojiCode'; import Tooltip from '../Tooltip'; import ReactionTooltipContent from './ReactionTooltipContent'; import * as Report from '../../libs/actions/Report'; -import * as PersonalDetailsUtils from '../../libs/PersonalDetailsUtils'; import * as ReactionList from '../../pages/home/report/ReactionList/ReactionList'; /** @@ -67,6 +66,9 @@ const propTypes = { }), ), + /** The ID of the reportAction. It is the string representation of the a 64-bit integer. */ + reportActionID: PropTypes.string.isRequired, + /** * Function to call when the user presses on an emoji. * This can also be an emoji the user already reacted with, @@ -126,8 +128,7 @@ const ReportActionItemEmojiReactions = (props) => { props.toggleReaction(emojiAsset); }; const onReactionListOpen = (event) => { - const users = PersonalDetailsUtils.getPersonalDetailsByIDs(reactionUsers, props.currentUserPersonalDetails.accountID); - ReactionList.showReactionList(event, popoverReactionListAnchor.current, users, reaction.emoji, emojiCodes, reactionCount, hasUserReacted); + ReactionList.showReactionList(event, popoverReactionListAnchor.current, reaction.emoji, props.reportActionID); }; return ( diff --git a/src/pages/home/report/ReactionList/PopoverReactionList.js b/src/pages/home/report/ReactionList/PopoverReactionList.js index 7538c6f44cad..e59209e3832e 100644 --- a/src/pages/home/report/ReactionList/PopoverReactionList.js +++ b/src/pages/home/report/ReactionList/PopoverReactionList.js @@ -193,7 +193,7 @@ class PopoverReactionList extends React.Component { const reactionUsers = lodashMap(selectedReaction.users, (sender) => sender.accountID.toString()); const emoji = lodashFind(emojis, (e) => e.name === selectedReaction.emoji); const emojiCodes = getUniqueEmojiCodes(emoji, selectedReaction.users); - const hasUserReacted = Report.hasAccountIDReacted(this.props.currentUserPersonalDetails.accountID, reactionUsers); + const hasUserReacted = Report.hasAccountIDEmojiReacted(this.props.currentUserPersonalDetails.accountID, reactionUsers); const users = PersonalDetailsUtils.getPersonalDetailsByIDs(reactionUsers); return { emojiCount, From 7d5eca7f7c9744307c9818d6df5111696848bef6 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 17 May 2023 15:16:11 -0600 Subject: [PATCH 29/68] Remove unused method --- src/libs/actions/Report.js | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index a7ae1cee36b1..4dbfd9672a67 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1425,29 +1425,6 @@ function getOptimisticDataForReportActionUpdate(originalReportAction, message, r ]; } -/** - * Returns true if the accountID has reacted to the report action (with the given skin tone). - * Uses the OLD FORMAT for "reactions" - * @param {String} accountID - * @param {Array} users - * @param {Number} [skinTone] - * @returns {boolean} - */ -function hasAccountIDReacted(accountID, users, skinTone) { - return ( - _.find(users, (user) => { - let userAccountID; - if (typeof user === 'object') { - userAccountID = `${user.accountID}`; - } else { - userAccountID = `${user}`; - } - - return userAccountID === `${accountID}` && (skinTone == null ? true : user.skinTone === skinTone); - }) !== undefined - ); -} - /** * Returns true if the accountID has reacted to the report action (with the given skin tone). * Uses the NEW FORMAT for "emojiReactions" @@ -1619,7 +1596,6 @@ export { subscribeToNewActionEvent, showReportActionNotification, toggleEmojiReaction, - hasAccountIDReacted, hasAccountIDEmojiReacted, shouldShowReportActionNotification, }; From 28b31c97b0aa04f0e2a69dc2dc2b96656047e6f7 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 17 May 2023 16:20:50 -0600 Subject: [PATCH 30/68] Sort the reactions by timestamp so that they are always in the same order --- .../ReportActionItemEmojiReactions.js | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/components/Reactions/ReportActionItemEmojiReactions.js b/src/components/Reactions/ReportActionItemEmojiReactions.js index 44216da5ca74..31bcf460b527 100644 --- a/src/components/Reactions/ReportActionItemEmojiReactions.js +++ b/src/components/Reactions/ReportActionItemEmojiReactions.js @@ -88,23 +88,26 @@ const defaultProps = { const ReportActionItemEmojiReactions = (props) => { const popoverReactionListAnchor = useRef(null); let totalReactionCount = 0; - // @TODO: need to sort everything so that emojis and users are in the order they were added - // each emoji is sorted by the oldest timestamp of user reactions + // Each emoji is sorted by the oldest timestamp of user reactions so that they will always appear in the same order for everyone const sortedReactions = _.sortBy(props.emojiReactions, (emojiReaction, emojiName) => { + // Since the emojiName is only stored as the object key, when _.sortBy() runs, the object is converted to an array and the + // keys are lost. To keep from losing the emojiName, it's copied to the emojiReaction object. + // eslint-disable-next-line no-param-reassign emojiReaction.emojiName = emojiName; - return _.reduce( - emojiReaction.users, - (oldestTimestamp, userData) => { - // userData can be null in the case where they have removed all their reactions of this emoji + return _.chain(emojiReaction.users) + .reduce((allTimestampsArray, userData) => { if (!userData) { - return oldestTimestamp; + return allTimestampsArray; } - const oldestUserReaction = _.chain(userData.skinTones).values().flatten().sort().first().value(); - return oldestUserReaction < oldestTimestamp ? oldestUserReaction : oldestTimestamp; - }, - '', - ); + _.each(userData.skinTones, (createdAt) => { + allTimestampsArray.push(createdAt); + }); + return allTimestampsArray; + }, []) + .sort() + .first() + .value(); }); return ( Date: Tue, 23 May 2023 11:22:23 +0100 Subject: [PATCH 31/68] Update from recent code changes --- .../ReportActionItemEmojiReactions.js | 26 ++----------------- src/libs/EmojiUtils.js | 22 +++++++++------- 2 files changed, 14 insertions(+), 34 deletions(-) diff --git a/src/components/Reactions/ReportActionItemEmojiReactions.js b/src/components/Reactions/ReportActionItemEmojiReactions.js index 31bcf460b527..d9a3f1cf651a 100644 --- a/src/components/Reactions/ReportActionItemEmojiReactions.js +++ b/src/components/Reactions/ReportActionItemEmojiReactions.js @@ -2,38 +2,16 @@ import React, {useRef} from 'react'; import _ from 'underscore'; import {View} from 'react-native'; import PropTypes from 'prop-types'; -import lodashGet from 'lodash/get'; import styles from '../../styles/styles'; import EmojiReactionBubble from './EmojiReactionBubble'; import emojis from '../../../assets/emojis'; import AddReactionBubble from './AddReactionBubble'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '../withCurrentUserPersonalDetails'; -import getPreferredEmojiCode from './getPreferredEmojiCode'; import Tooltip from '../Tooltip'; import ReactionTooltipContent from './ReactionTooltipContent'; import * as Report from '../../libs/actions/Report'; import * as ReactionList from '../../pages/home/report/ReactionList/ReactionList'; - -/** - * Given an emoji object and a list of senders it will return an - * array of emoji codes, that represents all used variations of the - * emoji. - * @param {{ name: string, code: string, types: string[] }} emojiAsset - * @param {Object} users - * @return {string[]} - * */ -const getUniqueEmojiCodes = (emojiAsset, users) => { - const uniqueEmojiCodes = []; - _.each(users, (userSkinTones) => { - _.each(lodashGet(userSkinTones, 'skinTones'), (createdAt, skinTone) => { - const emojiCode = getPreferredEmojiCode(emojiAsset, skinTone); - if (emojiCode && !uniqueEmojiCodes.includes(emojiCode)) { - uniqueEmojiCodes.push(emojiCode); - } - }); - }); - return uniqueEmojiCodes; -}; +import * as EmojiUtils from '../../libs/EmojiUtils'; const propTypes = { /** All the emoji reactions for the report action. An object that looks like this: @@ -123,7 +101,7 @@ const ReportActionItemEmojiReactions = (props) => { } totalReactionCount += reactionCount; const emojiAsset = _.find(emojis, (emoji) => emoji.name === reactionEmojiName); - const emojiCodes = getUniqueEmojiCodes(emojiAsset, reaction.users); + const emojiCodes = EmojiUtils.getUniqueEmojiCodes(emojiAsset, reaction.users); const hasUserReacted = Report.hasAccountIDEmojiReacted(props.currentUserPersonalDetails.accountID, reaction.users); const reactionUsers = _.keys(reaction.users); diff --git a/src/libs/EmojiUtils.js b/src/libs/EmojiUtils.js index a4325af27c9c..f15f1f2d4d98 100644 --- a/src/libs/EmojiUtils.js +++ b/src/libs/EmojiUtils.js @@ -2,6 +2,7 @@ import _ from 'underscore'; import moment from 'moment'; import Str from 'expensify-common/lib/str'; import Onyx from 'react-native-onyx'; +import lodashGet from 'lodash/get'; import ONYXKEYS from '../ONYXKEYS'; import CONST from '../CONST'; import emojisTrie from './EmojiTrie'; @@ -326,20 +327,21 @@ const getPreferredEmojiCode = (emoji, preferredSkinTone) => { * Given an emoji object and a list of senders it will return an * array of emoji codes, that represents all used variations of the * emoji. - * @param {{ name: string, code: string, types: string[] }} emoji + * @param {{ name: string, code: string, types: string[] }} emojiAsset * @param {Array} users * @return {string[]} * */ -const getUniqueEmojiCodes = (emoji, users) => { - const emojiCodes = []; - _.forEach(users, (user) => { - const emojiCode = getPreferredEmojiCode(emoji, user.skinTone); - - if (emojiCode && !emojiCodes.includes(emojiCode)) { - emojiCodes.push(emojiCode); - } +const getUniqueEmojiCodes = (emojiAsset, users) => { + const uniqueEmojiCodes = []; + _.each(users, (userSkinTones) => { + _.each(lodashGet(userSkinTones, 'skinTones'), (createdAt, skinTone) => { + const emojiCode = getPreferredEmojiCode(emojiAsset, skinTone); + if (emojiCode && !uniqueEmojiCodes.includes(emojiCode)) { + uniqueEmojiCodes.push(emojiCode); + } + }); }); - return emojiCodes; + return uniqueEmojiCodes; }; export { From 729f2aa86dd382a165d1657cfa0f21dece62466c Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 24 May 2023 10:51:50 +0100 Subject: [PATCH 32/68] Remove debug --- src/components/Reactions/ReactionTooltipContent.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Reactions/ReactionTooltipContent.js b/src/components/Reactions/ReactionTooltipContent.js index 1d4a04f3ad66..42c2a1f0fa61 100644 --- a/src/components/Reactions/ReactionTooltipContent.js +++ b/src/components/Reactions/ReactionTooltipContent.js @@ -32,7 +32,6 @@ const defaultProps = { }; const ReactionTooltipContent = (props) => { - console.log(props.accountIDs); const users = useMemo( () => PersonalDetailsUtils.getPersonalDetailsByIDs(props.accountIDs, props.currentUserPersonalDetails.accountID, true), [props.currentUserPersonalDetails.accountID, props.accountIDs], From 5ae68d6df349ae899407eeed2436df8cd78c6b7a Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 24 May 2023 12:38:42 +0100 Subject: [PATCH 33/68] Use negative values instead of strings --- src/libs/actions/Report.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 1dc7d7ee7ef9..e1e73ae7ff12 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1480,7 +1480,7 @@ function addEmojiReaction(reportID, reportActionID, emoji, skinTone = preferredS users: { [currentUserAccountID]: { skinTones: { - [skinTone || 'nothing']: createdAt, + [skinTone || -1]: createdAt, }, }, }, @@ -1547,7 +1547,7 @@ function toggleEmojiReaction(reportID, reportAction, emoji, existingReactions, p const reactionObject = lodashGet(existingReactions, [emoji.name]); // Only use skin tone if emoji supports it - const skinTone = emoji.types === undefined ? 'nothing' : paramSkinTone; + const skinTone = emoji.types === undefined ? -1 : paramSkinTone; if (reactionObject && hasAccountIDEmojiReacted(currentUserAccountID, reactionObject.users, skinTone)) { removeEmojiReaction(reportID, reportAction.reportActionID, emoji); From f26dacc260f48d239058807714fc56067f746a72 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 24 May 2023 12:38:54 +0100 Subject: [PATCH 34/68] Remove unused parameter --- src/libs/actions/Report.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index e1e73ae7ff12..715a40a99741 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1506,9 +1506,8 @@ function addEmojiReaction(reportID, reportActionID, emoji, skinTone = preferredS * @param {String} reportID * @param {String} reportActionID * @param {{ name: string, code: string, types: string[] }} emoji - * @param {Object} existingReactions */ -function removeEmojiReaction(reportID, reportActionID, emoji, existingReactions) { +function removeEmojiReaction(reportID, reportActionID, emoji) { const optimisticData = [ { onyxMethod: Onyx.METHOD.MERGE, From 270e166af4d10816eecb08622b61f4c545578ac9 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 24 May 2023 16:58:50 +0100 Subject: [PATCH 35/68] Share proptypes --- .../Reactions/EmojiReactionsPropTypes.js | 35 +++++++++++++++++++ .../Reactions/MiniQuickEmojiReactions.js | 7 ++-- .../ReportActionItemEmojiReactions.js | 33 ++--------------- 3 files changed, 43 insertions(+), 32 deletions(-) create mode 100644 src/components/Reactions/EmojiReactionsPropTypes.js diff --git a/src/components/Reactions/EmojiReactionsPropTypes.js b/src/components/Reactions/EmojiReactionsPropTypes.js new file mode 100644 index 000000000000..2989e3c8de1a --- /dev/null +++ b/src/components/Reactions/EmojiReactionsPropTypes.js @@ -0,0 +1,35 @@ +import PropTypes from 'prop-types'; + +const propTypes = { + /** All the emoji reactions for the report action. An object that looks like this: + "emojiReactions": { + "+1": { // The emoji added to the action + "createdAt": "2021-01-01 00:00:00", + "users": { + 2352342: { // The accountID of the user who added this emoji + "skinTones": { + "1": "2021-01-01 00:00:00", + "2": "2021-01-01 00:00:00", + }, + }, + }, + }, + }, + */ + emojiReactions: PropTypes.objectOf( + PropTypes.shape({ + /** The time the emoji was added */ + createdAt: PropTypes.string, + + /** All the users who have added this emoji */ + users: PropTypes.objectOf( + PropTypes.shape({ + /** The skin tone which was used and also the timestamp of when it was added */ + skinTones: PropTypes.objectOf(PropTypes.string), + }), + ), + }), + ), +}; +const defaultProps = {}; +export {propTypes, defaultProps}; diff --git a/src/components/Reactions/MiniQuickEmojiReactions.js b/src/components/Reactions/MiniQuickEmojiReactions.js index 80ca1c68d9dc..4a80d7587ea6 100644 --- a/src/components/Reactions/MiniQuickEmojiReactions.js +++ b/src/components/Reactions/MiniQuickEmojiReactions.js @@ -17,8 +17,10 @@ import withLocalize, {withLocalizePropTypes} from '../withLocalize'; import compose from '../../libs/compose'; import ONYXKEYS from '../../ONYXKEYS'; import * as EmojiUtils from '../../libs/EmojiUtils'; +import * as EmojiReactionPropTypes from './EmojiReactionsPropTypes'; const propTypes = { + ...EmojiReactionPropTypes.propTypes, ...baseQuickEmojiReactionsPropTypes, /** @@ -32,6 +34,7 @@ const propTypes = { }; const defaultProps = { + ...EmojiReactionPropTypes.defaultProps, onEmojiPickerClosed: () => {}, preferredSkinTone: CONST.EMOJI_DEFAULT_SKIN_TONE, }; @@ -65,7 +68,7 @@ const MiniQuickEmojiReactions = (props) => { key={emoji.name} isDelayButtonStateComplete={false} tooltipText={`:${emoji.name}:`} - onPress={() => props.onEmojiSelected(emoji, props.reactions)} + onPress={() => props.onEmojiSelected(emoji, props.emojiReactions)} > {EmojiUtils.getPreferredEmojiCode(emoji, props.preferredSkinTone)} @@ -97,7 +100,7 @@ export default compose( preferredSkinTone: { key: ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, }, - reactions: { + emojiReactions: { key: ({reportID, reportActionID}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportID}${reportActionID}`, }, }), diff --git a/src/components/Reactions/ReportActionItemEmojiReactions.js b/src/components/Reactions/ReportActionItemEmojiReactions.js index d9a3f1cf651a..1682616dae5d 100644 --- a/src/components/Reactions/ReportActionItemEmojiReactions.js +++ b/src/components/Reactions/ReportActionItemEmojiReactions.js @@ -12,37 +12,10 @@ import ReactionTooltipContent from './ReactionTooltipContent'; import * as Report from '../../libs/actions/Report'; import * as ReactionList from '../../pages/home/report/ReactionList/ReactionList'; import * as EmojiUtils from '../../libs/EmojiUtils'; +import * as EmojiReactionPropTypes from './EmojiReactionsPropTypes'; const propTypes = { - /** All the emoji reactions for the report action. An object that looks like this: - "emojiReactions": { - "+1": { // The emoji added to the action - "createdAt": "2021-01-01 00:00:00", - "users": { - 2352342: { // The accountID of the user who added this emoji - "skinTones": { - "1": "2021-01-01 00:00:00", - "2": "2021-01-01 00:00:00", - }, - }, - }, - }, - }, - */ - emojiReactions: PropTypes.objectOf( - PropTypes.shape({ - /** The time the emoji was added */ - createdAt: PropTypes.string, - - /** All the users who have added this emoji */ - users: PropTypes.objectOf( - PropTypes.shape({ - /** The skin tone which was used and also the timestamp of when it was added */ - skinTones: PropTypes.objectOf(PropTypes.string), - }), - ), - }), - ), + ...EmojiReactionPropTypes.propTypes, /** The ID of the reportAction. It is the string representation of the a 64-bit integer. */ reportActionID: PropTypes.string.isRequired, @@ -60,7 +33,7 @@ const propTypes = { const defaultProps = { ...withCurrentUserPersonalDetailsDefaultProps, - emojiReactions: {}, + ...EmojiReactionPropTypes.defaultProps, }; const ReportActionItemEmojiReactions = (props) => { From 5f9dc0983df7a2b147cb0a158b32d98086c66458 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 24 May 2023 17:01:15 +0100 Subject: [PATCH 36/68] Share more propTypes --- .../QuickEmojiReactions/BaseQuickEmojiReactions.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js b/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js index 1706d54cd48f..0c63e38bdae6 100644 --- a/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js +++ b/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js @@ -10,8 +10,10 @@ import styles from '../../../styles/styles'; import ONYXKEYS from '../../../ONYXKEYS'; import Tooltip from '../../Tooltip'; import * as EmojiUtils from '../../../libs/EmojiUtils'; +import * as EmojiReactionsPropTypes from '../EmojiReactionsPropTypes'; const baseQuickEmojiReactionsPropTypes = { + ...EmojiReactionsPropTypes.propTypes, /** * Callback to fire when an emoji is selected. */ @@ -31,6 +33,7 @@ const baseQuickEmojiReactionsPropTypes = { }; const baseQuickEmojiReactionsDefaultProps = { + ...EmojiReactionsPropTypes.defaultProps, onWillShowPicker: () => {}, onPressOpenPicker: () => {}, }; @@ -58,7 +61,7 @@ const BaseQuickEmojiReactions = (props) => ( emojiCodes={[EmojiUtils.getPreferredEmojiCode(emoji, props.preferredSkinTone)]} isContextMenu onPress={() => { - props.onEmojiSelected(emoji, props.reactions); + props.onEmojiSelected(emoji, props.emojiReactions); }} /> @@ -67,7 +70,7 @@ const BaseQuickEmojiReactions = (props) => ( isContextMenu onPressOpenPicker={props.onPressOpenPicker} onWillShowPicker={props.onWillShowPicker} - onSelectEmoji={(emoji) => props.onEmojiSelected(emoji, props.reactions)} + onSelectEmoji={(emoji) => props.onEmojiSelected(emoji, props.emojiReactions)} /> ); @@ -79,7 +82,7 @@ export default withOnyx({ preferredSkinTone: { key: ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, }, - reactions: { + emojiReactions: { key: ({reportID, reportActionID}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportID}${reportActionID}`, }, })(BaseQuickEmojiReactions); From 9f26e87511950e6770f3df69a5a70f01c3b4f382 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Wed, 24 May 2023 17:01:20 +0100 Subject: [PATCH 37/68] Remove unused method --- src/libs/actions/Report.js | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 715a40a99741..af969b14d6a6 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1418,29 +1418,6 @@ function clearIOUError(reportID) { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {errorFields: {iou: null}}); } -/** - * Internal function to help with updating the onyx state of a message of a report action. - * @param {Object} originalReportAction - * @param {Object} message - * @param {String} reportID - * @return {Object[]} - */ -function getOptimisticDataForReportActionUpdate(originalReportAction, message, reportID) { - const reportActionID = originalReportAction.reportActionID; - - return [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, - value: { - [reportActionID]: { - message: [message], - }, - }, - }, - ]; -} - /** * Returns true if the accountID has reacted to the report action (with the given skin tone). * Uses the NEW FORMAT for "emojiReactions" From 2994e0074464713c12739f329dbc5a504040b8c2 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Thu, 25 May 2023 10:04:38 +0100 Subject: [PATCH 38/68] Fix lint errors --- src/components/Reactions/EmojiReactionsPropTypes.js | 5 ++--- src/components/Reactions/MiniQuickEmojiReactions.js | 6 ++---- .../QuickEmojiReactions/BaseQuickEmojiReactions.js | 12 ++++++++---- .../Reactions/ReportActionItemEmojiReactions.js | 6 +++--- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/components/Reactions/EmojiReactionsPropTypes.js b/src/components/Reactions/EmojiReactionsPropTypes.js index 2989e3c8de1a..62875cf52b76 100644 --- a/src/components/Reactions/EmojiReactionsPropTypes.js +++ b/src/components/Reactions/EmojiReactionsPropTypes.js @@ -1,6 +1,6 @@ import PropTypes from 'prop-types'; -const propTypes = { +const EmojiReactionsPropTypes = { /** All the emoji reactions for the report action. An object that looks like this: "emojiReactions": { "+1": { // The emoji added to the action @@ -31,5 +31,4 @@ const propTypes = { }), ), }; -const defaultProps = {}; -export {propTypes, defaultProps}; +export default EmojiReactionsPropTypes; diff --git a/src/components/Reactions/MiniQuickEmojiReactions.js b/src/components/Reactions/MiniQuickEmojiReactions.js index 4a80d7587ea6..0dfb835c3132 100644 --- a/src/components/Reactions/MiniQuickEmojiReactions.js +++ b/src/components/Reactions/MiniQuickEmojiReactions.js @@ -12,15 +12,13 @@ import Icon from '../Icon'; import * as Expensicons from '../Icon/Expensicons'; import getButtonState from '../../libs/getButtonState'; import * as EmojiPickerAction from '../../libs/actions/EmojiPickerAction'; -import {baseQuickEmojiReactionsPropTypes} from './QuickEmojiReactions/BaseQuickEmojiReactions'; +import {baseQuickEmojiReactionsPropTypes, baseQuickEmojiReactionsDefaultProps} from './QuickEmojiReactions/BaseQuickEmojiReactions'; import withLocalize, {withLocalizePropTypes} from '../withLocalize'; import compose from '../../libs/compose'; import ONYXKEYS from '../../ONYXKEYS'; import * as EmojiUtils from '../../libs/EmojiUtils'; -import * as EmojiReactionPropTypes from './EmojiReactionsPropTypes'; const propTypes = { - ...EmojiReactionPropTypes.propTypes, ...baseQuickEmojiReactionsPropTypes, /** @@ -34,7 +32,7 @@ const propTypes = { }; const defaultProps = { - ...EmojiReactionPropTypes.defaultProps, + ...baseQuickEmojiReactionsDefaultProps, onEmojiPickerClosed: () => {}, preferredSkinTone: CONST.EMOJI_DEFAULT_SKIN_TONE, }; diff --git a/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js b/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js index 0c63e38bdae6..1be6bc583504 100644 --- a/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js +++ b/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js @@ -10,10 +10,11 @@ import styles from '../../../styles/styles'; import ONYXKEYS from '../../../ONYXKEYS'; import Tooltip from '../../Tooltip'; import * as EmojiUtils from '../../../libs/EmojiUtils'; -import * as EmojiReactionsPropTypes from '../EmojiReactionsPropTypes'; +import EmojiReactionsPropTypes from '../EmojiReactionsPropTypes'; const baseQuickEmojiReactionsPropTypes = { - ...EmojiReactionsPropTypes.propTypes, + ...EmojiReactionsPropTypes, + /** * Callback to fire when an emoji is selected. */ @@ -33,7 +34,7 @@ const baseQuickEmojiReactionsPropTypes = { }; const baseQuickEmojiReactionsDefaultProps = { - ...EmojiReactionsPropTypes.defaultProps, + emojiReactions: {}, onWillShowPicker: () => {}, onPressOpenPicker: () => {}, }; @@ -78,6 +79,9 @@ const BaseQuickEmojiReactions = (props) => ( BaseQuickEmojiReactions.displayName = 'BaseQuickEmojiReactions'; BaseQuickEmojiReactions.propTypes = propTypes; BaseQuickEmojiReactions.defaultProps = defaultProps; +// ESLint throws an error because it can't see that emojiReactions is defined in props. It is defined in props, but +// because of a couple spread operators, I think that's why ESLint struggles to see it +// eslint-disable-next-line rulesdir/onyx-props-must-have-default export default withOnyx({ preferredSkinTone: { key: ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, @@ -87,4 +91,4 @@ export default withOnyx({ }, })(BaseQuickEmojiReactions); -export {baseQuickEmojiReactionsPropTypes}; +export {baseQuickEmojiReactionsPropTypes, baseQuickEmojiReactionsDefaultProps}; diff --git a/src/components/Reactions/ReportActionItemEmojiReactions.js b/src/components/Reactions/ReportActionItemEmojiReactions.js index 1682616dae5d..c93e910ffd67 100644 --- a/src/components/Reactions/ReportActionItemEmojiReactions.js +++ b/src/components/Reactions/ReportActionItemEmojiReactions.js @@ -12,10 +12,10 @@ import ReactionTooltipContent from './ReactionTooltipContent'; import * as Report from '../../libs/actions/Report'; import * as ReactionList from '../../pages/home/report/ReactionList/ReactionList'; import * as EmojiUtils from '../../libs/EmojiUtils'; -import * as EmojiReactionPropTypes from './EmojiReactionsPropTypes'; +import EmojiReactionsPropTypes from './EmojiReactionsPropTypes'; const propTypes = { - ...EmojiReactionPropTypes.propTypes, + ...EmojiReactionsPropTypes, /** The ID of the reportAction. It is the string representation of the a 64-bit integer. */ reportActionID: PropTypes.string.isRequired, @@ -33,7 +33,7 @@ const propTypes = { const defaultProps = { ...withCurrentUserPersonalDetailsDefaultProps, - ...EmojiReactionPropTypes.defaultProps, + emojiReactions: {}, }; const ReportActionItemEmojiReactions = (props) => { From b90fed1e21e1465bfd3548e62f70f47fce2fcba8 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Thu, 25 May 2023 10:44:50 +0100 Subject: [PATCH 39/68] Correct the tests when removing emoji --- tests/actions/ReportTest.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/actions/ReportTest.js b/tests/actions/ReportTest.js index ab002bb4e4d9..83ec5c3ad3b5 100644 --- a/tests/actions/ReportTest.js +++ b/tests/actions/ReportTest.js @@ -585,10 +585,10 @@ describe('actions/Report', () => { return waitForPromisesToResolve(); }) .then(() => { - // Expect the reaction to no longer exist + // Expect the reaction to have null where the users reaction used to be expect(reportActionsReactions).toHaveProperty(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`); const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`]; - expect(reportActionReaction).not.toHaveProperty(EMOJI.name); + expect(reportActionReaction[EMOJI.name].users[TEST_USER_ACCOUNT_ID]).toBeNull(); }) .then(() => { // Add the same reaction to the same report action with a different skintone @@ -621,10 +621,10 @@ describe('actions/Report', () => { return waitForPromisesToResolve(); }) .then(() => { - // Expect the reaction to no longer exist + // Expect the reaction to have null where the users reaction used to be expect(reportActionsReactions).toHaveProperty(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`); const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`]; - expect(reportActionReaction).not.toHaveProperty(EMOJI.name); + expect(reportActionReaction[EMOJI.name].users[TEST_USER_ACCOUNT_ID]).toBeNull(); }); }); }); @@ -685,10 +685,10 @@ describe('actions/Report', () => { return waitForPromisesToResolve(); }) .then(() => { - // Expect the reaction to no longer exist + // Expect the reaction to have null where the users reaction used to be expect(reportActionsReactions).toHaveProperty(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`); const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`]; - expect(reportActionReaction).not.toHaveProperty(EMOJI.name); + expect(reportActionReaction[EMOJI.name].users[TEST_USER_ACCOUNT_ID]).toBeNull(); }); }); }); From c24edaa806ae58c16e40443e443f623fd8e92969 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Thu, 25 May 2023 10:52:09 +0100 Subject: [PATCH 40/68] Disable eslint error --- src/components/Reactions/MiniQuickEmojiReactions.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/Reactions/MiniQuickEmojiReactions.js b/src/components/Reactions/MiniQuickEmojiReactions.js index 0dfb835c3132..145ee09eb698 100644 --- a/src/components/Reactions/MiniQuickEmojiReactions.js +++ b/src/components/Reactions/MiniQuickEmojiReactions.js @@ -92,6 +92,9 @@ const MiniQuickEmojiReactions = (props) => { MiniQuickEmojiReactions.displayName = 'MiniQuickEmojiReactions'; MiniQuickEmojiReactions.propTypes = propTypes; MiniQuickEmojiReactions.defaultProps = defaultProps; +// ESLint throws an error because it can't see that emojiReactions is defined in props. It is defined in props, but +// because of a couple spread operators, I think that's why ESLint struggles to see it +// eslint-disable-next-line rulesdir/onyx-props-must-have-default export default compose( withLocalize, withOnyx({ From 93012d78a7c087efe5619763873b64485b75e3a7 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Thu, 25 May 2023 11:19:42 +0100 Subject: [PATCH 41/68] Move ESLint disable to a better line --- src/components/Reactions/MiniQuickEmojiReactions.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/Reactions/MiniQuickEmojiReactions.js b/src/components/Reactions/MiniQuickEmojiReactions.js index 145ee09eb698..a92b7f1a1837 100644 --- a/src/components/Reactions/MiniQuickEmojiReactions.js +++ b/src/components/Reactions/MiniQuickEmojiReactions.js @@ -92,11 +92,11 @@ const MiniQuickEmojiReactions = (props) => { MiniQuickEmojiReactions.displayName = 'MiniQuickEmojiReactions'; MiniQuickEmojiReactions.propTypes = propTypes; MiniQuickEmojiReactions.defaultProps = defaultProps; -// ESLint throws an error because it can't see that emojiReactions is defined in props. It is defined in props, but -// because of a couple spread operators, I think that's why ESLint struggles to see it -// eslint-disable-next-line rulesdir/onyx-props-must-have-default export default compose( withLocalize, + // ESLint throws an error because it can't see that emojiReactions is defined in props. It is defined in props, but + // because of a couple spread operators, I think that's why ESLint struggles to see it + // eslint-disable-next-line rulesdir/onyx-props-must-have-default withOnyx({ preferredSkinTone: { key: ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, From b518fcdbeffcc534ba1747704bbb7733e0cb3f1c Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Thu, 25 May 2023 16:42:25 +0100 Subject: [PATCH 42/68] Add more data to the sorting key so that things will stay in order --- .../ReportActionItemEmojiReactions.js | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/components/Reactions/ReportActionItemEmojiReactions.js b/src/components/Reactions/ReportActionItemEmojiReactions.js index 7f4d437b2357..8397e4bda1bc 100644 --- a/src/components/Reactions/ReportActionItemEmojiReactions.js +++ b/src/components/Reactions/ReportActionItemEmojiReactions.js @@ -46,19 +46,24 @@ const ReportActionItemEmojiReactions = (props) => { // keys are lost. To keep from losing the emojiName, it's copied to the emojiReaction object. // eslint-disable-next-line no-param-reassign emojiReaction.emojiName = emojiName; - return _.chain(emojiReaction.users) - .reduce((allTimestampsArray, userData) => { - if (!userData) { + return ( + _.chain(emojiReaction.users) + .reduce((allTimestampsArray, userData) => { + if (!userData) { + return allTimestampsArray; + } + _.each(userData.skinTones, (createdAt) => { + allTimestampsArray.push(createdAt); + }); return allTimestampsArray; - } - _.each(userData.skinTones, (createdAt) => { - allTimestampsArray.push(createdAt); - }); - return allTimestampsArray; - }, []) - .sort() - .first() - .value(); + }, []) + .sort() + .first() + // Just in case two emojis have the same timestamp, also combine the timestamp with the + // emojiName so that the order will always be the same. Without this, the order can be pretty random + // and shift around a little bit. + .value() + emojiName + ); }); return ( Date: Mon, 29 May 2023 19:28:07 +0300 Subject: [PATCH 43/68] Simplify onyx key for collection --- .../Reactions/MiniQuickEmojiReactions.js | 2 +- .../BaseQuickEmojiReactions.js | 2 +- src/libs/actions/Report.js | 4 ++-- src/pages/home/report/ReportActionItem.js | 2 +- tests/actions/ReportTest.js | 24 +++++++++---------- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/components/Reactions/MiniQuickEmojiReactions.js b/src/components/Reactions/MiniQuickEmojiReactions.js index a92b7f1a1837..fbcc0c4eff13 100644 --- a/src/components/Reactions/MiniQuickEmojiReactions.js +++ b/src/components/Reactions/MiniQuickEmojiReactions.js @@ -102,7 +102,7 @@ export default compose( key: ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, }, emojiReactions: { - key: ({reportID, reportActionID}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportID}${reportActionID}`, + key: ({reportActionID}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`, }, }), )(MiniQuickEmojiReactions); diff --git a/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js b/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js index 572c03e182c4..857c0dad38c5 100644 --- a/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js +++ b/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js @@ -87,7 +87,7 @@ export default withOnyx({ key: ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, }, emojiReactions: { - key: ({reportID, reportActionID}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportID}${reportActionID}`, + key: ({reportActionID}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`, }, })(BaseQuickEmojiReactions); diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index a5485c550d11..e8c79a0c8550 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1489,7 +1489,7 @@ function addEmojiReaction(reportID, reportActionID, emoji, skinTone = preferredS const optimisticData = [ { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportID}${reportActionID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`, value: { [emoji.name]: { createdAt, @@ -1527,7 +1527,7 @@ function removeEmojiReaction(reportID, reportActionID, emoji) { const optimisticData = [ { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportID}${reportActionID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`, value: { [emoji.name]: { users: { diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index e62a2703d2c6..2d1fc3a83471 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -451,7 +451,7 @@ export default compose( key: ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, }, emojiReactions: { - key: ({report, action}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${report.reportID}${action.reportActionID}`, + key: ({action}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${action.reportActionID}`, }, betas: { key: ONYXKEYS.BETAS, diff --git a/tests/actions/ReportTest.js b/tests/actions/ReportTest.js index 83ec5c3ad3b5..a66951945557 100644 --- a/tests/actions/ReportTest.js +++ b/tests/actions/ReportTest.js @@ -570,10 +570,10 @@ describe('actions/Report', () => { }) .then(() => { // Expect the reaction to exist in the reportActionsReactions collection - expect(reportActionsReactions).toHaveProperty(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`); + expect(reportActionsReactions).toHaveProperty(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`); // Expect the reaction to have the emoji on it - const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`]; + const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`]; expect(reportActionReaction).toHaveProperty(EMOJI.name); // Expect the emoji to have the user accountID @@ -586,8 +586,8 @@ describe('actions/Report', () => { }) .then(() => { // Expect the reaction to have null where the users reaction used to be - expect(reportActionsReactions).toHaveProperty(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`); - const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`]; + expect(reportActionsReactions).toHaveProperty(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`); + const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`]; expect(reportActionReaction[EMOJI.name].users[TEST_USER_ACCOUNT_ID]).toBeNull(); }) .then(() => { @@ -595,16 +595,16 @@ describe('actions/Report', () => { Report.toggleEmojiReaction(REPORT_ID, reportAction, EMOJI); return waitForPromisesToResolve() .then(() => { - const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`]; + const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`]; Report.toggleEmojiReaction(REPORT_ID, reportAction, EMOJI, reportActionReaction, EMOJI_SKIN_TONE); return waitForPromisesToResolve(); }) .then(() => { // Expect the reaction to exist in the reportActionsReactions collection - expect(reportActionsReactions).toHaveProperty(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`); + expect(reportActionsReactions).toHaveProperty(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`); // Expect the reaction to have the emoji on it - const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`]; + const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`]; expect(reportActionReaction).toHaveProperty(EMOJI.name); // Expect the emoji to have the user accountID @@ -622,8 +622,8 @@ describe('actions/Report', () => { }) .then(() => { // Expect the reaction to have null where the users reaction used to be - expect(reportActionsReactions).toHaveProperty(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`); - const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`]; + expect(reportActionsReactions).toHaveProperty(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`); + const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`]; expect(reportActionReaction[EMOJI.name].users[TEST_USER_ACCOUNT_ID]).toBeNull(); }); }); @@ -680,14 +680,14 @@ describe('actions/Report', () => { }) .then(() => { // Now we toggle the reaction while the skin tone has changed. Since the emoji doesn't support skin tones, the emoji should get removed instead of added again. - const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`]; + const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`]; Report.toggleEmojiReaction(REPORT_ID, reportAction, EMOJI, reportActionReaction, 2); return waitForPromisesToResolve(); }) .then(() => { // Expect the reaction to have null where the users reaction used to be - expect(reportActionsReactions).toHaveProperty(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`); - const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${REPORT_ID}${reportActionID}`]; + expect(reportActionsReactions).toHaveProperty(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`); + const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`]; expect(reportActionReaction[EMOJI.name].users[TEST_USER_ACCOUNT_ID]).toBeNull(); }); }); From 3d6f5a2c18ab41e59a03c2009771cba3cc2abdce Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Mon, 29 May 2023 19:30:25 +0300 Subject: [PATCH 44/68] Add comment about cleanup task --- src/libs/actions/Report.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index e8c79a0c8550..8d3a5cedb2cb 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1511,6 +1511,7 @@ function addEmojiReaction(reportID, reportActionID, emoji, skinTone = preferredS emojiCode: emoji.name, reportActionID, createdAt, + // This will be removed as part of https://github.com/Expensify/App/issues/19535 useEmojiReactions: true, }; API.write('AddEmojiReaction', parameters, {optimisticData}); @@ -1542,6 +1543,7 @@ function removeEmojiReaction(reportID, reportActionID, emoji) { reportID, reportActionID, emojiCode: emoji.name, + // This will be removed as part of https://github.com/Expensify/App/issues/19535 useEmojiReactions: true, }; API.write('RemoveEmojiReaction', parameters, {optimisticData}); From 52272827e0cc04189f353d37b6416905af035a98 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Mon, 29 May 2023 19:33:19 +0300 Subject: [PATCH 45/68] Rename method and fix proptypes --- .../home/report/ContextMenu/ContextMenuActions.js | 6 +++--- src/pages/home/report/ReportActionItem.js | 13 ++----------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.js b/src/pages/home/report/ContextMenu/ContextMenuActions.js index 2ddd316e8c52..709a5c8ed27a 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.js +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.js @@ -55,7 +55,7 @@ export default [ } }; - const onEmojiSelected = (emoji, existingReactions) => { + const toggleEmojiAndCloseMenu = (emoji, existingReactions) => { Report.toggleEmojiReaction(reportID, reportAction, emoji, existingReactions); closeContextMenu(); @@ -65,7 +65,7 @@ export default [ return ( diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 2d1fc3a83471..ab72d0d40ae6 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -52,6 +52,7 @@ import ReportActionItemDraft from './ReportActionItemDraft'; import TaskPreview from '../../../components/ReportActionItem/TaskPreview'; import TaskAction from '../../../components/ReportActionItem/TaskAction'; import Permissions from '../../../libs/Permissions'; +import EmojiReactionsPropTypes from '../../../components/Reactions/EmojiReactionsPropTypes'; const propTypes = { /** Report for this action */ @@ -88,17 +89,7 @@ const propTypes = { /** All of the personalDetails */ personalDetails: PropTypes.objectOf(personalDetailsPropType), - emojiReactions: PropTypes.objectOf( - PropTypes.shape({ - emoji: PropTypes.string, - users: PropTypes.objectOf( - PropTypes.shape({ - accountID: PropTypes.number, - skinTone: PropTypes.string, - }), - ), - }), - ), + ...EmojiReactionsPropTypes, /** List of betas available to current user */ betas: PropTypes.arrayOf(PropTypes.string), From 6d60bee18401e1f852779cbb919045aa40a77b52 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Mon, 29 May 2023 19:55:56 +0300 Subject: [PATCH 46/68] Make the sorting work a little more reliably --- .../ReportActionItemEmojiReactions.js | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/components/Reactions/ReportActionItemEmojiReactions.js b/src/components/Reactions/ReportActionItemEmojiReactions.js index 8397e4bda1bc..5c7b7671c8e0 100644 --- a/src/components/Reactions/ReportActionItemEmojiReactions.js +++ b/src/components/Reactions/ReportActionItemEmojiReactions.js @@ -46,24 +46,24 @@ const ReportActionItemEmojiReactions = (props) => { // keys are lost. To keep from losing the emojiName, it's copied to the emojiReaction object. // eslint-disable-next-line no-param-reassign emojiReaction.emojiName = emojiName; - return ( - _.chain(emojiReaction.users) - .reduce((allTimestampsArray, userData) => { - if (!userData) { - return allTimestampsArray; - } - _.each(userData.skinTones, (createdAt) => { - allTimestampsArray.push(createdAt); - }); + const oldestUserReactionTimestamp = _.chain(emojiReaction.users) + .reduce((allTimestampsArray, userData) => { + if (!userData) { return allTimestampsArray; - }, []) - .sort() - .first() - // Just in case two emojis have the same timestamp, also combine the timestamp with the - // emojiName so that the order will always be the same. Without this, the order can be pretty random - // and shift around a little bit. - .value() + emojiName - ); + } + _.each(userData.skinTones, (createdAt) => { + allTimestampsArray.push(createdAt); + }); + return allTimestampsArray; + }, []) + .sort() + .first() + .value(); + + // Just in case two emojis have the same timestamp, also combine the timestamp with the + // emojiName so that the order will always be the same. Without this, the order can be pretty random + // and shift around a little bit. + return (oldestUserReactionTimestamp || emojiReaction.createdAt) + emojiName; }); return ( Date: Mon, 29 May 2023 19:57:38 +0300 Subject: [PATCH 47/68] Replace prop that was mistakingly removed --- src/components/Reactions/ReportActionItemEmojiReactions.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Reactions/ReportActionItemEmojiReactions.js b/src/components/Reactions/ReportActionItemEmojiReactions.js index 5c7b7671c8e0..e40a76cf7d52 100644 --- a/src/components/Reactions/ReportActionItemEmojiReactions.js +++ b/src/components/Reactions/ReportActionItemEmojiReactions.js @@ -101,6 +101,7 @@ const ReportActionItemEmojiReactions = (props) => { /> )} key={reactionEmojiName} + renderTooltipContentKey={[...reactionUsers, ...emojiCodes]} > Date: Mon, 29 May 2023 20:21:34 +0300 Subject: [PATCH 48/68] Fix lint problem with propTypes --- .../Reactions/EmojiReactionsPropTypes.js | 51 +++++++++---------- .../BaseQuickEmojiReactions.js | 5 +- .../ReportActionItemEmojiReactions.js | 2 +- src/pages/home/report/ReportActionItem.js | 3 +- 4 files changed, 27 insertions(+), 34 deletions(-) diff --git a/src/components/Reactions/EmojiReactionsPropTypes.js b/src/components/Reactions/EmojiReactionsPropTypes.js index 62875cf52b76..ace88abefcd9 100644 --- a/src/components/Reactions/EmojiReactionsPropTypes.js +++ b/src/components/Reactions/EmojiReactionsPropTypes.js @@ -1,34 +1,31 @@ import PropTypes from 'prop-types'; -const EmojiReactionsPropTypes = { - /** All the emoji reactions for the report action. An object that looks like this: - "emojiReactions": { - "+1": { // The emoji added to the action - "createdAt": "2021-01-01 00:00:00", - "users": { - 2352342: { // The accountID of the user who added this emoji - "skinTones": { - "1": "2021-01-01 00:00:00", - "2": "2021-01-01 00:00:00", - }, +/** All the emoji reactions for the report action. An object that looks like this: + "emojiReactions": { + "+1": { // The emoji added to the action + "createdAt": "2021-01-01 00:00:00", + "users": { + 2352342: { // The accountID of the user who added this emoji + "skinTones": { + "1": "2021-01-01 00:00:00", + "2": "2021-01-01 00:00:00", }, }, }, }, - */ - emojiReactions: PropTypes.objectOf( - PropTypes.shape({ - /** The time the emoji was added */ - createdAt: PropTypes.string, + }, +*/ +export default PropTypes.objectOf( + PropTypes.shape({ + /** The time the emoji was added */ + createdAt: PropTypes.string, - /** All the users who have added this emoji */ - users: PropTypes.objectOf( - PropTypes.shape({ - /** The skin tone which was used and also the timestamp of when it was added */ - skinTones: PropTypes.objectOf(PropTypes.string), - }), - ), - }), - ), -}; -export default EmojiReactionsPropTypes; + /** All the users who have added this emoji */ + users: PropTypes.objectOf( + PropTypes.shape({ + /** The skin tone which was used and also the timestamp of when it was added */ + skinTones: PropTypes.objectOf(PropTypes.string), + }), + ), + }), +); diff --git a/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js b/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js index 857c0dad38c5..66d2b35d29db 100644 --- a/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js +++ b/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js @@ -13,7 +13,7 @@ import * as EmojiUtils from '../../../libs/EmojiUtils'; import EmojiReactionsPropTypes from '../EmojiReactionsPropTypes'; const baseQuickEmojiReactionsPropTypes = { - ...EmojiReactionsPropTypes, + emojiReactions: EmojiReactionsPropTypes, /** * Callback to fire when an emoji is selected. @@ -79,9 +79,6 @@ const BaseQuickEmojiReactions = (props) => ( BaseQuickEmojiReactions.displayName = 'BaseQuickEmojiReactions'; BaseQuickEmojiReactions.propTypes = propTypes; BaseQuickEmojiReactions.defaultProps = defaultProps; -// ESLint throws an error because it can't see that emojiReactions is defined in props. It is defined in props, but -// because of a couple spread operators, I think that's why ESLint struggles to see it -// eslint-disable-next-line rulesdir/onyx-props-must-have-default export default withOnyx({ preferredSkinTone: { key: ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, diff --git a/src/components/Reactions/ReportActionItemEmojiReactions.js b/src/components/Reactions/ReportActionItemEmojiReactions.js index e40a76cf7d52..410accba9bd8 100644 --- a/src/components/Reactions/ReportActionItemEmojiReactions.js +++ b/src/components/Reactions/ReportActionItemEmojiReactions.js @@ -15,7 +15,7 @@ import * as EmojiUtils from '../../libs/EmojiUtils'; import EmojiReactionsPropTypes from './EmojiReactionsPropTypes'; const propTypes = { - ...EmojiReactionsPropTypes, + emojiReactions: EmojiReactionsPropTypes, /** The ID of the reportAction. It is the string representation of the a 64-bit integer. */ reportActionID: PropTypes.string.isRequired, diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index ab72d0d40ae6..0f0dffb1fc22 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -89,12 +89,11 @@ const propTypes = { /** All of the personalDetails */ personalDetails: PropTypes.objectOf(personalDetailsPropType), - ...EmojiReactionsPropTypes, - /** List of betas available to current user */ betas: PropTypes.arrayOf(PropTypes.string), ...windowDimensionsPropTypes, + emojiReactions: EmojiReactionsPropTypes, }; const defaultProps = { From 149cb3736ed01a9276649f80265085f5062e1b33 Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Tue, 30 May 2023 07:33:08 +0300 Subject: [PATCH 49/68] Fix prop reference and jsdocs --- .../Reactions/MiniQuickEmojiReactions.js | 2 +- src/libs/actions/Report.js | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/components/Reactions/MiniQuickEmojiReactions.js b/src/components/Reactions/MiniQuickEmojiReactions.js index fbcc0c4eff13..527be78a7d6d 100644 --- a/src/components/Reactions/MiniQuickEmojiReactions.js +++ b/src/components/Reactions/MiniQuickEmojiReactions.js @@ -53,7 +53,7 @@ const MiniQuickEmojiReactions = (props) => { EmojiPickerAction.showEmojiPicker( props.onEmojiPickerClosed, (emojiCode, emojiObject) => { - props.onEmojiSelected(emojiObject, props.reactions); + props.onEmojiSelected(emojiObject, props.emojiReactions); }, ref.current, ); diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 8d3a5cedb2cb..073c74a506b6 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1481,8 +1481,11 @@ function hasAccountIDEmojiReacted(accountID, users, skinTone) { * Uses the NEW FORMAT for "emojiReactions" * @param {String} reportID * @param {String} reportActionID - * @param {{ name: string, code: string, types: string[] }} emoji - * @param {number} [skinTone] Optional. + * @param {Object} emoji + * @param {String} emoji.name + * @param {String} emoji.code + * @param {String[]} [emoji.types] + * @param {Number} [skinTone] */ function addEmojiReaction(reportID, reportActionID, emoji, skinTone = preferredSkinTone) { const createdAt = moment().utc().format(CONST.DATE.SQL_DATE_TIME); @@ -1522,7 +1525,10 @@ function addEmojiReaction(reportID, reportActionID, emoji, skinTone = preferredS * Uses the NEW FORMAT for "emojiReactions" * @param {String} reportID * @param {String} reportActionID - * @param {{ name: string, code: string, types: string[] }} emoji + * @param {Object} emoji + * @param {String} emoji.name + * @param {String} emoji.code + * @param {String[]} [emoji.types] */ function removeEmojiReaction(reportID, reportActionID, emoji) { const optimisticData = [ @@ -1554,7 +1560,10 @@ function removeEmojiReaction(reportID, reportActionID, emoji) { * Uses the NEW FORMAT for "emojiReactions" * @param {String} reportID * @param {Object} reportAction - * @param {{ name: string, code: string, types: string[] }} emoji + * @param {Object} emoji + * @param {String} emoji.name + * @param {String} emoji.code + * @param {String[]} [emoji.types] * @param {Object} existingReactions * @param {Number} [paramSkinTone] */ From 69a05a58e886494e208133ec9559320c038e202f Mon Sep 17 00:00:00 2001 From: Tim Golen Date: Tue, 30 May 2023 07:38:29 +0300 Subject: [PATCH 50/68] Fix other JSDocs --- src/components/AttachmentCarousel/index.js | 5 ++++- src/components/AttachmentPicker/index.native.js | 3 ++- src/components/ThumbnailImage.js | 4 +++- src/libs/EmojiUtils.js | 5 ++++- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/components/AttachmentCarousel/index.js b/src/components/AttachmentCarousel/index.js index 56f592bc5865..98b3871dccb3 100644 --- a/src/components/AttachmentCarousel/index.js +++ b/src/components/AttachmentCarousel/index.js @@ -258,7 +258,10 @@ class AttachmentCarousel extends React.Component { /** * Defines how a single attachment should be rendered - * @param {{ source: String, file: { name: String } }} item + * @param {Object} item + * @param {String} item.source + * @param {Object} item.file + * @param {String} item.file.name * @returns {JSX.Element} */ renderItem({item}) { diff --git a/src/components/AttachmentPicker/index.native.js b/src/components/AttachmentPicker/index.native.js index b0b1222316f0..b4b7d0b04c4e 100644 --- a/src/components/AttachmentPicker/index.native.js +++ b/src/components/AttachmentPicker/index.native.js @@ -315,7 +315,8 @@ class AttachmentPicker extends Component { /** * Setup native attachment selection to start after this popover closes * - * @param {{pickAttachment: function}} item - an item from this.menuItemData + * @param {Object} item - an item from this.menuItemData + * @param {Function} item.pickAttachment */ selectItem(item) { /* setTimeout delays execution to the frame after the modal closes diff --git a/src/components/ThumbnailImage.js b/src/components/ThumbnailImage.js index 5ac519c3bfbc..3eb114ef2fa0 100644 --- a/src/components/ThumbnailImage.js +++ b/src/components/ThumbnailImage.js @@ -78,7 +78,9 @@ class ThumbnailImage extends PureComponent { /** * Update the state with the computed thumbnail sizes. * - * @param {{ width: number, height: number }} Params - width and height of the original image. + * @param {Object} Params - width and height of the original image. + * @param {Number} Params.width + * @param {Number} Params.height */ updateImageSize({width, height}) { const {thumbnailWidth, thumbnailHeight} = this.calculateThumbnailImageSize(width, height); diff --git a/src/libs/EmojiUtils.js b/src/libs/EmojiUtils.js index f15f1f2d4d98..2f9c76b7a341 100644 --- a/src/libs/EmojiUtils.js +++ b/src/libs/EmojiUtils.js @@ -327,7 +327,10 @@ const getPreferredEmojiCode = (emoji, preferredSkinTone) => { * Given an emoji object and a list of senders it will return an * array of emoji codes, that represents all used variations of the * emoji. - * @param {{ name: string, code: string, types: string[] }} emojiAsset + * @param {Object} emojiAsset + * @param {String} emojiAsset.name + * @param {String} emojiAsset.code + * @param {String[]} [emojiAsset.types] * @param {Array} users * @return {string[]} * */ From f2cf7069f3aee7b5016c32aa627710e4a43c5501 Mon Sep 17 00:00:00 2001 From: Brandon Stites Date: Wed, 5 Jul 2023 13:06:50 -0600 Subject: [PATCH 51/68] Fix emojiReaction prop name --- src/pages/home/report/ReportActionItem.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 52756a47052b..50f8ba8bd984 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -361,7 +361,7 @@ function ReportActionItem(props) { { if (Session.isAnonymousUser()) { hideContextMenu(false); From d11717b59310d6ddd08bbdb3019fa89cb7ca98cc Mon Sep 17 00:00:00 2001 From: Brandon Stites Date: Wed, 5 Jul 2023 13:34:32 -0600 Subject: [PATCH 52/68] Fix memo check --- src/pages/home/report/ReportActionItem.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 50f8ba8bd984..51ac62a16fe3 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -558,7 +558,7 @@ export default compose( prevProps.isMostRecentIOUReportAction === nextProps.isMostRecentIOUReportAction && prevProps.hasOutstandingIOU === nextProps.hasOutstandingIOU && prevProps.shouldDisplayNewMarker === nextProps.shouldDisplayNewMarker && - !_.isEqual(prevProps.emojiReactions, nextProps.emojiReactions) && + _.isEqual(prevProps.emojiReactions, nextProps.emojiReactions) && _.isEqual(prevProps.action, nextProps.action) && lodashGet(prevProps.report, 'statusNum') === lodashGet(nextProps.report, 'statusNum') && lodashGet(prevProps.report, 'stateNum') === lodashGet(nextProps.report, 'stateNum') && From bf7f6f718ef7a065c20a33e3015832a81a7b52dc Mon Sep 17 00:00:00 2001 From: Brandon Stites Date: Wed, 5 Jul 2023 14:04:55 -0600 Subject: [PATCH 53/68] Fix test --- tests/actions/ReportTest.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/actions/ReportTest.js b/tests/actions/ReportTest.js index 551f09ae6b6e..b9a021ed5b6c 100644 --- a/tests/actions/ReportTest.js +++ b/tests/actions/ReportTest.js @@ -647,7 +647,6 @@ describe('actions/Report', () => { reportActionsReactions[key] = val; }, }); - let reportAction; let reportActionID; // Set up Onyx with some test user data @@ -664,8 +663,7 @@ describe('actions/Report', () => { return waitForPromisesToResolve(); }) .then(() => { - reportAction = _.first(_.values(reportActions)); - reportActionID = reportAction.reportActionID; + const resultAction = _.first(_.values(reportActions)); // Add a reaction to the comment Report.toggleEmojiReaction(REPORT_ID, resultAction.reportActionID, EMOJI); From ccf50b074512c9e29e3ff9de25b98b97b8aafa18 Mon Sep 17 00:00:00 2001 From: Brandon Stites Date: Wed, 5 Jul 2023 14:05:46 -0600 Subject: [PATCH 54/68] Lint --- src/pages/home/report/ReportActionItem.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index defd0ebc1be0..49c7f30f40ec 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -54,7 +54,6 @@ import ReportPreview from '../../../components/ReportActionItem/ReportPreview'; import ReportActionItemDraft from './ReportActionItemDraft'; import TaskPreview from '../../../components/ReportActionItem/TaskPreview'; import TaskAction from '../../../components/ReportActionItem/TaskAction'; -import Permissions from '../../../libs/Permissions'; import EmojiReactionsPropTypes from '../../../components/Reactions/EmojiReactionsPropTypes'; import * as Session from '../../../libs/actions/Session'; import {hideContextMenu} from './ContextMenu/ReportActionContextMenu'; @@ -93,9 +92,6 @@ const propTypes = { /** Stores user's preferred skin tone */ preferredSkinTone: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - /** List of betas available to current user */ - betas: PropTypes.arrayOf(PropTypes.string), - ...windowDimensionsPropTypes, emojiReactions: EmojiReactionsPropTypes, personalDetailsList: PropTypes.objectOf(personalDetailsPropType), @@ -536,9 +532,6 @@ export default compose( emojiReactions: { key: ({action}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${action.reportActionID}`, }, - betas: { - key: ONYXKEYS.BETAS, - }, }), )( memo( From 979066a5a32104d70dac8269cc1ff1fcdc528d43 Mon Sep 17 00:00:00 2001 From: Brandon Stites Date: Wed, 5 Jul 2023 15:01:02 -0600 Subject: [PATCH 55/68] Actually fix tests --- tests/actions/ReportTest.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/actions/ReportTest.js b/tests/actions/ReportTest.js index b9a021ed5b6c..b3cc2d774a22 100644 --- a/tests/actions/ReportTest.js +++ b/tests/actions/ReportTest.js @@ -558,7 +558,7 @@ describe('actions/Report', () => { reportActionID = reportAction.reportActionID; // Add a reaction to the comment - Report.toggleEmojiReaction(REPORT_ID, reportAction, EMOJI); + Report.toggleEmojiReaction(REPORT_ID, reportActionID, EMOJI); return waitForPromisesToResolve(); }) .then(() => { @@ -574,7 +574,7 @@ describe('actions/Report', () => { expect(reportActionReactionEmoji.users).toHaveProperty(`${TEST_USER_ACCOUNT_ID}`); // Now we remove the reaction - Report.toggleEmojiReaction(REPORT_ID, reportAction, EMOJI, reportActionReaction); + Report.toggleEmojiReaction(REPORT_ID, reportActionID, EMOJI, reportActionReaction); return waitForPromisesToResolve(); }) .then(() => { @@ -585,11 +585,11 @@ describe('actions/Report', () => { }) .then(() => { // Add the same reaction to the same report action with a different skintone - Report.toggleEmojiReaction(REPORT_ID, reportAction, EMOJI); + Report.toggleEmojiReaction(REPORT_ID, reportActionID, EMOJI); return waitForPromisesToResolve() .then(() => { const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`]; - Report.toggleEmojiReaction(REPORT_ID, reportAction, EMOJI, reportActionReaction, EMOJI_SKIN_TONE); + Report.toggleEmojiReaction(REPORT_ID, reportActionID, EMOJI, reportActionReaction, EMOJI_SKIN_TONE); return waitForPromisesToResolve(); }) .then(() => { @@ -610,7 +610,7 @@ describe('actions/Report', () => { expect(reportActionReactionEmojiUserSkinTones).toHaveProperty('2'); // Now we remove the reaction, and expect that both variations are removed - Report.toggleEmojiReaction(REPORT_ID, reportAction, EMOJI, reportActionReaction); + Report.toggleEmojiReaction(REPORT_ID, reportActionID, EMOJI, reportActionReaction); return waitForPromisesToResolve(); }) .then(() => { @@ -647,7 +647,8 @@ describe('actions/Report', () => { reportActionsReactions[key] = val; }, }); - let reportActionID; + + let resultAction; // Set up Onyx with some test user data return TestHelper.signInWithTestUser(TEST_USER_ACCOUNT_ID, TEST_USER_LOGIN) @@ -663,25 +664,24 @@ describe('actions/Report', () => { return waitForPromisesToResolve(); }) .then(() => { - const resultAction = _.first(_.values(reportActions)); + resultAction = _.first(_.values(reportActions)); // Add a reaction to the comment - Report.toggleEmojiReaction(REPORT_ID, resultAction.reportActionID, EMOJI); + Report.toggleEmojiReaction(REPORT_ID, resultAction.reportActionID, EMOJI, {}); return waitForPromisesToResolve(); }) .then(() => { - const resultAction = _.first(_.values(reportActions)); - // Now we toggle the reaction while the skin tone has changed. // As the emoji doesn't support skin tones, the emoji // should get removed instead of added again. - Report.toggleEmojiReaction(REPORT_ID, resultAction.reportActionID, EMOJI, 2); + const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${resultAction.reportActionID}`]; + Report.toggleEmojiReaction(REPORT_ID, resultAction.reportActionID, EMOJI, reportActionReaction, 2); return waitForPromisesToResolve(); }) .then(() => { // Expect the reaction to have null where the users reaction used to be - expect(reportActionsReactions).toHaveProperty(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`); - const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`]; + expect(reportActionsReactions).toHaveProperty(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${resultAction.reportActionID}`); + const reportActionReaction = reportActionsReactions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${resultAction.reportActionID}`]; expect(reportActionReaction[EMOJI.name].users[TEST_USER_ACCOUNT_ID]).toBeNull(); }); }); From 0ed88fd158f096d1663b7c44bbc6fbd9d710b059 Mon Sep 17 00:00:00 2001 From: Brandon Stites Date: Wed, 5 Jul 2023 15:06:18 -0600 Subject: [PATCH 56/68] Fix counting of skintones --- .../Reactions/ReportActionItemEmojiReactions.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/components/Reactions/ReportActionItemEmojiReactions.js b/src/components/Reactions/ReportActionItemEmojiReactions.js index 8f44df8800c4..3ab3b16444df 100644 --- a/src/components/Reactions/ReportActionItemEmojiReactions.js +++ b/src/components/Reactions/ReportActionItemEmojiReactions.js @@ -74,7 +74,14 @@ function ReportActionItemEmojiReactions(props) { {_.map(sortedReactions, (reaction) => { const reactionEmojiName = reaction.emojiName; const usersWithReactions = _.filter(reaction.users, (userData) => userData); - const reactionCount = _.size(usersWithReactions); + let reactionCount = 0; + _.forEach(usersWithReactions, (user) => { + if (user.skinTones) { + reactionCount += _.size(user.skinTones); + } else { + reactionCount += 1; + } + }); if (!reactionCount) { return null; } From f5fb5a1b4ea79a287e04d1101dbeb8f93c055a21 Mon Sep 17 00:00:00 2001 From: Brandon Stites Date: Wed, 5 Jul 2023 15:19:19 -0600 Subject: [PATCH 57/68] Remove unnecessary lodash get --- src/pages/home/report/ReportActionItem.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 49c7f30f40ec..531b6849fce8 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -329,7 +329,6 @@ function ReportActionItem(props) { ); } - const emojiReactions = lodashGet(props, ['emojiReactions'], {}); const numberOfThreadReplies = _.get(props, ['action', 'childVisibleActionCount'], 0); const hasReplies = numberOfThreadReplies > 0; @@ -348,7 +347,7 @@ function ReportActionItem(props) { { if (Session.isAnonymousUser()) { hideContextMenu(false); From 9d6e4e00918375a27a053f2c1364408073785874 Mon Sep 17 00:00:00 2001 From: Brandon Stites Date: Wed, 5 Jul 2023 15:22:16 -0600 Subject: [PATCH 58/68] Fix quick reactions not properly removing emojis --- src/pages/home/report/ContextMenu/ContextMenuActions.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.js b/src/pages/home/report/ContextMenu/ContextMenuActions.js index e340376f6f84..45907148f293 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.js +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.js @@ -57,8 +57,8 @@ export default [ } }; - const onEmojiSelected = (emoji) => { - Report.toggleEmojiReaction(reportID, reportAction.reportActionID, emoji); + const onEmojiSelected = (emoji, existingReactions) => { + Report.toggleEmojiReaction(reportID, reportAction.reportActionID, emoji, existingReactions); closeContextMenu(); }; From 86d836a7e1e7f2c84b0947fb133e894b19d4b197 Mon Sep 17 00:00:00 2001 From: Brandon Stites Date: Wed, 5 Jul 2023 15:52:12 -0600 Subject: [PATCH 59/68] Only get users who have reacted to display tooltip, and pass as number --- src/components/Reactions/ReportActionItemEmojiReactions.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/Reactions/ReportActionItemEmojiReactions.js b/src/components/Reactions/ReportActionItemEmojiReactions.js index 3ab3b16444df..cafe08bc65e0 100644 --- a/src/components/Reactions/ReportActionItemEmojiReactions.js +++ b/src/components/Reactions/ReportActionItemEmojiReactions.js @@ -73,7 +73,7 @@ function ReportActionItemEmojiReactions(props) { > {_.map(sortedReactions, (reaction) => { const reactionEmojiName = reaction.emojiName; - const usersWithReactions = _.filter(reaction.users, (userData) => userData); + const usersWithReactions = _.pick(reaction.users, _.identity); let reactionCount = 0; _.forEach(usersWithReactions, (user) => { if (user.skinTones) { @@ -89,7 +89,8 @@ function ReportActionItemEmojiReactions(props) { const emojiAsset = _.find(emojis, (emoji) => emoji.name === reactionEmojiName); const emojiCodes = EmojiUtils.getUniqueEmojiCodes(emojiAsset, reaction.users); const hasUserReacted = Report.hasAccountIDEmojiReacted(props.currentUserPersonalDetails.accountID, reaction.users); - const reactionUsers = _.keys(reaction.users); + const reactionUsers = _.keys(usersWithReactions); + const reactionUsersNumbers = _.map(reactionUsers, Number); const onPress = () => { props.toggleReaction(emojiAsset); @@ -105,7 +106,7 @@ function ReportActionItemEmojiReactions(props) { )} From f6593a43f70746d29dbf5f93c29b37f1600d8ef3 Mon Sep 17 00:00:00 2001 From: Brandon Stites Date: Thu, 6 Jul 2023 13:41:11 -0600 Subject: [PATCH 60/68] Simplify counting and update variable name --- .../Reactions/ReportActionItemEmojiReactions.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/components/Reactions/ReportActionItemEmojiReactions.js b/src/components/Reactions/ReportActionItemEmojiReactions.js index cafe08bc65e0..f8c780bad90f 100644 --- a/src/components/Reactions/ReportActionItemEmojiReactions.js +++ b/src/components/Reactions/ReportActionItemEmojiReactions.js @@ -75,12 +75,10 @@ function ReportActionItemEmojiReactions(props) { const reactionEmojiName = reaction.emojiName; const usersWithReactions = _.pick(reaction.users, _.identity); let reactionCount = 0; + + // Loop through the users who have reacted and see how many skintones they reacted with so that we get the total count _.forEach(usersWithReactions, (user) => { - if (user.skinTones) { - reactionCount += _.size(user.skinTones); - } else { - reactionCount += 1; - } + reactionCount += _.size(user.skinTones); }); if (!reactionCount) { return null; @@ -90,7 +88,7 @@ function ReportActionItemEmojiReactions(props) { const emojiCodes = EmojiUtils.getUniqueEmojiCodes(emojiAsset, reaction.users); const hasUserReacted = Report.hasAccountIDEmojiReacted(props.currentUserPersonalDetails.accountID, reaction.users); const reactionUsers = _.keys(usersWithReactions); - const reactionUsersNumbers = _.map(reactionUsers, Number); + const reactionUserAccountIDs = _.map(reactionUsers, Number); const onPress = () => { props.toggleReaction(emojiAsset); @@ -106,7 +104,7 @@ function ReportActionItemEmojiReactions(props) { )} From aa0c58775b2af027d7614b2b5c2420d113f468b3 Mon Sep 17 00:00:00 2001 From: Brandon Stites Date: Thu, 6 Jul 2023 13:41:29 -0600 Subject: [PATCH 61/68] Fix bad check that would fail for skintone==0 --- src/libs/actions/Report.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 70f764a3a780..c8553535b258 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1494,7 +1494,7 @@ function clearIOUError(reportID) { * @returns {boolean} */ function hasAccountIDEmojiReacted(accountID, users, skinTone) { - if (!skinTone) { + if (_.isUndefined(skinTone)) { return Boolean(users[accountID]); } const usersReaction = users[accountID]; From 47e6505b7440a286b6a5e56a063f4670faa117c8 Mon Sep 17 00:00:00 2001 From: Brandon Stites Date: Thu, 6 Jul 2023 13:41:34 -0600 Subject: [PATCH 62/68] Fix bad merge --- src/pages/home/report/ReportActionItem.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 531b6849fce8..3179c53ab2f7 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -202,7 +202,6 @@ function ReportActionItem(props) { const toggleReaction = useCallback( (emoji) => { - Report.toggleEmojiReaction(props.report.reportID, props.action, emoji, props.emojiReactions); Report.toggleEmojiReaction(props.report.reportID, props.action.reportActionID, emoji, props.emojiReactions); }, [props.report, props.action, props.emojiReactions], From 88e6d1aa7add8036fb72ca67badb8d0f587c1bb3 Mon Sep 17 00:00:00 2001 From: Brandon Stites Date: Thu, 6 Jul 2023 13:45:49 -0600 Subject: [PATCH 63/68] Fix param documentation --- src/components/AttachmentCarousel/index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/AttachmentCarousel/index.js b/src/components/AttachmentCarousel/index.js index 403915f32938..22c87f898386 100644 --- a/src/components/AttachmentCarousel/index.js +++ b/src/components/AttachmentCarousel/index.js @@ -265,7 +265,11 @@ class AttachmentCarousel extends React.Component { /** * Defines how a single attachment should be rendered - * @param {{ isAuthTokenRequired: Boolean, source: String, file: { name: String } }} item + * @param {Object} item + * @param {Boolean} item.isAuthTokenRequired + * @param {String} item.source + * @param {Object} item.file + * @param {String} item.file.name * @returns {JSX.Element} */ renderItem({item}) { From ae31431602adea36decb94dbf5ed129e0a078f83 Mon Sep 17 00:00:00 2001 From: Brandon Stites Date: Thu, 6 Jul 2023 13:47:22 -0600 Subject: [PATCH 64/68] Update name to follow conventions --- src/pages/home/report/ContextMenu/ContextMenuActions.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.js b/src/pages/home/report/ContextMenu/ContextMenuActions.js index 45907148f293..62f57e092a92 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.js +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.js @@ -57,7 +57,7 @@ export default [ } }; - const onEmojiSelected = (emoji, existingReactions) => { + const toggleEmojiAndCloseMenu = (emoji, existingReactions) => { Report.toggleEmojiReaction(reportID, reportAction.reportActionID, emoji, existingReactions); closeContextMenu(); }; @@ -66,7 +66,7 @@ export default [ return ( Date: Fri, 7 Jul 2023 16:14:33 -0600 Subject: [PATCH 65/68] Check for undefined to account for 0 --- src/libs/actions/Report.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index c8553535b258..e66e02717139 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1527,7 +1527,7 @@ function addEmojiReaction(reportID, reportActionID, emoji, skinTone = preferredS users: { [currentUserAccountID]: { skinTones: { - [skinTone || -1]: createdAt, + [!_.isUndefined(skinTone) ? skinTone : -1]: createdAt, }, }, }, From 846d059a3c47b8a493b974e2dec71d79b92abae6 Mon Sep 17 00:00:00 2001 From: Brandon Stites Date: Mon, 10 Jul 2023 16:56:09 -0600 Subject: [PATCH 66/68] Finish merge --- .../ReportActionItemEmojiReactions.js | 2 -- src/libs/actions/Report.js | 31 +++---------------- src/pages/home/report/ReportActionItem.js | 3 -- 3 files changed, 5 insertions(+), 31 deletions(-) diff --git a/src/components/Reactions/ReportActionItemEmojiReactions.js b/src/components/Reactions/ReportActionItemEmojiReactions.js index 75267007f751..6b0cc513bd06 100644 --- a/src/components/Reactions/ReportActionItemEmojiReactions.js +++ b/src/components/Reactions/ReportActionItemEmojiReactions.js @@ -86,8 +86,6 @@ function ReportActionItemEmojiReactions(props) { } totalReactionCount += reactionCount; const emojiAsset = EmojiUtils.findEmojiByName(reactionEmojiName); - // const emojiAsset = _.find(emojis, (emoji) => emoji.name === reactionEmojiName); - debugger; const emojiCodes = EmojiUtils.getUniqueEmojiCodes(emojiAsset, reaction.users); const hasUserReacted = Report.hasAccountIDEmojiReacted(props.currentUserPersonalDetails.accountID, reaction.users); const reactionUsers = _.keys(usersWithReactions); diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index eddeedef7074..5ec43652b847 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1595,49 +1595,28 @@ function removeEmojiReaction(reportID, reportActionID, emoji) { * Uses the NEW FORMAT for "emojiReactions" * @param {String} reportID * @param {String} reportActionID -<<<<<<< HEAD - * @param {Object} emoji - * @param {String} emoji.name - * @param {String} emoji.code - * @param {String[]} [emoji.types] + * @param {Object} reactionObject * @param {Object} existingReactions * @param {Number} [paramSkinTone] */ -function toggleEmojiReaction(reportID, reportActionID, emoji, existingReactions, paramSkinTone = preferredSkinTone) { -======= - * @param {Object} reactionEmoji - * @param {number} paramSkinTone - * @returns {Promise} - */ -function toggleEmojiReaction(reportID, reportActionID, reactionEmoji, paramSkinTone = preferredSkinTone) { ->>>>>>> 0df937588afc6379190d8838e31d29be524aa4c5 +function toggleEmojiReaction(reportID, reportActionID, reactionObject, existingReactions, paramSkinTone = preferredSkinTone) { const reportAction = ReportActionsUtils.getReportAction(reportID, reportActionID); if (_.isEmpty(reportAction)) { return; } -<<<<<<< HEAD // This will get cleaned up as part of https://github.com/Expensify/App/issues/16506 once the old emoji // format is no longer being used - const reactionObject = lodashGet(existingReactions, [emoji.name]); + const emoji = EmojiUtils.findEmojiByCode(reactionObject.code); + const existingReactionObject = lodashGet(existingReactions, [emoji.name]); // Only use skin tone if emoji supports it const skinTone = emoji.types === undefined ? -1 : paramSkinTone; - if (reactionObject && hasAccountIDEmojiReacted(currentUserAccountID, reactionObject.users, skinTone)) { + if (existingReactionObject && hasAccountIDEmojiReacted(currentUserAccountID, existingReactionObject.users, skinTone)) { removeEmojiReaction(reportID, reportAction.reportActionID, emoji); return; -======= - const emoji = EmojiUtils.findEmojiByCode(reactionEmoji.code); - const message = reportAction.message[0]; - const reactionObject = message.reactions && _.find(message.reactions, (reaction) => reaction.emoji === emoji.name); - const skinTone = emoji.types === undefined ? null : paramSkinTone; // only use skin tone if emoji supports it - if (reactionObject) { - if (hasAccountIDReacted(currentUserAccountID, reactionObject.users, skinTone)) { - return removeEmojiReaction(reportID, reportAction, emoji, skinTone); - } ->>>>>>> 0df937588afc6379190d8838e31d29be524aa4c5 } addEmojiReaction(reportID, reportAction.reportActionID, emoji, skinTone); diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index ccfb721bf847..9781739fc93a 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -54,11 +54,8 @@ import ReportPreview from '../../../components/ReportActionItem/ReportPreview'; import ReportActionItemDraft from './ReportActionItemDraft'; import TaskPreview from '../../../components/ReportActionItem/TaskPreview'; import TaskAction from '../../../components/ReportActionItem/TaskAction'; -<<<<<<< HEAD import EmojiReactionsPropTypes from '../../../components/Reactions/EmojiReactionsPropTypes'; -======= import TaskView from '../../../components/ReportActionItem/TaskView'; ->>>>>>> 0df937588afc6379190d8838e31d29be524aa4c5 import * as Session from '../../../libs/actions/Session'; import {hideContextMenu} from './ContextMenu/ReportActionContextMenu'; From a9e26e513abfe5b202ed8b6c92338b1a95fd9694 Mon Sep 17 00:00:00 2001 From: Brandon Stites Date: Mon, 10 Jul 2023 17:05:31 -0600 Subject: [PATCH 67/68] Style --- .../Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js | 2 +- src/components/Reactions/ReportActionItemEmojiReactions.js | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js b/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js index c540b93a1a5d..69ff37338c2d 100644 --- a/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js +++ b/src/components/Reactions/QuickEmojiReactions/BaseQuickEmojiReactions.js @@ -95,7 +95,7 @@ export default withOnyx({ key: ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, }, emojiReactions: { - key: ({reportActionID}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`, + key: ({reportActionID}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`, }, preferredLocale: { key: ONYXKEYS.NVP_PREFERRED_LOCALE, diff --git a/src/components/Reactions/ReportActionItemEmojiReactions.js b/src/components/Reactions/ReportActionItemEmojiReactions.js index 6b0cc513bd06..bb21f034c11c 100644 --- a/src/components/Reactions/ReportActionItemEmojiReactions.js +++ b/src/components/Reactions/ReportActionItemEmojiReactions.js @@ -136,7 +136,6 @@ function ReportActionItemEmojiReactions(props) { ); } - ReportActionItemEmojiReactions.displayName = 'ReportActionItemReactions'; ReportActionItemEmojiReactions.propTypes = propTypes; ReportActionItemEmojiReactions.defaultProps = defaultProps; From 73ca1376aa7b2ddd8c77a362a644dfd1b4e29b02 Mon Sep 17 00:00:00 2001 From: Brandon Stites Date: Mon, 17 Jul 2023 16:01:35 -0600 Subject: [PATCH 68/68] Remove unused prop --- src/libs/actions/Report.js | 2 +- src/pages/home/report/ContextMenu/ContextMenuActions.js | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index be9d7c669f14..7a6cbd1d9d45 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1654,7 +1654,7 @@ function removeEmojiReaction(reportID, reportActionID, emoji) { } /** - * Calls either addReaction or removeEmojiReaction depending on if the current user has reacted to the report action. + * Calls either addEmojiReaction or removeEmojiReaction depending on if the current user has reacted to the report action. * Uses the NEW FORMAT for "emojiReactions" * @param {String} reportID * @param {String} reportActionID diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.js b/src/pages/home/report/ContextMenu/ContextMenuActions.js index fe2ad95dfef5..a7596fdc07ef 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.js +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.js @@ -70,7 +70,6 @@ export default [ onEmojiSelected={toggleEmojiAndCloseMenu} onPressOpenPicker={openContextMenu} onEmojiPickerClosed={closeContextMenu} - reportID={reportID} reportActionID={reportAction.reportActionID} reportAction={reportAction} /> @@ -82,7 +81,6 @@ export default [ key="BaseQuickEmojiReactions" closeContextMenu={closeContextMenu} onEmojiSelected={toggleEmojiAndCloseMenu} - reportID={reportID} reportActionID={reportAction.reportActionID} reportAction={reportAction} />