Skip to content

Commit

Permalink
Merge pull request #21029 from Expensify/beaman-fixUserIsTypingIndicator
Browse files Browse the repository at this point in the history
Fix "user is typing..." indicator - key by accountID

(cherry picked from commit b25a014)
  • Loading branch information
luacmartins authored and OSBotify committed Jun 19, 2023
1 parent 5cd0b4a commit a4417c5
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 17 deletions.
5 changes: 5 additions & 0 deletions src/languages/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,11 @@ export default {
per: 'per',
mi: 'mile',
km: 'kilometer',
<<<<<<< HEAD
=======
copied: 'Copied!',
someone: 'Someone',
>>>>>>> b25a014743 (Merge pull request #21029 from Expensify/beaman-fixUserIsTypingIndicator)
},
anonymousReportFooter: {
logoTagline: 'Join in on the discussion.',
Expand Down
5 changes: 5 additions & 0 deletions src/languages/es.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,11 @@ export default {
per: 'por',
mi: 'milla',
km: 'kilómetro',
<<<<<<< HEAD
=======
copied: '¡Copiado!',
someone: 'Alguien',
>>>>>>> b25a014743 (Merge pull request #21029 from Expensify/beaman-fixUserIsTypingIndicator)
},
anonymousReportFooter: {
logoTagline: 'Únete a la discussion.',
Expand Down
34 changes: 28 additions & 6 deletions src/libs/actions/PersonalDetails.js
Original file line number Diff line number Diff line change
Expand Up @@ -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),
});

/**
Expand All @@ -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;
Expand All @@ -50,6 +50,27 @@ function getDisplayName(login, personalDetail) {
return fullName || userLogin;
}

/**
* @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 getDisplayNameForTypingIndicator(userAccountIDOrLogin, defaultDisplayName = '') {
// Try to convert to a number, which means we have an accountID
const accountID = Number(userAccountIDOrLogin);

// 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;
}

const detailsByAccountID = lodashGet(allPersonalDetails, accountID, {});
return detailsByAccountID.displayName || detailsByAccountID.login || 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,
Expand Down Expand Up @@ -402,8 +423,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,
},
Expand Down Expand Up @@ -439,7 +460,7 @@ function deleteAvatar() {
key: ONYXKEYS.PERSONAL_DETAILS_LIST,
value: {
[currentUserAccountID]: {
avatar: personalDetails[currentUserAccountID].avatar,
avatar: allPersonalDetails[currentUserAccountID].avatar,
},
},
},
Expand All @@ -466,6 +487,7 @@ function clearAvatarErrors() {

export {
getDisplayName,
getDisplayNameForTypingIndicator,
updateAvatar,
deleteAvatar,
openMoneyRequestModalPage,
Expand Down
19 changes: 11 additions & 8 deletions src/libs/actions/Report.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,27 +125,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 login = _.first(_.keys(normalizedTypingStatus));
const accountIDOrLogin = _.first(_.keys(normalizedTypingStatus));

if (!login) {
if (!accountIDOrLogin) {
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 (Number(accountIDOrLogin) === 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 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);

// 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[accountIDOrLogin] = false;
Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_TYPING}${reportID}`, typingStoppedStatus);
delete typingWatchTimers[reportUserIdentifier];
}, 1500);
Expand Down Expand Up @@ -755,7 +758,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);
}

Expand Down
6 changes: 3 additions & 3 deletions src/pages/home/report/ReportTypingIndicator.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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), (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;
Expand All @@ -43,7 +43,7 @@ function ReportTypingIndicator(props) {
case 1:
return (
<TextWithEllipsis
leadingText={PersonalDetails.getDisplayName(usersTyping[0])}
leadingText={PersonalDetails.getDisplayNameForTypingIndicator(usersTyping[0], props.translate('common.someone'))}
trailingText={` ${props.translate('reportTypingIndicator.isTyping')}`}
textStyle={[styles.chatItemComposeSecondaryRowSubText]}
wrapperStyle={[styles.chatItemComposeSecondaryRow, styles.flex1]}
Expand Down

0 comments on commit a4417c5

Please sign in to comment.