From 62579b67689feb3f8ee976996e3b3b224a43c0ab Mon Sep 17 00:00:00 2001 From: Pavlo Tsimura Date: Fri, 6 Oct 2023 07:25:41 +0200 Subject: [PATCH 1/6] Implement emoji users ordering Signed-off-by: Pavlo Tsimura --- .../ReportActionItemEmojiReactions.js | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/components/Reactions/ReportActionItemEmojiReactions.js b/src/components/Reactions/ReportActionItemEmojiReactions.js index 7ead2ab67ae7..04eff3fa25ce 100644 --- a/src/components/Reactions/ReportActionItemEmojiReactions.js +++ b/src/components/Reactions/ReportActionItemEmojiReactions.js @@ -1,5 +1,6 @@ import React, {useRef, useContext} from 'react'; import lodashGet from 'lodash/get'; +import lodashMin from 'lodash/min'; import _ from 'underscore'; import {View} from 'react-native'; import PropTypes from 'prop-types'; @@ -53,38 +54,37 @@ function ReportActionItemEmojiReactions(props) { const reportActionID = reportAction.reportActionID; // 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; - const oldestUserReactionTimestamp = _.chain(emojiReaction.users) - .reduce((allTimestampsArray, userData) => { - if (!userData) { - return allTimestampsArray; - } - _.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; - }); + const sortedReactions = _.chain(props.emojiReactions) + .map((emojiReaction, emojiName) => { + const sortedUsers = _.chain(emojiReaction.users) + .pick(_.identity) + .map((user, id) => ({ + id, + ...user, + oldestTimestamp: lodashMin(_.values(user.skinTones)) + })) + .sortBy('oldestTimestamp') + .value(); + + return { + ...emojiReaction, + emojiName, + users: sortedUsers, + // 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. + oldestTimestamp: lodashGet(sortedUsers, ['0', 'oldestTimestamp'], emojiReaction.createdAt) + emojiName + }; + }) + .sortBy('oldestTimestamp') + .value(); const formattedReactions = _.map(sortedReactions, (reaction) => { 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) => { + _.forEach(reaction.users, (user) => { reactionCount += _.size(user.skinTones); }); if (!reactionCount) { @@ -94,7 +94,7 @@ function ReportActionItemEmojiReactions(props) { const emojiAsset = EmojiUtils.findEmojiByName(reactionEmojiName); const emojiCodes = EmojiUtils.getUniqueEmojiCodes(emojiAsset, reaction.users); const hasUserReacted = Report.hasAccountIDEmojiReacted(props.currentUserPersonalDetails.accountID, reaction.users); - const reactionUsers = _.keys(usersWithReactions); + const reactionUsers = _.map(reaction.users, 'id'); const reactionUserAccountIDs = _.map(reactionUsers, Number); const onPress = () => { From 846f9e066335b22a344fbda8723f47437058ae22 Mon Sep 17 00:00:00 2001 From: Pavlo Tsimura Date: Fri, 6 Oct 2023 18:32:41 +0200 Subject: [PATCH 2/6] Move to using EmojiUtils Signed-off-by: Pavlo Tsimura --- .../ReportActionItemEmojiReactions.js | 95 +++++++------------ src/libs/EmojiUtils.js | 90 ++++++++++++++++-- .../BasePopoverReactionList.js | 33 ++++--- 3 files changed, 136 insertions(+), 82 deletions(-) diff --git a/src/components/Reactions/ReportActionItemEmojiReactions.js b/src/components/Reactions/ReportActionItemEmojiReactions.js index 04eff3fa25ce..e1d604242673 100644 --- a/src/components/Reactions/ReportActionItemEmojiReactions.js +++ b/src/components/Reactions/ReportActionItemEmojiReactions.js @@ -1,6 +1,5 @@ import React, {useRef, useContext} from 'react'; import lodashGet from 'lodash/get'; -import lodashMin from 'lodash/min'; import _ from 'underscore'; import {View} from 'react-native'; import PropTypes from 'prop-types'; @@ -10,7 +9,6 @@ import AddReactionBubble from './AddReactionBubble'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '../withCurrentUserPersonalDetails'; import withLocalize from '../withLocalize'; import compose from '../../libs/compose'; -import * as Report from '../../libs/actions/Report'; import EmojiReactionsPropTypes from './EmojiReactionsPropTypes'; import Tooltip from '../Tooltip'; import ReactionTooltipContent from './ReactionTooltipContent'; @@ -53,70 +51,46 @@ function ReportActionItemEmojiReactions(props) { const reportAction = props.reportAction; const reportActionID = reportAction.reportActionID; - // 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 = _.chain(props.emojiReactions) + const formattedReactions = _.chain(props.emojiReactions) .map((emojiReaction, emojiName) => { - const sortedUsers = _.chain(emojiReaction.users) - .pick(_.identity) - .map((user, id) => ({ - id, - ...user, - oldestTimestamp: lodashMin(_.values(user.skinTones)) - })) - .sortBy('oldestTimestamp') - .value(); + const { + emoji, + emojiCodes, + reactionCount, + hasUserReacted, + userAccountIDs, + oldestTimestamp, + } = EmojiUtils.getEmojiReactionDetails(emojiName, emojiReaction, props.currentUserPersonalDetails.accountID); + + if (reactionCount === 0) { + return null; + } + totalReactionCount += reactionCount; + + const onPress = () => { + props.toggleReaction(emoji); + }; + + const onReactionListOpen = (event) => { + reactionListRef.current.showReactionList(event, popoverReactionListAnchors.current[emojiName], emojiName, reportActionID); + }; return { - ...emojiReaction, - emojiName, - users: sortedUsers, - // 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. - oldestTimestamp: lodashGet(sortedUsers, ['0', 'oldestTimestamp'], emojiReaction.createdAt) + emojiName + reactionEmojiName: emojiName, + emojiCodes, + userAccountIDs, + reactionCount, + hasUserReacted, + onPress, + onReactionListOpen, + pendingAction: emojiReaction.pendingAction, + oldestTimestamp: lodashGet(oldestTimestamp, 'oldestTimestamp'), }; }) + // Each emoji is sorted by the oldest timestamp of user reactions so that they will always appear in the same order for everyone .sortBy('oldestTimestamp') .value(); - const formattedReactions = _.map(sortedReactions, (reaction) => { - const reactionEmojiName = reaction.emojiName; - 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(reaction.users, (user) => { - reactionCount += _.size(user.skinTones); - }); - if (!reactionCount) { - return null; - } - totalReactionCount += reactionCount; - const emojiAsset = EmojiUtils.findEmojiByName(reactionEmojiName); - const emojiCodes = EmojiUtils.getUniqueEmojiCodes(emojiAsset, reaction.users); - const hasUserReacted = Report.hasAccountIDEmojiReacted(props.currentUserPersonalDetails.accountID, reaction.users); - const reactionUsers = _.map(reaction.users, 'id'); - const reactionUserAccountIDs = _.map(reactionUsers, Number); - - const onPress = () => { - props.toggleReaction(emojiAsset); - }; - - const onReactionListOpen = (event) => { - reactionListRef.current.showReactionList(event, popoverReactionListAnchors.current[reactionEmojiName], reactionEmojiName, reportActionID); - }; - - return { - reactionEmojiName, - emojiCodes, - reactionUserAccountIDs, - onPress, - reactionUsers, - reactionCount, - hasUserReacted, - onReactionListOpen, - pendingAction: reaction.pendingAction, - }; - }); return ( totalReactionCount > 0 && ( @@ -131,11 +105,11 @@ function ReportActionItemEmojiReactions(props) { )} - renderTooltipContentKey={[..._.map(reaction.reactionUsers, (user) => user.toString()), ...reaction.emojiCodes]} + renderTooltipContentKey={[..._.map(reaction.userAccountIDs, String), ...reaction.emojiCodes]} key={reaction.reactionEmojiName} > @@ -148,7 +122,6 @@ function ReportActionItemEmojiReactions(props) { count={reaction.reactionCount} emojiCodes={reaction.emojiCodes} onPress={reaction.onPress} - reactionUsers={reaction.reactionUsers} hasUserReacted={reaction.hasUserReacted} onReactionListOpen={reaction.onReactionListOpen} shouldBlockReactions={props.shouldBlockReactions} diff --git a/src/libs/EmojiUtils.js b/src/libs/EmojiUtils.js index 80665541e24b..1b224f6ebd0e 100644 --- a/src/libs/EmojiUtils.js +++ b/src/libs/EmojiUtils.js @@ -7,6 +7,9 @@ import ONYXKEYS from '../ONYXKEYS'; import CONST from '../CONST'; import emojisTrie from './EmojiTrie'; import * as Emojis from '../../assets/emojis'; +import lodashMin from "lodash/min"; +import lodashSum from "lodash/sum"; +import * as Report from "./actions/Report"; let frequentlyUsedEmojis = []; Onyx.connect({ @@ -80,7 +83,7 @@ const getEmojiUnicode = _.memoize((input) => { const pairs = []; - // Some Emojis in UTF-16 are stored as pair of 2 Unicode characters (eg Flags) + // Some Emojis in UTF-16 are stored as a pair of 2 Unicode characters (e.g. Flags) // The first char is generally between the range U+D800 to U+DBFF called High surrogate // & the second char between the range U+DC00 to U+DFFF called low surrogate // More info in the following links: @@ -446,7 +449,7 @@ 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. + * emoji, sorted by the reaction timestamp. * @param {Object} emojiAsset * @param {String} emojiAsset.name * @param {String} emojiAsset.code @@ -455,18 +458,88 @@ const getPreferredEmojiCode = (emoji, preferredSkinTone) => { * @return {string[]} * */ const getUniqueEmojiCodes = (emojiAsset, users) => { - const uniqueEmojiCodes = []; - _.each(users, (userSkinTones) => { + const emojiCodes = _.reduce(users, (result, userSkinTones) => { _.each(lodashGet(userSkinTones, 'skinTones'), (createdAt, skinTone) => { const emojiCode = getPreferredEmojiCode(emojiAsset, skinTone); - if (emojiCode && !uniqueEmojiCodes.includes(emojiCode)) { - uniqueEmojiCodes.push(emojiCode); + if (!!emojiCode && (!result[emojiCode] || createdAt < result[emojiCode])) { + result[emojiCode] = createdAt; } }); - }); - return uniqueEmojiCodes; + return result; + }, {}); + + return _.chain(emojiCodes) + .pairs() + .sortBy((entry) => new Date(entry[1])) // Sort by values (timestamps) + .map((entry) => entry[0]) // Extract keys (emoji codes) + .value(); }; +/** + * Given an emoji reaction object and its name, it populates it with the oldest reaction timestamps. + * @param {Object} emoji + * @param {String} emojiName + * @returns {Object} + */ +const enrichEmojiReactionWithTimestamps = (emoji, emojiName) => { + let oldestEmojiTimestamp = null; + + let usersWithTimestamps = _.chain(emoji.users) + .pick(_.identity) + .mapObject((user, id) => { + const oldestUserTimestamp = lodashMin(_.values(user.skinTones)); + + if (!oldestEmojiTimestamp || oldestUserTimestamp < oldestEmojiTimestamp) { + oldestEmojiTimestamp = oldestUserTimestamp; + } + + return { + ...user, + id, + oldestTimestamp: oldestUserTimestamp + } + }) + .value(); + + return { + ...emoji, + users: usersWithTimestamps, + // 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. + oldestTimestamp: (oldestEmojiTimestamp || emoji.createdAt) + emojiName + }; +}; + +/** + * Given an emoji reaction and current user's account ID, it returns the reusable details of the emoji reaction. + * @param {String} emojiName + * @param {Object} reaction + * @param {String} currentUserAccountID + * @returns {Object} + */ +const getEmojiReactionDetails = (emojiName, reaction, currentUserAccountID) => { + const { users, oldestTimestamp } = enrichEmojiReactionWithTimestamps(reaction, emojiName); + + const emoji = findEmojiByName(emojiName); + const emojiCodes = getUniqueEmojiCodes(emoji, users); + const reactionCount = lodashSum(_.map(users, (user) => _.size(user.skinTones))); + const hasUserReacted = Report.hasAccountIDEmojiReacted(currentUserAccountID, users); + const userAccountIDs = _.chain(users) + .sortBy('oldestTimestamp') + .map((user) => Number(user.id)) + .value(); + + return { + emoji, + emojiCodes, + reactionCount, + hasUserReacted, + userAccountIDs, + oldestTimestamp, + } +} + export { findEmojiByName, findEmojiByCode, @@ -483,5 +556,6 @@ export { getPreferredSkinToneIndex, getPreferredEmojiCode, getUniqueEmojiCodes, + getEmojiReactionDetails, replaceAndExtractEmojis, }; diff --git a/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.js b/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.js index 9303d7a5bc39..db8c3ec177f6 100644 --- a/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.js +++ b/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.js @@ -4,7 +4,6 @@ import lodashGet from 'lodash/get'; import _ from 'underscore'; import {withOnyx} from 'react-native-onyx'; import PropTypes from 'prop-types'; -import * as Report from '../../../../../libs/actions/Report'; import withLocalize, {withLocalizePropTypes} from '../../../../../components/withLocalize'; import PopoverWithMeasuredContent from '../../../../../components/PopoverWithMeasuredContent'; import BaseReactionList from '../BaseReactionList'; @@ -121,30 +120,32 @@ class BasePopoverReactionList extends React.Component { * Get the reaction information. * * @param {Object} selectedReaction + * @param {String} emojiName * @returns {Object} */ - getReactionInformation(selectedReaction) { + getReactionInformation(selectedReaction, emojiName) { if (!selectedReaction) { return { emojiName: '', - emojiCount: 0, + reactionCount: 0, emojiCodes: [], hasUserReacted: false, users: [], }; } - const reactionUsers = _.pick(selectedReaction.users, _.identity); - const emojiCount = _.map(reactionUsers, (user) => user).length; - const userAccountIDs = _.map(reactionUsers, (user, accountID) => Number(accountID)); - const emojiName = selectedReaction.emojiName; - const emoji = EmojiUtils.findEmojiByName(emojiName); - const emojiCodes = EmojiUtils.getUniqueEmojiCodes(emoji, selectedReaction.users); - const hasUserReacted = Report.hasAccountIDEmojiReacted(this.props.currentUserPersonalDetails.accountID, reactionUsers); + + const { + emojiCodes, + reactionCount, + hasUserReacted, + userAccountIDs + } = EmojiUtils.getEmojiReactionDetails(emojiName, selectedReaction, this.props.currentUserPersonalDetails.accountID); + const users = PersonalDetailsUtils.getPersonalDetailsByIDs(userAccountIDs, this.props.currentUserPersonalDetails.accountID, true); return { emojiName, - emojiCount, emojiCodes, + reactionCount, hasUserReacted, users, }; @@ -205,7 +206,13 @@ class BasePopoverReactionList extends React.Component { render() { const selectedReaction = this.state.isPopoverVisible ? lodashGet(this.props.emojiReactions, [this.props.emojiName]) : null; - const {emojiName, emojiCount, emojiCodes, hasUserReacted, users} = this.getReactionInformation(selectedReaction); + const { + emojiName, + emojiCodes, + reactionCount, + hasUserReacted, + users + } = this.getReactionInformation(selectedReaction, this.props.emojiName); return ( From 0f31b23677ce83d7baa29cc23fc3dfc100353da0 Mon Sep 17 00:00:00 2001 From: Pavlo Tsimura Date: Fri, 6 Oct 2023 18:34:41 +0200 Subject: [PATCH 3/6] Prettier Signed-off-by: Pavlo Tsimura --- .../ReportActionItemEmojiReactions.js | 14 +++---- src/libs/EmojiUtils.js | 40 ++++++++++--------- .../BasePopoverReactionList.js | 15 +------ 3 files changed, 29 insertions(+), 40 deletions(-) diff --git a/src/components/Reactions/ReportActionItemEmojiReactions.js b/src/components/Reactions/ReportActionItemEmojiReactions.js index e1d604242673..388a028809c2 100644 --- a/src/components/Reactions/ReportActionItemEmojiReactions.js +++ b/src/components/Reactions/ReportActionItemEmojiReactions.js @@ -53,14 +53,11 @@ function ReportActionItemEmojiReactions(props) { const formattedReactions = _.chain(props.emojiReactions) .map((emojiReaction, emojiName) => { - const { - emoji, - emojiCodes, - reactionCount, - hasUserReacted, - userAccountIDs, - oldestTimestamp, - } = EmojiUtils.getEmojiReactionDetails(emojiName, emojiReaction, props.currentUserPersonalDetails.accountID); + const {emoji, emojiCodes, reactionCount, hasUserReacted, userAccountIDs, oldestTimestamp} = EmojiUtils.getEmojiReactionDetails( + emojiName, + emojiReaction, + props.currentUserPersonalDetails.accountID, + ); if (reactionCount === 0) { return null; @@ -91,7 +88,6 @@ function ReportActionItemEmojiReactions(props) { .sortBy('oldestTimestamp') .value(); - return ( totalReactionCount > 0 && ( diff --git a/src/libs/EmojiUtils.js b/src/libs/EmojiUtils.js index 1b224f6ebd0e..266e22fbaf0f 100644 --- a/src/libs/EmojiUtils.js +++ b/src/libs/EmojiUtils.js @@ -7,9 +7,9 @@ import ONYXKEYS from '../ONYXKEYS'; import CONST from '../CONST'; import emojisTrie from './EmojiTrie'; import * as Emojis from '../../assets/emojis'; -import lodashMin from "lodash/min"; -import lodashSum from "lodash/sum"; -import * as Report from "./actions/Report"; +import lodashMin from 'lodash/min'; +import lodashSum from 'lodash/sum'; +import * as Report from './actions/Report'; let frequentlyUsedEmojis = []; Onyx.connect({ @@ -458,15 +458,19 @@ const getPreferredEmojiCode = (emoji, preferredSkinTone) => { * @return {string[]} * */ const getUniqueEmojiCodes = (emojiAsset, users) => { - const emojiCodes = _.reduce(users, (result, userSkinTones) => { - _.each(lodashGet(userSkinTones, 'skinTones'), (createdAt, skinTone) => { - const emojiCode = getPreferredEmojiCode(emojiAsset, skinTone); - if (!!emojiCode && (!result[emojiCode] || createdAt < result[emojiCode])) { - result[emojiCode] = createdAt; - } - }); - return result; - }, {}); + const emojiCodes = _.reduce( + users, + (result, userSkinTones) => { + _.each(lodashGet(userSkinTones, 'skinTones'), (createdAt, skinTone) => { + const emojiCode = getPreferredEmojiCode(emojiAsset, skinTone); + if (!!emojiCode && (!result[emojiCode] || createdAt < result[emojiCode])) { + result[emojiCode] = createdAt; + } + }); + return result; + }, + {}, + ); return _.chain(emojiCodes) .pairs() @@ -496,8 +500,8 @@ const enrichEmojiReactionWithTimestamps = (emoji, emojiName) => { return { ...user, id, - oldestTimestamp: oldestUserTimestamp - } + oldestTimestamp: oldestUserTimestamp, + }; }) .value(); @@ -507,7 +511,7 @@ const enrichEmojiReactionWithTimestamps = (emoji, emojiName) => { // 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. - oldestTimestamp: (oldestEmojiTimestamp || emoji.createdAt) + emojiName + oldestTimestamp: (oldestEmojiTimestamp || emoji.createdAt) + emojiName, }; }; @@ -519,7 +523,7 @@ const enrichEmojiReactionWithTimestamps = (emoji, emojiName) => { * @returns {Object} */ const getEmojiReactionDetails = (emojiName, reaction, currentUserAccountID) => { - const { users, oldestTimestamp } = enrichEmojiReactionWithTimestamps(reaction, emojiName); + const {users, oldestTimestamp} = enrichEmojiReactionWithTimestamps(reaction, emojiName); const emoji = findEmojiByName(emojiName); const emojiCodes = getUniqueEmojiCodes(emoji, users); @@ -537,8 +541,8 @@ const getEmojiReactionDetails = (emojiName, reaction, currentUserAccountID) => { hasUserReacted, userAccountIDs, oldestTimestamp, - } -} + }; +}; export { findEmojiByName, diff --git a/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.js b/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.js index db8c3ec177f6..32433cc80ca5 100644 --- a/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.js +++ b/src/pages/home/report/ReactionList/PopoverReactionList/BasePopoverReactionList.js @@ -134,12 +134,7 @@ class BasePopoverReactionList extends React.Component { }; } - const { - emojiCodes, - reactionCount, - hasUserReacted, - userAccountIDs - } = EmojiUtils.getEmojiReactionDetails(emojiName, selectedReaction, this.props.currentUserPersonalDetails.accountID); + const {emojiCodes, reactionCount, hasUserReacted, userAccountIDs} = EmojiUtils.getEmojiReactionDetails(emojiName, selectedReaction, this.props.currentUserPersonalDetails.accountID); const users = PersonalDetailsUtils.getPersonalDetailsByIDs(userAccountIDs, this.props.currentUserPersonalDetails.accountID, true); return { @@ -206,13 +201,7 @@ class BasePopoverReactionList extends React.Component { render() { const selectedReaction = this.state.isPopoverVisible ? lodashGet(this.props.emojiReactions, [this.props.emojiName]) : null; - const { - emojiName, - emojiCodes, - reactionCount, - hasUserReacted, - users - } = this.getReactionInformation(selectedReaction, this.props.emojiName); + const {emojiName, emojiCodes, reactionCount, hasUserReacted, users} = this.getReactionInformation(selectedReaction, this.props.emojiName); return ( Date: Tue, 10 Oct 2023 20:23:03 +0200 Subject: [PATCH 4/6] Handle user details ordering Signed-off-by: Pavlo Tsimura --- src/libs/PersonalDetailsUtils.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/libs/PersonalDetailsUtils.js b/src/libs/PersonalDetailsUtils.js index 6b9335ab263d..6b9dce48903f 100644 --- a/src/libs/PersonalDetailsUtils.js +++ b/src/libs/PersonalDetailsUtils.js @@ -36,21 +36,21 @@ function getDisplayNameOrDefault(passedPersonalDetails, pathToDisplayName, defau * @returns {Array} - Array of personal detail objects */ function getPersonalDetailsByIDs(accountIDs, currentUserAccountID, shouldChangeUserDisplayName = false) { - const result = []; - _.each( - _.filter(personalDetails, (detail) => accountIDs.includes(detail.accountID)), - (detail) => { + return _.chain(accountIDs) + .filter((accountID) => !!allPersonalDetails[accountID]) + .map((accountID) => { + const detail = allPersonalDetails[accountID]; + if (shouldChangeUserDisplayName && currentUserAccountID === detail.accountID) { - result.push({ + return { ...detail, displayName: Localize.translateLocal('common.you'), - }); + }; } else { - result.push(detail); + return detail; } - }, - ); - return result; + }) + .value(); } /** From db8a97b9b087abc232f7918fee01323730c48641 Mon Sep 17 00:00:00 2001 From: Pavlo Tsimura Date: Wed, 18 Oct 2023 09:48:15 +0200 Subject: [PATCH 5/6] Move hasAccountIDEmojiReacted inside EmojiUtils --- src/libs/EmojiUtils.js | 30 +++++++++++++++++++++++++----- src/libs/PersonalDetailsUtils.js | 4 ++-- src/libs/actions/Report.js | 22 +--------------------- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/libs/EmojiUtils.js b/src/libs/EmojiUtils.js index 1202adc0993d..05ad1bd3c2ce 100644 --- a/src/libs/EmojiUtils.js +++ b/src/libs/EmojiUtils.js @@ -3,13 +3,12 @@ import {getUnixTime} from 'date-fns'; import Str from 'expensify-common/lib/str'; import Onyx from 'react-native-onyx'; import lodashGet from 'lodash/get'; +import lodashMin from 'lodash/min'; +import lodashSum from 'lodash/sum'; import ONYXKEYS from '../ONYXKEYS'; import CONST from '../CONST'; import emojisTrie from './EmojiTrie'; import * as Emojis from '../../assets/emojis'; -import lodashMin from 'lodash/min'; -import lodashSum from 'lodash/sum'; -import * as Report from './actions/Report'; let frequentlyUsedEmojis = []; Onyx.connect({ @@ -492,6 +491,7 @@ const getUniqueEmojiCodes = (emojiAsset, users) => { _.each(lodashGet(userSkinTones, 'skinTones'), (createdAt, skinTone) => { const emojiCode = getPreferredEmojiCode(emojiAsset, skinTone); if (!!emojiCode && (!result[emojiCode] || createdAt < result[emojiCode])) { + // eslint-disable-next-line no-param-reassign result[emojiCode] = createdAt; } }); @@ -516,7 +516,7 @@ const getUniqueEmojiCodes = (emojiAsset, users) => { const enrichEmojiReactionWithTimestamps = (emoji, emojiName) => { let oldestEmojiTimestamp = null; - let usersWithTimestamps = _.chain(emoji.users) + const usersWithTimestamps = _.chain(emoji.users) .pick(_.identity) .mapObject((user, id) => { const oldestUserTimestamp = lodashMin(_.values(user.skinTones)); @@ -543,6 +543,25 @@ const enrichEmojiReactionWithTimestamps = (emoji, emojiName) => { }; }; +/** + * 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} usersReactions - all the users reactions + * @param {Number} [skinTone] + * @returns {boolean} + */ +function hasAccountIDEmojiReacted(accountID, usersReactions, skinTone) { + if (_.isUndefined(skinTone)) { + return Boolean(usersReactions[accountID]); + } + const userReaction = usersReactions[accountID]; + if (!userReaction || !userReaction.skinTones || !_.size(userReaction.skinTones)) { + return false; + } + return Boolean(userReaction.skinTones[skinTone]); +} + /** * Given an emoji reaction and current user's account ID, it returns the reusable details of the emoji reaction. * @param {String} emojiName @@ -556,7 +575,7 @@ const getEmojiReactionDetails = (emojiName, reaction, currentUserAccountID) => { const emoji = findEmojiByName(emojiName); const emojiCodes = getUniqueEmojiCodes(emoji, users); const reactionCount = lodashSum(_.map(users, (user) => _.size(user.skinTones))); - const hasUserReacted = Report.hasAccountIDEmojiReacted(currentUserAccountID, users); + const hasUserReacted = hasAccountIDEmojiReacted(currentUserAccountID, users); const userAccountIDs = _.chain(users) .sortBy('oldestTimestamp') .map((user) => Number(user.id)) @@ -593,4 +612,5 @@ export { extractEmojis, getAddedEmojis, isFirstLetterEmoji, + hasAccountIDEmojiReacted, }; diff --git a/src/libs/PersonalDetailsUtils.js b/src/libs/PersonalDetailsUtils.js index 6b9dce48903f..29c49427bc81 100644 --- a/src/libs/PersonalDetailsUtils.js +++ b/src/libs/PersonalDetailsUtils.js @@ -46,9 +46,9 @@ function getPersonalDetailsByIDs(accountIDs, currentUserAccountID, shouldChangeU ...detail, displayName: Localize.translateLocal('common.you'), }; - } else { - return detail; } + + return detail; }) .value(); } diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index c9f3ba6318db..306df62727d5 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1728,25 +1728,6 @@ function clearIOUError(reportID) { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {errorFields: {iou: null}}); } -/** - * 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) { - if (_.isUndefined(skinTone)) { - return Boolean(users[accountID]); - } - const usersReaction = users[accountID]; - if (!usersReaction || !usersReaction.skinTones || !_.size(usersReaction.skinTones)) { - return false; - } - return Boolean(usersReaction.skinTones[skinTone]); -} - /** * Adds a reaction to the report action. * Uses the NEW FORMAT for "emojiReactions" @@ -1876,7 +1857,7 @@ function toggleEmojiReaction(reportID, reportAction, reactionObject, existingRea // Only use skin tone if emoji supports it const skinTone = emoji.types === undefined ? -1 : paramSkinTone; - if (existingReactionObject && hasAccountIDEmojiReacted(currentUserAccountID, existingReactionObject.users, skinTone)) { + if (existingReactionObject && EmojiUtils.hasAccountIDEmojiReacted(currentUserAccountID, existingReactionObject.users, skinTone)) { removeEmojiReaction(originalReportID, reportAction.reportActionID, emoji); return; } @@ -2322,7 +2303,6 @@ export { notifyNewAction, showReportActionNotification, toggleEmojiReaction, - hasAccountIDEmojiReacted, shouldShowReportActionNotification, leaveRoom, getCurrentUserAccountID, From 618739d83d1ac3357b64fa53bd7fdeb5346121ca Mon Sep 17 00:00:00 2001 From: Pavlo Tsimura Date: Thu, 19 Oct 2023 15:57:52 +0200 Subject: [PATCH 6/6] Fix oldestTimestamp --- src/components/Reactions/ReportActionItemEmojiReactions.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Reactions/ReportActionItemEmojiReactions.js b/src/components/Reactions/ReportActionItemEmojiReactions.js index 388a028809c2..5fdf74f877dd 100644 --- a/src/components/Reactions/ReportActionItemEmojiReactions.js +++ b/src/components/Reactions/ReportActionItemEmojiReactions.js @@ -73,15 +73,15 @@ function ReportActionItemEmojiReactions(props) { }; return { - reactionEmojiName: emojiName, emojiCodes, userAccountIDs, reactionCount, hasUserReacted, + oldestTimestamp, onPress, onReactionListOpen, + reactionEmojiName: emojiName, pendingAction: emojiReaction.pendingAction, - oldestTimestamp: lodashGet(oldestTimestamp, 'oldestTimestamp'), }; }) // Each emoji is sorted by the oldest timestamp of user reactions so that they will always appear in the same order for everyone