Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
versionCode 1001032903
versionName "1.3.29-3"
versionCode 1001032904
versionName "1.3.29-4"
}

splits {
Expand Down
2 changes: 1 addition & 1 deletion ios/NewExpensify/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>1.3.29.3</string>
<string>1.3.29.4</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSApplicationQueriesSchemes</key>
Expand Down
2 changes: 1 addition & 1 deletion ios/NewExpensifyTests/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.3.29.3</string>
<string>1.3.29.4</string>
</dict>
</plist>
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
"version": "1.3.29-3",
"version": "1.3.29-4",
"author": "Expensify, Inc.",
"homepage": "https://new.expensify.com",
"description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",
Expand Down
1 change: 1 addition & 0 deletions src/languages/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ export default {
per: 'per',
mi: 'mile',
km: 'kilometer',
someone: 'Someone',
},
anonymousReportFooter: {
logoTagline: 'Join in on the discussion.',
Expand Down
1 change: 1 addition & 0 deletions src/languages/es.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ export default {
per: 'por',
mi: 'milla',
km: 'kilómetro',
someone: 'Alguien',
},
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