From 8f48cda525b66eb87b4fa9ddf06ae94a6a2be8f9 Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Mon, 19 Jun 2023 15:55:11 +0300 Subject: [PATCH 01/10] Start keying typers by accountID --- src/libs/actions/Report.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 8c62046dea5a..ee29b2866fbd 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -95,6 +95,8 @@ function getReportChannelName(reportID) { function getNormalizedTypingStatus(typingStatus) { let normalizedTypingStatus = typingStatus; + // TODO: figure out what to do from here + // probably look up login in personalDetails and convert to accountID if it exists if (_.first(_.keys(typingStatus)) === 'userLogin') { normalizedTypingStatus = {[typingStatus.userLogin]: true}; } @@ -118,26 +120,26 @@ function subscribeToReportTypingEvents(reportID) { const pusherChannelName = getReportChannelName(reportID); Pusher.subscribe(pusherChannelName, Pusher.TYPE.USER_IS_TYPING, (typingStatus) => { const normalizedTypingStatus = getNormalizedTypingStatus(typingStatus); - const login = _.first(_.keys(normalizedTypingStatus)); + const accountID = _.first(_.keys(normalizedTypingStatus)); - if (!login) { + if (!accountID) { return; } - // Don't show the typing indicator if a user is typing on another platform - if (login === currentUserEmail) { + // Don't show the typing indicator if the user is typing on another platform + if (accountID === currentUserAccountID) { return; } - // Use a combo of the reportID and the login as a key for holding our timers. - const reportUserIdentifier = `${reportID}-${login}`; + // Use a combo of the reportID and the accountID as a key for holding our timers. + const reportUserIdentifier = `${reportID}-${accountID}`; clearTimeout(typingWatchTimers[reportUserIdentifier]); Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_TYPING}${reportID}`, normalizedTypingStatus); // Wait for 1.5s of no additional typing events before setting the status back to false. typingWatchTimers[reportUserIdentifier] = setTimeout(() => { const typingStoppedStatus = {}; - typingStoppedStatus[login] = false; + typingStoppedStatus[accountID] = false; Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_TYPING}${reportID}`, typingStoppedStatus); delete typingWatchTimers[reportUserIdentifier]; }, 1500); @@ -747,7 +749,7 @@ function setReportWithDraft(reportID, hasDraft) { function broadcastUserIsTyping(reportID) { const privateReportChannelName = getReportChannelName(reportID); const typingStatus = {}; - typingStatus[currentUserEmail] = true; + typingStatus[currentUserAccountID] = true; Pusher.sendEvent(privateReportChannelName, Pusher.TYPE.USER_IS_TYPING, typingStatus); } From 29ab3460a18c26e03760a377bc12e3a72b21bb90 Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Mon, 19 Jun 2023 15:58:07 +0300 Subject: [PATCH 02/10] Rename allPersonalDetails to be clear --- src/libs/actions/PersonalDetails.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libs/actions/PersonalDetails.js b/src/libs/actions/PersonalDetails.js index 53680f65a1ec..dac9a1ccbef6 100644 --- a/src/libs/actions/PersonalDetails.js +++ b/src/libs/actions/PersonalDetails.js @@ -20,10 +20,10 @@ Onyx.connect({ }, }); -let personalDetails; +let allPersonalDetails; Onyx.connect({ key: ONYXKEYS.PERSONAL_DETAILS_LIST, - callback: (val) => (personalDetails = val), + callback: (val) => (allPersonalDetails = val), }); /** @@ -37,7 +37,7 @@ function getDisplayName(login, personalDetail) { // If we have a number like +15857527441@expensify.sms then let's remove @expensify.sms and format it // so that the option looks cleaner in our UI. const userLogin = LocalePhoneNumber.formatPhoneNumber(login); - const userDetails = personalDetail || lodashGet(personalDetails, login); + const userDetails = personalDetail || lodashGet(allPersonalDetails, login); if (!userDetails) { return userLogin; @@ -402,8 +402,8 @@ function updateAvatar(file) { key: ONYXKEYS.PERSONAL_DETAILS_LIST, value: { [currentUserAccountID]: { - avatar: personalDetails[currentUserAccountID].avatar, - avatarThumbnail: personalDetails[currentUserAccountID].avatarThumbnail || personalDetails[currentUserAccountID].avatar, + avatar: allPersonalDetails[currentUserAccountID].avatar, + avatarThumbnail: allPersonalDetails[currentUserAccountID].avatarThumbnail || allPersonalDetails[currentUserAccountID].avatar, pendingFields: { avatar: null, }, @@ -439,7 +439,7 @@ function deleteAvatar() { key: ONYXKEYS.PERSONAL_DETAILS_LIST, value: { [currentUserAccountID]: { - avatar: personalDetails[currentUserAccountID].avatar, + avatar: allPersonalDetails[currentUserAccountID].avatar, }, }, }, From f9478fade446933a9e94df6971e5217152cd532b Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Mon, 19 Jun 2023 15:58:21 +0300 Subject: [PATCH 03/10] New function for getting displayName by accountID --- src/libs/actions/PersonalDetails.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/libs/actions/PersonalDetails.js b/src/libs/actions/PersonalDetails.js index dac9a1ccbef6..88651fad3ac8 100644 --- a/src/libs/actions/PersonalDetails.js +++ b/src/libs/actions/PersonalDetails.js @@ -50,6 +50,23 @@ function getDisplayName(login, personalDetail) { return fullName || userLogin; } +/** + * + * @param {Number} accountID + * @param {String} [defaultDisplayName] display name to use if user details don't exist in Onyx or if + * found details don't include the user's displayName or login + * @returns {String} + */ +function getDisplayNameByAccountID(accountID, defaultDisplayName = '') { + const userDetails = allPersonalDetails && lodashGet(allPersonalDetails, accountID, {}); + + if (_.isEmpty(userDetails)) { + return defaultDisplayName; + } + + return userDetails.displayName || getDisplayName(userDetails.login || '', userDetails) || defaultDisplayName; +} + /** * Gets the first and last name from the user's personal details. * If the login is the same as the displayName, then they don't exist, @@ -466,6 +483,7 @@ function clearAvatarErrors() { export { getDisplayName, + getDisplayNameByAccountID, updateAvatar, deleteAvatar, openMoneyRequestModalPage, From b3115506c2aa019b8e11968caefeaa927ad4a29f Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Mon, 19 Jun 2023 15:59:20 +0300 Subject: [PATCH 04/10] Get typing user display name by accountID --- src/pages/home/report/ReportTypingIndicator.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/home/report/ReportTypingIndicator.js b/src/pages/home/report/ReportTypingIndicator.js index 38e857e88d77..062190281a1c 100755 --- a/src/pages/home/report/ReportTypingIndicator.js +++ b/src/pages/home/report/ReportTypingIndicator.js @@ -27,7 +27,7 @@ const defaultProps = { }; function ReportTypingIndicator(props) { - const usersTyping = useMemo(() => _.filter(_.keys(props.userTypingStatuses), (login) => props.userTypingStatuses[login]), [props.userTypingStatuses]); + const usersTyping = useMemo(() => _.filter(_.keys(props.userTypingStatuses), (accountID) => props.userTypingStatuses[accountID]), [props.userTypingStatuses]); // If we are offline, the user typing statuses are not up-to-date so do not show them if (props.network.isOffline) { return null; @@ -43,7 +43,7 @@ function ReportTypingIndicator(props) { case 1: return ( Date: Mon, 19 Jun 2023 16:06:07 +0300 Subject: [PATCH 05/10] Whitespace diff i think --- src/libs/actions/PersonalDetails.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/PersonalDetails.js b/src/libs/actions/PersonalDetails.js index 88651fad3ac8..ff2f9345254c 100644 --- a/src/libs/actions/PersonalDetails.js +++ b/src/libs/actions/PersonalDetails.js @@ -51,7 +51,7 @@ function getDisplayName(login, personalDetail) { } /** - * + * * @param {Number} accountID * @param {String} [defaultDisplayName] display name to use if user details don't exist in Onyx or if * found details don't include the user's displayName or login From 7dd504124f6a3b6fcfdb98ea9f614c4f96ba37b5 Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Mon, 19 Jun 2023 18:42:49 +0300 Subject: [PATCH 06/10] Revert comment --- src/libs/actions/Report.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index ee29b2866fbd..dce1d424edd8 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -95,8 +95,6 @@ function getReportChannelName(reportID) { function getNormalizedTypingStatus(typingStatus) { let normalizedTypingStatus = typingStatus; - // TODO: figure out what to do from here - // probably look up login in personalDetails and convert to accountID if it exists if (_.first(_.keys(typingStatus)) === 'userLogin') { normalizedTypingStatus = {[typingStatus.userLogin]: true}; } From 85fd5d1c40f99d7f2dfadc531fe8b277fc4a4229 Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Mon, 19 Jun 2023 21:33:45 +0300 Subject: [PATCH 07/10] Use translation for someone --- src/languages/en.js | 1 + src/languages/es.js | 1 + src/pages/home/report/ReportTypingIndicator.js | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/languages/en.js b/src/languages/en.js index c3d470901f0f..7eadd521cb53 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -145,6 +145,7 @@ export default { mi: 'mile', km: 'kilometer', copied: 'Copied!', + someone: 'Someone', }, anonymousReportFooter: { logoTagline: 'Join in on the discussion.', diff --git a/src/languages/es.js b/src/languages/es.js index ae8fd71adbed..ec3c0b3cbd4f 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -144,6 +144,7 @@ export default { mi: 'milla', km: 'kilómetro', copied: '¡Copiado!', + someone: 'Alguien', }, anonymousReportFooter: { logoTagline: 'Únete a la discussion.', diff --git a/src/pages/home/report/ReportTypingIndicator.js b/src/pages/home/report/ReportTypingIndicator.js index 062190281a1c..b73fa6d931dd 100755 --- a/src/pages/home/report/ReportTypingIndicator.js +++ b/src/pages/home/report/ReportTypingIndicator.js @@ -13,7 +13,7 @@ import Text from '../../../components/Text'; import TextWithEllipsis from '../../../components/TextWithEllipsis'; const propTypes = { - /** Key-value pairs of user logins and whether or not they are typing. Keys are logins. */ + /** Key-value pairs of user accountIDs/logins and whether or not they are typing. Keys are accountIDs or logins. */ userTypingStatuses: PropTypes.objectOf(PropTypes.bool), /** Information about the network */ @@ -43,7 +43,7 @@ function ReportTypingIndicator(props) { case 1: return ( Date: Mon, 19 Jun 2023 21:34:18 +0300 Subject: [PATCH 08/10] Make it clear typing status can be accountID or login --- src/libs/actions/Report.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index dce1d424edd8..5d516731e602 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -117,27 +117,30 @@ function subscribeToReportTypingEvents(reportID) { const pusherChannelName = getReportChannelName(reportID); Pusher.subscribe(pusherChannelName, Pusher.TYPE.USER_IS_TYPING, (typingStatus) => { + // If the pusher message comes from OldDot, we expect the typing status to be keyed by user + // login OR by 'Concierge'. If the pusher message comes from NewDot, it is keyed by accountID + // since personal details are keyed by accountID. const normalizedTypingStatus = getNormalizedTypingStatus(typingStatus); - const accountID = _.first(_.keys(normalizedTypingStatus)); + const accountIDOrLogin = _.first(_.keys(normalizedTypingStatus)); - if (!accountID) { + if (!accountIDOrLogin) { return; } // Don't show the typing indicator if the user is typing on another platform - if (accountID === currentUserAccountID) { + if (Number(accountIDOrLogin) === currentUserAccountID) { return; } // Use a combo of the reportID and the accountID as a key for holding our timers. - const reportUserIdentifier = `${reportID}-${accountID}`; + const reportUserIdentifier = `${reportID}-${accountIDOrLogin}`; clearTimeout(typingWatchTimers[reportUserIdentifier]); Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_TYPING}${reportID}`, normalizedTypingStatus); // Wait for 1.5s of no additional typing events before setting the status back to false. typingWatchTimers[reportUserIdentifier] = setTimeout(() => { const typingStoppedStatus = {}; - typingStoppedStatus[accountID] = false; + typingStoppedStatus[accountIDOrLogin] = false; Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_TYPING}${reportID}`, typingStoppedStatus); delete typingWatchTimers[reportUserIdentifier]; }, 1500); From e2af83d3909ce33888c467adce694feab00a007d Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Mon, 19 Jun 2023 21:34:40 +0300 Subject: [PATCH 09/10] update how we get display name --- src/libs/actions/PersonalDetails.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/libs/actions/PersonalDetails.js b/src/libs/actions/PersonalDetails.js index ff2f9345254c..10f29208cb3c 100644 --- a/src/libs/actions/PersonalDetails.js +++ b/src/libs/actions/PersonalDetails.js @@ -52,19 +52,24 @@ function getDisplayName(login, personalDetail) { /** * - * @param {Number} accountID + * @param {String} userAccountIDOrLogin * @param {String} [defaultDisplayName] display name to use if user details don't exist in Onyx or if * found details don't include the user's displayName or login * @returns {String} */ -function getDisplayNameByAccountID(accountID, defaultDisplayName = '') { - const userDetails = allPersonalDetails && lodashGet(allPersonalDetails, accountID, {}); +function getDisplayNameForTypingIndicator(userAccountIDOrLogin, defaultDisplayName = '') { + // Try to convert to a number, which means we have an accountID + const accountID = Number(userAccountIDOrLogin); - if (_.isEmpty(userDetails)) { - return defaultDisplayName; + // If the user is typing on OldDot, userAccountIDOrLogin will be a string (the user's login), + // so Number(string) is NaN. Search for personalDetails by login to get the display name. + if (_.isNaN(accountID)) { + const detailsByLogin = _.findWhere(allPersonalDetails, {login: userAccountIDOrLogin}) || {}; + return detailsByLogin.displayName || userAccountIDOrLogin; } - return userDetails.displayName || getDisplayName(userDetails.login || '', userDetails) || defaultDisplayName; + const detailsByAccountID = lodashGet(allPersonalDetails, accountID, {}); + return detailsByAccountID.displayName || detailsByAccountID.login || defaultDisplayName; } /** @@ -483,7 +488,7 @@ function clearAvatarErrors() { export { getDisplayName, - getDisplayNameByAccountID, + getDisplayNameForTypingIndicator, updateAvatar, deleteAvatar, openMoneyRequestModalPage, From 5976a0b4cf587502c41274d82f4e41e4003fd2ac Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Mon, 19 Jun 2023 22:24:36 +0300 Subject: [PATCH 10/10] Apply suggestions from code review Co-authored-by: Marc Glasser --- src/libs/actions/PersonalDetails.js | 1 - src/libs/actions/Report.js | 2 +- src/pages/home/report/ReportTypingIndicator.js | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/PersonalDetails.js b/src/libs/actions/PersonalDetails.js index 10f29208cb3c..b9c5f89a62eb 100644 --- a/src/libs/actions/PersonalDetails.js +++ b/src/libs/actions/PersonalDetails.js @@ -51,7 +51,6 @@ function getDisplayName(login, personalDetail) { } /** - * * @param {String} userAccountIDOrLogin * @param {String} [defaultDisplayName] display name to use if user details don't exist in Onyx or if * found details don't include the user's displayName or login diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 5d516731e602..127d71de6159 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -132,7 +132,7 @@ function subscribeToReportTypingEvents(reportID) { return; } - // Use a combo of the reportID and the accountID as a key for holding our timers. + // Use a combo of the reportID and the accountID or login as a key for holding our timers. const reportUserIdentifier = `${reportID}-${accountIDOrLogin}`; clearTimeout(typingWatchTimers[reportUserIdentifier]); Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_TYPING}${reportID}`, normalizedTypingStatus); diff --git a/src/pages/home/report/ReportTypingIndicator.js b/src/pages/home/report/ReportTypingIndicator.js index b73fa6d931dd..4de649c7eb49 100755 --- a/src/pages/home/report/ReportTypingIndicator.js +++ b/src/pages/home/report/ReportTypingIndicator.js @@ -27,7 +27,7 @@ const defaultProps = { }; function ReportTypingIndicator(props) { - const usersTyping = useMemo(() => _.filter(_.keys(props.userTypingStatuses), (accountID) => props.userTypingStatuses[accountID]), [props.userTypingStatuses]); + const usersTyping = useMemo(() => _.filter(_.keys(props.userTypingStatuses), (loginOrAccountID) => props.userTypingStatuses[loginOrAccountID]), [props.userTypingStatuses]); // If we are offline, the user typing statuses are not up-to-date so do not show them if (props.network.isOffline) { return null;