diff --git a/.env.example b/.env.example index cf13ef583016..944da2aa9296 100644 --- a/.env.example +++ b/.env.example @@ -11,3 +11,20 @@ USE_WEB_PROXY=false USE_WDYR=false CAPTURE_METRICS=false ONYX_METRICS=false + +EXPENSIFY_ACCOUNT_ID_ACCOUNTING=-1 +EXPENSIFY_ACCOUNT_ID_ADMIN=-1 +EXPENSIFY_ACCOUNT_ID_BILLS=-1 +EXPENSIFY_ACCOUNT_ID_CHRONOS=-1 +EXPENSIFY_ACCOUNT_ID_CONCIERGE=-1 +EXPENSIFY_ACCOUNT_ID_CONTRIBUTORS=-1 +EXPENSIFY_ACCOUNT_ID_FIRST_RESPONDER=-1 +EXPENSIFY_ACCOUNT_ID_HELP=-1 +EXPENSIFY_ACCOUNT_ID_INTEGRATION_TESTING_CREDS=-1 +EXPENSIFY_ACCOUNT_ID_PAYROLL=-1 +EXPENSIFY_ACCOUNT_ID_QA=-1 +EXPENSIFY_ACCOUNT_ID_QA_TRAVIS=-1 +EXPENSIFY_ACCOUNT_ID_RECEIPTS=-1 +EXPENSIFY_ACCOUNT_ID_REWARDS=-1 +EXPENSIFY_ACCOUNT_ID_STUDENT_AMBASSADOR=-1 +EXPENSIFY_ACCOUNT_ID_SVFG=-1 diff --git a/src/CONST.js b/src/CONST.js index 156df68f4836..68557e3ef774 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -860,22 +860,43 @@ const CONST = { LHN_SKELETON_VIEW_ITEM_HEIGHT: 64, EXPENSIFY_PARTNER_NAME: 'expensify.com', EMAIL: { - CONCIERGE: 'concierge@expensify.com', - HELP: 'help@expensify.com', - RECEIPTS: 'receipts@expensify.com', + ACCOUNTING: 'accounting@expensify.com', + ADMIN: 'admin@expensify.com', + BILLS: 'bills@expensify.com', CHRONOS: 'chronos@expensify.com', - QA: 'qa@expensify.com', + CONCIERGE: 'concierge@expensify.com', CONTRIBUTORS: 'contributors@expensify.com', FIRST_RESPONDER: 'firstresponders@expensify.com', + GUIDES_DOMAIN: 'team.expensify.com', + HELP: 'help@expensify.com', + INTEGRATION_TESTING_CREDS: 'integrationtestingcreds@expensify.com', + PAYROLL: 'payroll@expensify.com', + QA: 'qa@expensify.com', QA_TRAVIS: 'qa+travisreceipts@expensify.com', - BILLS: 'bills@expensify.com', + RECEIPTS: 'receipts@expensify.com', STUDENT_AMBASSADOR: 'studentambassadors@expensify.com', - ACCOUNTING: 'accounting@expensify.com', - PAYROLL: 'payroll@expensify.com', SVFG: 'svfg@expensify.com', - INTEGRATION_TESTING_CREDS: 'integrationtestingcreds@expensify.com', - ADMIN: 'admin@expensify.com', - GUIDES_DOMAIN: 'team.expensify.com', + }, + + ACCOUNT_ID: { + ACCOUNTING: Number(lodashGet(Config, 'EXPENSIFY_ACCOUNT_ID_ACCOUNTING', 9645353)), + ADMIN: Number(lodashGet(Config, 'EXPENSIFY_ACCOUNT_ID_ADMIN', -1)), + BILLS: Number(lodashGet(Config, 'EXPENSIFY_ACCOUNT_ID_BILLS', 1371)), + CHRONOS: Number(lodashGet(Config, 'EXPENSIFY_ACCOUNT_ID_CHRONOS', 10027416)), + CONCIERGE: Number(lodashGet(Config, 'EXPENSIFY_ACCOUNT_ID_CONCIERGE', 8392101)), + CONTRIBUTORS: Number(lodashGet(Config, 'EXPENSIFY_ACCOUNT_ID_CONTRIBUTORS', 9675014)), + FIRST_RESPONDER: Number(lodashGet(Config, 'EXPENSIFY_ACCOUNT_ID_FIRST_RESPONDER', 9375152)), + HELP: Number(lodashGet(Config, 'EXPENSIFY_ACCOUNT_ID_HELP', -1)), + INTEGRATION_TESTING_CREDS: Number(lodashGet(Config, 'EXPENSIFY_ACCOUNT_ID_INTEGRATION_TESTING_CREDS', -1)), + PAYROLL: Number(lodashGet(Config, 'EXPENSIFY_ACCOUNT_ID_PAYROLL', 9679724)), + QA: Number(lodashGet(Config, 'EXPENSIFY_ACCOUNT_ID_QA', 3126513)), + QA_TRAVIS: Number(lodashGet(Config, 'EXPENSIFY_ACCOUNT_ID_QA_TRAVIS', 8595733)), + RECEIPTS: Number(lodashGet(Config, 'EXPENSIFY_ACCOUNT_ID_RECEIPTS', -1)), + REWARDS: Number(lodashGet(Config, 'EXPENSIFY_ACCOUNT_ID_REWARDS', 11023767)), // rewards@expensify.com + STUDENT_AMBASSADOR: Number(lodashGet(Config, 'EXPENSIFY_ACCOUNT_ID_STUDENT_AMBASSADOR', 10476956)), + SVFG: Number(lodashGet(Config, 'EXPENSIFY_ACCOUNT_ID_SVFG', 2012843)), + // TODO: maybe make separate domain email things?? + // GUIDES_DOMAIN: , }, ENVIRONMENT: { @@ -1164,21 +1185,41 @@ const CONST = { }, get EXPENSIFY_EMAILS() { return [ - this.EMAIL.CONCIERGE, - this.EMAIL.HELP, - this.EMAIL.RECEIPTS, + this.EMAIL.ACCOUNTING, + this.EMAIL.ADMIN, + this.EMAIL.BILLS, this.EMAIL.CHRONOS, - this.EMAIL.QA, + this.EMAIL.CONCIERGE, this.EMAIL.CONTRIBUTORS, this.EMAIL.FIRST_RESPONDER, + this.EMAIL.HELP, + this.EMAIL.INTEGRATION_TESTING_CREDS, + this.EMAIL.PAYROLL, + this.EMAIL.QA, this.EMAIL.QA_TRAVIS, - this.EMAIL.BILLS, + this.EMAIL.RECEIPTS, this.EMAIL.STUDENT_AMBASSADOR, - this.EMAIL.ACCOUNTING, - this.EMAIL.PAYROLL, this.EMAIL.SVFG, - this.EMAIL.INTEGRATION_TESTING_CREDS, - this.EMAIL.ADMIN, + ]; + }, + get EXPENSIFY_ACCOUNT_IDS() { + return [ + this.ACCOUNT_ID.ACCOUNTING, + this.ACCOUNT_ID.ADMIN, + this.ACCOUNT_ID.BILLS, + this.ACCOUNT_ID.CHRONOS, + this.ACCOUNT_ID.CONCIERGE, + this.ACCOUNT_ID.CONTRIBUTORS, + this.ACCOUNT_ID.FIRST_RESPONDER, + this.ACCOUNT_ID.HELP, + this.ACCOUNT_ID.INTEGRATION_TESTING_CREDS, + this.ACCOUNT_ID.PAYROLL, + this.ACCOUNT_ID.QA, + this.ACCOUNT_ID.QA_TRAVIS, + this.ACCOUNT_ID.RECEIPTS, + this.ACCOUNT_ID.REWARDS, + this.ACCOUNT_ID.STUDENT_AMBASSADOR, + this.ACCOUNT_ID.SVFG, ]; }, diff --git a/src/ONYXKEYS.js b/src/ONYXKEYS.js index 082a1c7efbd5..fae9181be000 100755 --- a/src/ONYXKEYS.js +++ b/src/ONYXKEYS.js @@ -42,9 +42,6 @@ export default { // Has information about the network status (offline/online) NETWORK: 'network', - // Contains all the personalDetails the user has access to - PERSONAL_DETAILS: 'personalDetails', - // Contains all the personalDetails the user has access to, keyed by accountID PERSONAL_DETAILS_LIST: 'personalDetailsList', @@ -114,6 +111,7 @@ export default { DOWNLOAD: 'download_', POLICY: 'policy_', POLICY_MEMBER_LIST: 'policyMemberList_', + POLICY_MEMBERS: 'policyMembers_', WORKSPACE_INVITE_MEMBERS_DRAFT: 'workspaceInviteMembersDraft_', REPORT: 'report_', REPORT_ACTIONS: 'reportActions_', diff --git a/src/components/ArchivedReportFooter.js b/src/components/ArchivedReportFooter.js index 75117cd4cb42..13da54b64ecf 100644 --- a/src/components/ArchivedReportFooter.js +++ b/src/components/ArchivedReportFooter.js @@ -12,6 +12,7 @@ import * as ReportUtils from '../libs/ReportUtils'; import reportPropTypes from '../pages/reportPropTypes'; import * as ReportActionsUtils from '../libs/ReportActionsUtils'; import styles from '../styles/styles'; +import * as PersonalDetailsUtils from '../libs/PersonalDetailsUtils'; const propTypes = { /** The reason this report was archived */ @@ -49,14 +50,16 @@ const defaultProps = { const ArchivedReportFooter = (props) => { const archiveReason = lodashGet(props.reportClosedAction, 'originalMessage.reason', CONST.REPORT.ARCHIVE_REASON.DEFAULT); - let displayName = lodashGet(props.personalDetails, `${props.report.ownerEmail}.displayName`, props.report.ownerEmail); + let displayName = PersonalDetailsUtils.getDisplayNameOrDefault( + props.personalDetails, [props.report.ownerAccountID, 'displayName'], props.report.ownerEmail, + ); let oldDisplayName; if (archiveReason === CONST.REPORT.ARCHIVE_REASON.ACCOUNT_MERGED) { - const newLogin = props.reportClosedAction.originalMessage.newLogin; - const oldLogin = props.reportClosedAction.originalMessage.oldLogin; - displayName = lodashGet(props.personalDetails, `${newLogin}.displayName`, newLogin); - oldDisplayName = lodashGet(props.personalDetails, `${oldLogin}.displayName`, oldLogin); + const newAccountID = props.reportClosedAction.originalMessage.newAccountID; + const oldAccountID = props.reportClosedAction.originalMessage.oldAccountID; + displayName = PersonalDetailsUtils.getDisplayNameOrDefault(props.personalDetails, [newAccountID, 'displayName']); + oldDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(props.personalDetails, [oldAccountID, 'displayName']); } return ( @@ -81,7 +84,7 @@ export default compose( withLocalize, withOnyx({ personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, }, reportClosedAction: { key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`, diff --git a/src/components/AvatarWithDisplayName.js b/src/components/AvatarWithDisplayName.js index 6da7f00f5637..3ae2d82cc5d3 100644 --- a/src/components/AvatarWithDisplayName.js +++ b/src/components/AvatarWithDisplayName.js @@ -54,7 +54,7 @@ const AvatarWithDisplayName = (props) => { const subtitle = ReportUtils.getChatRoomSubtitle(props.report); const isExpenseReport = ReportUtils.isExpenseReport(props.report); const icons = ReportUtils.getIcons(props.report, props.personalDetails, props.policies); - const ownerPersonalDetails = OptionsListUtils.getPersonalDetailsForLogins([props.report.ownerEmail], props.personalDetails); + const ownerPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs([props.report.ownerAccountID], props.personalDetails); const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips(ownerPersonalDetails, false); const avatarContainerStyle = StyleUtils.getEmptyAvatarStyle(props.size) || styles.emptyAvatar; return ( diff --git a/src/components/AvatarWithIndicator.js b/src/components/AvatarWithIndicator.js index 1835e739c25b..44036bad1669 100644 --- a/src/components/AvatarWithIndicator.js +++ b/src/components/AvatarWithIndicator.js @@ -29,7 +29,7 @@ const propTypes = { /* Onyx Props */ /** The employee list of all policies (coming from Onyx) */ - policiesMemberList: PropTypes.objectOf(policyMemberPropType), + allPolicyMembers: PropTypes.objectOf(PropTypes.objectOf(policyMemberPropType)), /** All the user's policies (from Onyx via withFullPolicy) */ policies: PropTypes.objectOf(policyPropTypes.policy), @@ -62,7 +62,7 @@ const propTypes = { const defaultProps = { tooltipText: '', reimbursementAccount: {}, - policiesMemberList: {}, + allPolicyMembers: {}, policies: {}, bankAccountList: {}, cardList: {}, @@ -75,7 +75,7 @@ const AvatarWithIndicator = (props) => { // If a policy was just deleted from Onyx, then Onyx will pass a null value to the props, and // those should be cleaned out before doing any error checking const cleanPolicies = _.pick(props.policies, (policy) => policy); - const cleanPolicyMembers = _.pick(props.policiesMemberList, (member) => member); + const cleanAllPolicyMembers = _.pick(props.allPolicyMembers, (policyMembers) => policyMembers); // All of the error & info-checking methods are put into an array. This is so that using _.some() will return // early as soon as the first error / info condition is returned. This makes the checks very efficient since @@ -85,7 +85,7 @@ const AvatarWithIndicator = (props) => { () => PaymentMethods.hasPaymentMethodError(props.bankAccountList, props.cardList), () => _.some(cleanPolicies, PolicyUtils.hasPolicyError), () => _.some(cleanPolicies, PolicyUtils.hasCustomUnitsError), - () => _.some(cleanPolicyMembers, PolicyUtils.hasPolicyMemberError), + () => _.some(cleanAllPolicyMembers, PolicyUtils.hasPolicyMemberError), () => !_.isEmpty(props.reimbursementAccount.errors), () => UserUtils.hasLoginListError(props.loginList), @@ -114,8 +114,8 @@ AvatarWithIndicator.propTypes = propTypes; AvatarWithIndicator.displayName = 'AvatarWithIndicator'; export default withOnyx({ - policiesMemberList: { - key: ONYXKEYS.COLLECTION.POLICY_MEMBER_LIST, + allPolicyMembers: { + key: ONYXKEYS.COLLECTION.POLICY_MEMBERS, }, policies: { key: ONYXKEYS.COLLECTION.POLICY, diff --git a/src/components/MoneyRequestHeader.js b/src/components/MoneyRequestHeader.js index 600a96aede18..816c0ba5814d 100644 --- a/src/components/MoneyRequestHeader.js +++ b/src/components/MoneyRequestHeader.js @@ -80,16 +80,16 @@ const MoneyRequestHeader = (props) => { const moneyRequestReport = props.isSingleTransactionView ? props.parentReport : props.report; const isSettled = ReportUtils.isSettled(moneyRequestReport.reportID); const isExpenseReport = ReportUtils.isExpenseReport(moneyRequestReport); - const payeeName = isExpenseReport ? ReportUtils.getPolicyName(moneyRequestReport, props.policies) : ReportUtils.getDisplayNameForParticipant(moneyRequestReport.managerEmail); + const payeeName = isExpenseReport ? ReportUtils.getPolicyName(moneyRequestReport, props.policies) : ReportUtils.getDisplayNameForParticipant(moneyRequestReport.managerID); const payeeAvatar = isExpenseReport ? ReportUtils.getWorkspaceAvatar(moneyRequestReport) - : UserUtils.getAvatar(lodashGet(props.personalDetails, [moneyRequestReport.managerEmail, 'avatar']), moneyRequestReport.managerEmail); + : UserUtils.getAvatar(lodashGet(props.personalDetails, [moneyRequestReport.managerID, 'avatar']), moneyRequestReport.managerID); const policy = props.policies[`${ONYXKEYS.COLLECTION.POLICY}${props.report.policyID}`]; const isPayer = - Policy.isAdminOfFreePolicy([policy]) || (ReportUtils.isMoneyRequestReport(moneyRequestReport) && lodashGet(props.session, 'email', null) === moneyRequestReport.managerEmail); + Policy.isAdminOfFreePolicy([policy]) || (ReportUtils.isMoneyRequestReport(moneyRequestReport) && lodashGet(props.session, 'accountID', null) === moneyRequestReport.managerID); const shouldShowSettlementButton = !isSettled && !props.isSingleTransactionView && isPayer; const bankAccountRoute = ReportUtils.getBankAccountRoute(props.chatReport); - const shouldShowPaypal = Boolean(lodashGet(props.personalDetails, [moneyRequestReport.managerEmail, 'payPalMeAddress'])); + const shouldShowPaypal = Boolean(lodashGet(props.personalDetails, [moneyRequestReport.managerID, 'payPalMeAddress'])); return ( { if (_.isEmpty(props.iouReport) && !props.isBillSplit) { return null; } - const sessionEmail = lodashGet(props.session, 'email', null); - const managerEmail = props.iouReport.managerEmail || ''; - const ownerEmail = props.iouReport.ownerEmail || ''; - const participantEmails = props.isBillSplit ? lodashGet(props.action, 'originalMessage.participants', []) : [managerEmail, ownerEmail]; - const participantAvatars = OptionsListUtils.getAvatarsForLogins(participantEmails, props.personalDetails); + const sessionAccountID = lodashGet(props.session, 'accountID', null); + const managerID = props.iouReport.managerID || ''; + const ownerAccountID = props.iouReport.ownerAccountID || ''; + const participantAccountIDs = props.isBillSplit ? lodashGet(props.action, 'originalMessage.participantAccountIDs', []) : [managerID, ownerAccountID]; + const participantAvatars = OptionsListUtils.getAvatarsForAccountIDs(participantAccountIDs, props.personalDetails); // Pay button should only be visible to the manager of the report. - const isCurrentUserManager = managerEmail === sessionEmail; + const isCurrentUserManager = managerID === sessionAccountID; const moneyRequestAction = ReportUtils.getMoneyRequestAction(props.action); @@ -228,10 +234,10 @@ const IOUPreview = (props) => { )} {!_.isEmpty(requestComment) && {requestComment}} - {props.isBillSplit && !_.isEmpty(participantEmails) && ( + {props.isBillSplit && !_.isEmpty(participantAccountIDs) && ( {props.translate('iou.amountEach', { - amount: CurrencyUtils.convertToDisplayString(IOUUtils.calculateAmount(participantEmails.length - 1, requestAmount), requestCurrency), + amount: CurrencyUtils.convertToDisplayString(IOUUtils.calculateAmount(participantAccountIDs.length - 1, requestAmount), requestCurrency), })} )} @@ -267,7 +273,7 @@ export default compose( withLocalize, withOnyx({ personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, }, iouReport: { key: ({iouReportID}) => `${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`, diff --git a/src/components/ReportActionItem/MoneyRequestAction.js b/src/components/ReportActionItem/MoneyRequestAction.js index 1f11eeae780e..90833843139e 100644 --- a/src/components/ReportActionItem/MoneyRequestAction.js +++ b/src/components/ReportActionItem/MoneyRequestAction.js @@ -103,10 +103,9 @@ const MoneyRequestAction = (props) => { // If the childReportID is not present, we need to create a new thread const childReportID = lodashGet(props.action, 'childReportID', '0'); if (childReportID === '0') { - const participants = _.uniq([props.session.email, props.action.actorEmail]); - const formattedUserLogins = _.map(participants, (login) => OptionsListUtils.addSMSDomainIfPhoneNumber(login).toLowerCase()); + const participantAccountIDs = _.uniq([props.session.accountID, props.action.actorAccountID]); const thread = ReportUtils.buildOptimisticChatReport( - formattedUserLogins, + participantAccountIDs, props.translate(ReportActionsUtils.isSentMoneyReportAction(props.action) ? 'iou.threadSentMoneyReportName' : 'iou.threadRequestReportName', { formattedAmount: ReportActionsUtils.getFormattedAmount(props.action), comment: props.action.originalMessage.comment, @@ -114,6 +113,7 @@ const MoneyRequestAction = (props) => { '', CONST.POLICY.OWNER_EMAIL_FAKE, CONST.POLICY.OWNER_EMAIL_FAKE, + 0, false, '', undefined, diff --git a/src/components/ReportWelcomeText.js b/src/components/ReportWelcomeText.js index f2a79dd50fe3..e788209fb52a 100644 --- a/src/components/ReportWelcomeText.js +++ b/src/components/ReportWelcomeText.js @@ -54,12 +54,11 @@ const ReportWelcomeText = (props) => { const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(props.report); const isChatRoom = ReportUtils.isChatRoom(props.report); const isDefault = !(isChatRoom || isPolicyExpenseChat); - const participants = lodashGet(props.report, 'participants', []); const participantAccountIDs = lodashGet(props.report, 'participantAccountIDs', []); - const isMultipleParticipant = participants.length > 1; - const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips(OptionsListUtils.getPersonalDetailsForLogins(participants, props.personalDetails), isMultipleParticipant); + const isMultipleParticipant = participantAccountIDs.length > 1; + const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips(OptionsListUtils.getPersonalDetailsForAccountIDs(participantAccountIDs, props.personalDetails), isMultipleParticipant); const roomWelcomeMessage = ReportUtils.getRoomWelcomeMessage(props.report); - const moneyRequestOptions = ReportUtils.getMoneyRequestOptions(props.report, participants, props.betas); + const moneyRequestOptions = ReportUtils.getMoneyRequestOptions(props.report, participantAccountIDs, props.betas); return ( <> @@ -71,7 +70,7 @@ const ReportWelcomeText = (props) => { {props.translate('reportActionsView.beginningOfChatHistoryPolicyExpenseChatPartOne')} {/* Use the policyExpenseChat owner's first name or their email if it's undefined or an empty string */} - {lodashGet(props.personalDetails, [props.report.ownerEmail, 'firstName']) || props.report.ownerEmail} + {lodashGet(props.personalDetails, [props.report.ownerAccountID, 'firstName']) || props.report.ownerEmail} {props.translate('reportActionsView.beginningOfChatHistoryPolicyExpenseChatPartTwo')} {ReportUtils.getPolicyName(props.report)} @@ -130,7 +129,7 @@ export default compose( key: ONYXKEYS.BETAS, }, personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, }, }), )(ReportWelcomeText); diff --git a/src/components/TaskHeader.js b/src/components/TaskHeader.js index 2a6d62183954..e84361713217 100644 --- a/src/components/TaskHeader.js +++ b/src/components/TaskHeader.js @@ -36,8 +36,8 @@ const propTypes = { function TaskHeader(props) { const title = ReportUtils.getReportName(props.report); - const assigneeName = ReportUtils.getDisplayNameForParticipant(props.report.managerEmail); - const assigneeAvatar = UserUtils.getAvatar(lodashGet(props.personalDetails, [props.report.managerEmail, 'avatar']), props.report.managerEmail); + const assigneeName = ReportUtils.getDisplayNameForParticipant(props.report.managerID); + const assigneeAvatar = UserUtils.getAvatar(lodashGet(props.personalDetails, [props.report.managerID, 'avatar']), props.report.managerID); const isOpen = props.report.stateNum === CONST.REPORT.STATE_NUM.OPEN && props.report.statusNum === CONST.REPORT.STATUS.OPEN; const isCompleted = props.report.stateNum === CONST.REPORT.STATE_NUM.SUBMITTED && props.report.statusNum === CONST.REPORT.STATUS.APPROVED; @@ -60,7 +60,7 @@ function TaskHeader(props) { > - {!_.isEmpty(props.report.managerEmail) && ( + {!_.isEmpty(props.report.managerID) && ( <> { - const currentUserEmail = props.session.email; const accountID = props.session.accountID; - const currentUserPersonalDetails = useMemo(() => ({...props.personalDetails[currentUserEmail], accountID}), [props.personalDetails, currentUserEmail, accountID]); + const currentUserPersonalDetails = useMemo(() => ({...props.personalDetails[accountID], accountID}), [props.personalDetails, accountID]); return ( { if (!val || timezone) { return; diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index ced088836927..f3282460d9b7 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -23,9 +23,13 @@ import * as UserUtils from './UserUtils'; */ let currentUserLogin; +let currentUserAccountID; Onyx.connect({ key: ONYXKEYS.SESSION, - callback: (val) => (currentUserLogin = val && val.email), + callback: (val) => { + currentUserLogin = val && val.email; + currentUserAccountID = val && val.accountID; + }, }); let loginList; @@ -161,38 +165,56 @@ function getAvatarsForLogins(logins, personalDetails) { } /** - * Returns the personal details for an array of logins + * Returns avatar data for a list of user accountIDs + * + * @param {Array} accountIDs + * @param {Object} personalDetails + * @returns {Object} + */ +function getAvatarsForAccountIDs(accountIDs, personalDetails) { + return _.map(accountIDs, (accountID) => { + const userPersonalDetail = lodashGet(personalDetails, accountID, {login: '', accountID, avatar: ''}); + return { + source: UserUtils.getAvatar(userPersonalDetail.avatar, userPersonalDetail.accountID), + type: CONST.ICON_TYPE_AVATAR, + name: userPersonalDetail.login, + }; + }); +} + +/** + * Returns the personal details for an array of accountIDs * - * @param {Array} logins + * @param {Array} accountIDs * @param {Object} personalDetails * @returns {Object} – keys of the object are emails, values are PersonalDetails objects. */ -function getPersonalDetailsForLogins(logins, personalDetails) { - const personalDetailsForLogins = {}; +function getPersonalDetailsForAccountIDs(accountIDs, personalDetails) { + const personalDetailsForAccountIDs = {}; if (!personalDetails) { - return personalDetailsForLogins; + return personalDetailsForAccountIDs; } - _.chain(logins) - - // Somehow it's possible for the logins coming from report.participants to contain undefined values so we use compact to remove them. + _.chain(accountIDs) + // NOTE: this comment is possibly legacy, we need to verify if it's still true + // Somehow it's possible for the accountIDs coming from report.participantAccountIDs to contain undefined values so we use compact to remove them. .compact() - .each((login) => { - let personalDetail = personalDetails[login]; + .each((accountID) => { + let personalDetail = personalDetails[accountID]; if (!personalDetail) { + // NOTE: this can possibly be changed to 'hidden' personalDetail = { - login, - displayName: LocalePhoneNumber.formatPhoneNumber(login), - avatar: UserUtils.getDefaultAvatar(login), + accountID, + avatar: UserUtils.getDefaultAvatar(accountID), }; } - if (login === CONST.EMAIL.CONCIERGE) { + if (accountID === CONST.ACCOUNT_ID.CONCIERGE) { personalDetail.avatar = CONST.CONCIERGE_ICON_URL; } - personalDetailsForLogins[login] = personalDetail; + personalDetailsForAccountIDs[accountID] = personalDetail; }); - return personalDetailsForLogins; + return personalDetailsForAccountIDs; } /** @@ -201,7 +223,7 @@ function getPersonalDetailsForLogins(logins, personalDetails) { * @returns {Boolean} */ function isPersonalDetailsReady(personalDetails) { - return !_.isEmpty(personalDetails) && _.some(_.keys(personalDetails), (key) => personalDetails[key].login); + return !_.isEmpty(personalDetails) && _.some(_.keys(personalDetails), (key) => personalDetails[key].accountID); } /** @@ -211,17 +233,18 @@ function isPersonalDetailsReady(personalDetails) { * @returns {Array} */ function getParticipantsOptions(report, personalDetails) { - const participants = lodashGet(report, 'participants', []); - return _.map(getPersonalDetailsForLogins(participants, personalDetails), (details) => ({ + const participants = lodashGet(report, 'participantAccountIDs', []); + return _.map(getPersonalDetailsForAccountIDs(participants, personalDetails), (details) => ({ keyForList: details.login, login: details.login, + accountID: details.accountID, text: details.displayName, firstName: lodashGet(details, 'firstName', ''), lastName: lodashGet(details, 'lastName', ''), alternateText: Str.isSMSLogin(details.login || '') ? LocalePhoneNumber.formatPhoneNumber(details.login) : details.login, icons: [ { - source: UserUtils.getAvatar(details.avatar, details.login), + source: UserUtils.getAvatar(details.avatar, details.accountID), name: details.login, type: CONST.ICON_TYPE_AVATAR, }, @@ -243,6 +266,7 @@ function getParticipantNames(personalDetailList) { // `_.contains(Array, value)` for an Array with n members. const participantNames = new Set(); _.each(personalDetailList, (participant) => { + // TODO: maybe get participant name by accountID in personalDetails?? if (participant.login) { participantNames.add(participant.login.toLowerCase()); } @@ -302,10 +326,12 @@ function getSearchText(report, reportName, personalDetailList, isChatRoomOrPolic for (let i = 0; i < personalDetailList.length; i++) { const personalDetail = personalDetailList[i]; - // The regex below is used to remove dots only from the local part of the user email (local-part@domain) - // so that we can match emails that have dots without explicitly writing the dots (e.g: fistlast@domain will match first.last@domain) - // More info https://github.com/Expensify/App/issues/8007 - searchTerms = searchTerms.concat([personalDetail.displayName, personalDetail.login, personalDetail.login.replace(/\.(?=[^\s@]*@)/g, '')]); + if (personalDetail.login) { + // The regex below is used to remove dots only from the local part of the user email (local-part@domain) + // so that we can match emails that have dots without explicitly writing the dots (e.g: fistlast@domain will match first.last@domain) + // More info https://github.com/Expensify/App/issues/8007 + searchTerms = searchTerms.concat([personalDetail.displayName, personalDetail.login, personalDetail.login.replace(/\.(?=[^\s@]*@)/g, '')]); + } } } if (report) { @@ -362,7 +388,7 @@ function getAllReportErrors(report, reportActions) { /** * Creates a report list option * - * @param {Array} logins + * @param {Array} accountIDs * @param {Object} personalDetails * @param {Object} report * @param {Object} reportActions @@ -371,7 +397,7 @@ function getAllReportErrors(report, reportActions) { * @param {Boolean} [options.forcePolicyNamePreview] * @returns {Object} */ -function createOption(logins, personalDetails, report, reportActions = {}, {showChatPreviewLine = false, forcePolicyNamePreview = false}) { +function createOption(accountIDs, personalDetails, report, reportActions = {}, {showChatPreviewLine = false, forcePolicyNamePreview = false}) { const result = { text: null, alternateText: null, @@ -402,7 +428,7 @@ function createOption(logins, personalDetails, report, reportActions = {}, {show isPolicyExpenseChat: false, }; - const personalDetailMap = getPersonalDetailsForLogins(logins, personalDetails); + const personalDetailMap = getPersonalDetailsForAccountIDs(accountIDs, personalDetails); const personalDetailList = _.values(personalDetailMap); const personalDetail = personalDetailList[0] || {}; let hasMultipleParticipants = personalDetailList.length > 1; @@ -430,7 +456,7 @@ function createOption(logins, personalDetails, report, reportActions = {}, {show result.isPinned = report.isPinned; result.iouReportID = report.iouReportID; result.keyForList = String(report.reportID); - result.tooltipText = ReportUtils.getReportParticipantsTitle(report.participants || []); + result.tooltipText = ReportUtils.getReportParticipantsTitle(report.participantAccountIDs || []); result.hasOutstandingIOU = report.hasOutstandingIOU; hasMultipleParticipants = personalDetailList.length > 1 || result.isChatRoom || result.isPolicyExpenseChat; @@ -443,8 +469,8 @@ function createOption(logins, personalDetails, report, reportActions = {}, {show lastMessageTextFromReport = report ? report.lastMessageText || '' : ''; } - const lastActorDetails = personalDetailMap[report.lastActorEmail] || null; - let lastMessageText = hasMultipleParticipants && lastActorDetails && lastActorDetails.login !== currentUserLogin ? `${lastActorDetails.displayName}: ` : ''; + const lastActorDetails = personalDetailMap[report.lastActorAccountID] || null; + let lastMessageText = hasMultipleParticipants && lastActorDetails && lastActorDetails.accountID !== currentUserAccountID ? `${lastActorDetails.displayName}: ` : ''; lastMessageText += report ? lastMessageTextFromReport : ''; if (result.isArchivedRoom) { @@ -466,10 +492,9 @@ function createOption(logins, personalDetails, report, reportActions = {}, {show } reportName = ReportUtils.getReportName(report); } else { - const login = logins[0]; - reportName = ReportUtils.getDisplayNameForParticipant(login); - result.keyForList = login; - result.alternateText = LocalePhoneNumber.formatPhoneNumber(login); + reportName = ReportUtils.getDisplayNameForParticipant(accountIDs[0]); + result.keyForList = accountIDs[0]; + result.alternateText = ''; } result.isIOUReportOwner = ReportUtils.isIOUOwnedByCurrentUser(result, iouReports); @@ -483,7 +508,7 @@ function createOption(logins, personalDetails, report, reportActions = {}, {show result.text = reportName; result.searchText = getSearchText(report, reportName, personalDetailList, result.isChatRoom || result.isPolicyExpenseChat, result.isThread); - result.icons = ReportUtils.getIcons(report, personalDetails, UserUtils.getAvatar(personalDetail.avatar, personalDetail.login)); + result.icons = ReportUtils.getIcons(report, personalDetails, UserUtils.getAvatar(personalDetail.avatar, personalDetail.accountID)); result.subtitle = subtitle; return result; @@ -509,6 +534,8 @@ function isSearchStringMatch(searchValue, searchText, participantNames = new Set /** * Checks if the given userDetails is currentUser or not. + * Note: We can't migrate this off of using logins because this is used to check if you're trying to start a chat with + * yourself or a different user, and people won't be starting new chats via accountID usually. * * @param {Object} userDetails * @returns {Boolean} @@ -572,7 +599,7 @@ function getOptions( let recentReportOptions = []; let personalDetailsOptions = []; - const reportMapForLogins = {}; + const reportMapForAccountIDs = {}; const parsedPhoneNumber = parsePhoneNumber(LoginUtils.appendCountryCode(searchInputValue)); const searchValue = parsedPhoneNumber.possible ? parsedPhoneNumber.number.e164 : searchInputValue; @@ -603,7 +630,7 @@ function getOptions( const isChatRoom = ReportUtils.isChatRoom(report); const isTaskReport = ReportUtils.isTaskReport(report); const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(report); - const logins = report.participants || []; + const accountIDs = report.participantAccountIDs || []; if (isPolicyExpenseChat && report.isOwnPolicyExpenseChat && !includeOwnedWorkspaceChats) { return; @@ -620,8 +647,8 @@ function getOptions( // Save the report in the map if this is a single participant so we can associate the reportID with the // personal detail option later. Individuals should not be associated with single participant // policyExpenseChats or chatRooms since those are not people. - if (logins.length <= 1 && !isPolicyExpenseChat && !isChatRoom) { - reportMapForLogins[logins[0]] = report; + if (accountIDs.length <= 1 && !isPolicyExpenseChat && !isChatRoom) { + reportMapForAccountIDs[accountIDs[0]] = report; } const isSearchingSomeonesPolicyExpenseChat = !report.isOwnPolicyExpenseChat && searchValue !== ''; @@ -630,7 +657,7 @@ function getOptions( const isPolicyChatAdmin = ReportUtils.isPolicyExpenseChatAdmin(report, policies); allReportOptions.push( - createOption(logins, personalDetails, report, reportActions, { + createOption(accountIDs, personalDetails, report, reportActions, { showChatPreviewLine, forcePolicyNamePreview: isPolicyExpenseChat ? isSearchingSomeonesPolicyExpenseChat || isPolicyChatAdmin : forcePolicyNamePreview, }), @@ -638,7 +665,7 @@ function getOptions( }); let allPersonalDetailsOptions = _.map(personalDetails, (personalDetail) => - createOption([personalDetail.login], personalDetails, reportMapForLogins[personalDetail.login], reportActions, { + createOption([personalDetail.accountID], personalDetails, reportMapForAccountIDs[personalDetail.accountID], reportActions, { showChatPreviewLine, forcePolicyNamePreview, }), @@ -727,15 +754,17 @@ function getOptions( !_.find(loginOptionsToExclude, (loginOptionToExclude) => loginOptionToExclude.login === addSMSDomainIfPhoneNumber(searchValue).toLowerCase()) && (searchValue !== CONST.EMAIL.CHRONOS || Permissions.canUseChronos(betas)) ) { - userToInvite = createOption([searchValue], personalDetails, null, reportActions, { + const fakeAccountID = UserUtils.generateAccountID(); + userToInvite = createOption([fakeAccountID], personalDetails, null, reportActions, { showChatPreviewLine, }); + userToInvite.login = searchValue; // If user doesn't exist, use a default avatar userToInvite.icons = [ { - source: UserUtils.getAvatar('', searchValue), - name: searchValue, + source: UserUtils.getAvatar('', fakeAccountID), + login: searchValue, type: CONST.ICON_TYPE_AVATAR, }, ]; @@ -821,6 +850,7 @@ function getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetail, amount ], descriptiveText: amountText, login: personalDetail.login, + accountID: personalDetail.accountID, }; } @@ -953,6 +983,7 @@ function getHeaderMessage(hasSelectableOptions, hasUserToInvite, searchValue, ma export { addSMSDomainIfPhoneNumber, getAvatarsForLogins, + getAvatarsForAccountIDs, isCurrentUser, isPersonalDetailsReady, getSearchOptions, @@ -960,7 +991,7 @@ export { getShareDestinationOptions, getMemberInviteOptions, getHeaderMessage, - getPersonalDetailsForLogins, + getPersonalDetailsForAccountIDs, getIOUConfirmationOptionsFromPayeePersonalDetail, getIOUConfirmationOptionsFromParticipants, getSearchText, diff --git a/src/libs/PersonalDetailsUtils.js b/src/libs/PersonalDetailsUtils.js index 1cdddc136f7e..2282a1e91ae6 100644 --- a/src/libs/PersonalDetailsUtils.js +++ b/src/libs/PersonalDetailsUtils.js @@ -1,3 +1,4 @@ +import lodashGet from 'lodash/get'; import Onyx from 'react-native-onyx'; import _ from 'underscore'; import ONYXKEYS from '../ONYXKEYS'; @@ -9,6 +10,18 @@ Onyx.connect({ callback: (val) => (personalDetails = _.values(val)), }); +/** + * @param {Object} passedPersonalDetails + * @param {Array} pathToDisplayName + * @param {String} [defaultValue] optional default display name value + * @returns {String} + */ +function getDisplayNameOrDefault(passedPersonalDetails, pathToDisplayName, defaultValue) { + const displayName = lodashGet(passedPersonalDetails, pathToDisplayName); + + return displayName || defaultValue || 'Hidden'; +} + /** * Given a list of account IDs (as string) it will return an array of personal details objects. * @param {Array} accountIDs - Array of accountIDs @@ -35,6 +48,6 @@ function getPersonalDetailsByIDs(accountIDs, currentUserAccountID, shouldChangeU } export { - // eslint-disable-next-line import/prefer-default-export + getDisplayNameOrDefault, getPersonalDetailsByIDs, }; diff --git a/src/libs/PolicyUtils.js b/src/libs/PolicyUtils.js index 91396be628a3..86f5bc5e1458 100644 --- a/src/libs/PolicyUtils.js +++ b/src/libs/PolicyUtils.js @@ -5,14 +5,14 @@ import CONST from '../CONST'; import ONYXKEYS from '../ONYXKEYS'; /** - * Checks if we have any errors stored within the POLICY_MEMBER_LIST. Determines whether we should show a red brick road error or not. - * Data structure: {email: {role:'user', errors: []}, email2: {role:'admin', errors: [{1231312313: 'Unable to do X'}]}, ...} + * Checks if we have any errors stored within the POLICY_MEMBERS. Determines whether we should show a red brick road error or not. + * Data structure: {accountID: {role:'user', errors: []}, accountID2: {role:'admin', errors: [{1231312313: 'Unable to do X'}]}, ...} * - * @param {Object} policyMemberList + * @param {Object} policyMembers * @returns {Boolean} */ -function hasPolicyMemberError(policyMemberList) { - return _.some(policyMemberList, (member) => !_.isEmpty(member.errors)); +function hasPolicyMemberError(policyMembers) { + return _.some(policyMembers, (member) => !_.isEmpty(member.errors)); } /** @@ -53,12 +53,12 @@ function hasCustomUnitsError(policy) { * * @param {Object} policy * @param {String} policy.id - * @param {Object} policyMembers + * @param {Object} policyMembersCollection * @returns {String} */ -function getPolicyBrickRoadIndicatorStatus(policy, policyMembers) { - const policyMemberList = lodashGet(policyMembers, `${ONYXKEYS.COLLECTION.POLICY_MEMBER_LIST}${policy.id}`, {}); - if (hasPolicyMemberError(policyMemberList) || hasCustomUnitsError(policy) || hasPolicyErrorFields(policy)) { +function getPolicyBrickRoadIndicatorStatus(policy, policyMembersCollection) { + const policyMembers = lodashGet(policyMembersCollection, `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policy.id}`, {}); + if (hasPolicyMemberError(policyMembers) || hasCustomUnitsError(policy) || hasPolicyErrorFields(policy)) { return CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR; } return ''; @@ -100,4 +100,31 @@ function isExpensifyTeam(email) { */ const isPolicyAdmin = (policy) => lodashGet(policy, 'role') === CONST.POLICY.ROLE.ADMIN; -export {hasPolicyMemberError, hasPolicyError, hasPolicyErrorFields, hasCustomUnitsError, getPolicyBrickRoadIndicatorStatus, shouldShowPolicy, isExpensifyTeam, isPolicyAdmin}; +/** + * @param {Object} policyMembers + * @param {Object} personalDetails + * @returns {Object} + * + * Create an object mapping member emails to their accountIDs. Filter for members without errors, and get the login email from the personalDetail object using the accountID. + * TODO: Clean up uses of this function to work with accountIDs instead of emails. + * + * We only return members without errors. Otherwise, the members with errors would immediately be removed before the user has a chance to read the error. + */ +function getClientPolicyMemberEmailsToAccountIDs(policyMembers, personalDetails) { + return _.chain(policyMembers) + .filter((member) => _.isEmpty(member.errors)) + .keys() + .reduce((result, accountID) => { + const personalDetail = personalDetails[accountID]; + if (personalDetail && personalDetail.login) { + return { + ...result, + [personalDetail.login]: accountID, + } + } + return result; + }, {}) + .value(); +} + +export {hasPolicyMemberError, hasPolicyError, hasPolicyErrorFields, hasCustomUnitsError, getPolicyBrickRoadIndicatorStatus, shouldShowPolicy, isExpensifyTeam, isPolicyAdmin, getClientPolicyMemberEmailsToAccountIDs}; diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 0d26ff733f7d..46ed72d37f23 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -18,49 +18,46 @@ import DateUtils from './DateUtils'; import linkingConfig from './Navigation/linkingConfig'; import isReportMessageAttachment from './isReportMessageAttachment'; import * as defaultWorkspaceAvatars from '../components/Icon/WorkspaceDefaultAvatars'; -import * as LocalePhoneNumber from './LocalePhoneNumber'; import * as CurrencyUtils from './CurrencyUtils'; import * as UserUtils from './UserUtils'; let sessionEmail; +let sessionAccountID; +let currentUserEmail; +let currentUserAccountID; Onyx.connect({ key: ONYXKEYS.SESSION, - callback: (val) => (sessionEmail = val ? val.email : null), -}); - -let preferredLocale = CONST.LOCALES.DEFAULT; -Onyx.connect({ - key: ONYXKEYS.NVP_PREFERRED_LOCALE, callback: (val) => { + // When signed out, val is undefined if (!val) { return; } - preferredLocale = val; + + sessionEmail = val.email; + sessionAccountID = val.accountID; + currentUserEmail = val.email; + currentUserAccountID = val.accountID; }, }); -let currentUserEmail; -let currentUserAccountID; +let preferredLocale = CONST.LOCALES.DEFAULT; Onyx.connect({ - key: ONYXKEYS.SESSION, + key: ONYXKEYS.NVP_PREFERRED_LOCALE, callback: (val) => { - // When signed out, val is undefined if (!val) { return; } - - currentUserEmail = val.email; - currentUserAccountID = val.accountID; + preferredLocale = val; }, }); let allPersonalDetails; let currentUserPersonalDetails; Onyx.connect({ - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, callback: (val) => { - currentUserPersonalDetails = lodashGet(val, currentUserEmail, {}); - allPersonalDetails = val; + currentUserPersonalDetails = lodashGet(val, currentUserAccountID, {}); + allPersonalDetails = val || {}; }, }); @@ -92,16 +89,15 @@ function getChatType(report) { /** * Returns the concatenated title for the PrimaryLogins of a report * - * @param {Array} logins + * @param {Array} accountIDs * @returns {string} */ -function getReportParticipantsTitle(logins) { +function getReportParticipantsTitle(accountIDs) { return ( - _.chain(logins) + _.chain(accountIDs) - // Somehow it's possible for the logins coming from report.participants to contain undefined values so we use compact to remove them. + // Somehow it's possible for the logins coming from report.participantAccountIDs to contain undefined values so we use compact to remove them. .compact() - .map((login) => Str.removeSMSDomain(login)) .value() .join(', ') ); @@ -374,7 +370,7 @@ function hasExpensifyGuidesEmails(emails) { * @returns {Boolean} */ function isConciergeChatReport(report) { - return lodashGet(report, 'participants', []).length === 1 && report.participants[0] === CONST.EMAIL.CONCIERGE; + return lodashGet(report, 'participantAccountIDs', []).length === 1 && report.participantAccountIDs[0] == CONST.ACCOUNT_ID.CONCIERGE; } /** @@ -404,6 +400,8 @@ function findLastAccessedReport(reports, ignoreDomainRooms, policies, isFirstTim // We allow public announce rooms, admins, and announce rooms through since we bypass the default rooms beta for them. // Check where ReportUtils.findLastAccessedReport is called in MainDrawerNavigator.js for more context. // Domain rooms are now the only type of default room that are on the defaultRooms beta. + // TODO: migrate to report.participantAccountIDs later - not sure how we'll determine if a list of account IDs + // includes an expensify guide account ID :O sortedReports = _.filter( sortedReports, (report) => !isDomainRoom(report) || getPolicyType(report, policies) === CONST.POLICY.TYPE.FREE || hasExpensifyGuidesEmails(lodashGet(report, ['participants'], [])), @@ -611,7 +609,7 @@ function getRoomWelcomeMessage(report) { * @returns {Boolean} */ function chatIncludesConcierge(report) { - return report.participants && _.contains(report.participants, CONST.EMAIL.CONCIERGE); + return report.participantAccountIDs && _.contains(report.participantAccountIDs, CONST.ACCOUNT_ID.CONCIERGE); } /** @@ -623,6 +621,15 @@ function hasAutomatedExpensifyEmails(emails) { return _.intersection(emails, CONST.EXPENSIFY_EMAILS).length > 0; } +/** + * Returns true if there is any automated expensify account in accountIDs + * @param {Array} accountIDs + * @returns {Boolean} + */ +function hasAutomatedExpensifyAccountIDs(accountIDs) { + return _.intersection(accountIDs, CONST.EXPENSIFY_ACCOUNT_IDS).length > 0; +} + /** * Returns true if there are any Expensify accounts (i.e. with domain 'expensify.com') in the set of emails. * @@ -641,10 +648,10 @@ function hasExpensifyEmails(emails) { * @return {Boolean} */ function canShowReportRecipientLocalTime(personalDetails, report, login) { - const reportParticipants = _.without(lodashGet(report, 'participants', []), login); - const participantsWithoutExpensifyEmails = _.difference(reportParticipants, CONST.EXPENSIFY_EMAILS); - const hasMultipleParticipants = participantsWithoutExpensifyEmails.length > 1; - const reportRecipient = personalDetails[participantsWithoutExpensifyEmails[0]]; + const reportParticipants = _.without(lodashGet(report, 'participantAccountIDs', []), login); + const participantsWithoutExpensifyAccountIDs = _.difference(reportParticipants, CONST.EXPENSIFY_ACCOUNT_IDS); + const hasMultipleParticipants = participantsWithoutExpensifyAccountIDs.length > 1; + const reportRecipient = personalDetails[participantsWithoutExpensifyAccountIDs[0]]; const reportRecipientTimezone = lodashGet(reportRecipient, 'timezone', CONST.DEFAULT_TIME_ZONE); const isReportParticipantValidated = lodashGet(reportRecipient, 'validated', false); return Boolean( @@ -704,9 +711,9 @@ function getIconsForParticipants(participants, personalDetails) { const participantsList = participants || []; for (let i = 0; i < participantsList.length; i++) { - const login = participantsList[i]; - const avatarSource = UserUtils.getAvatar(lodashGet(personalDetails, [login, 'avatar'], ''), login); - participantDetails.push([login, lodashGet(personalDetails, [login, 'firstName'], ''), avatarSource]); + const accountID = participantsList[i]; + const avatarSource = UserUtils.getAvatar(lodashGet(personalDetails, [accountID, 'avatar'], ''), accountID); + participantDetails.push([accountID, lodashGet(personalDetails, [accountID, 'firstName'], ''), avatarSource]); } // Sort all logins by first name (which is the second element in the array) @@ -759,8 +766,9 @@ function getIcons(report, personalDetails, defaultIcon = null, isPayer = false) const parentReportAction = ReportActionsUtils.getParentReportAction(report); const actorEmail = lodashGet(parentReportAction, 'actorEmail', ''); + const actorAccountID = lodashGet(parentReportAction, 'actorAccountID', ''); const actorIcon = { - source: UserUtils.getAvatar(lodashGet(personalDetails, [actorEmail, 'avatar']), actorEmail), + source: UserUtils.getAvatar(lodashGet(personalDetails, [actorAccountID, 'avatar']), actorAccountID), name: actorEmail, type: CONST.ICON_TYPE_AVATAR, }; @@ -797,7 +805,7 @@ function getIcons(report, personalDetails, defaultIcon = null, isPayer = false) } const adminIcon = { - source: UserUtils.getAvatar(lodashGet(personalDetails, [report.ownerEmail, 'avatar']), report.ownerEmail), + source: UserUtils.getAvatar(lodashGet(personalDetails, [report.ownerAccountID, 'avatar']), report.ownerAccountID), name: report.ownerEmail, type: CONST.ICON_TYPE_AVATAR, }; @@ -814,33 +822,39 @@ function getIcons(report, personalDetails, defaultIcon = null, isPayer = false) } if (isIOUReport(report)) { const email = isPayer ? report.managerEmail : report.ownerEmail; + const accountID = isPayer ? report.managerID : report.ownerAccountID; return [ { - source: UserUtils.getAvatar(lodashGet(personalDetails, [email, 'avatar']), email), + source: UserUtils.getAvatar(lodashGet(personalDetails, [accountID, 'avatar']), accountID), name: email, type: CONST.ICON_TYPE_AVATAR, }, ]; } - return getIconsForParticipants(report.participants, personalDetails); + return getIconsForParticipants(report.participantAccountIDs, personalDetails); } /** - * Gets the personal details for a login by looking in the ONYXKEYS.PERSONAL_DETAILS Onyx key (stored in the local variable, allPersonalDetails). If it doesn't exist in Onyx, + * Gets the personal details for a login by looking in the ONYXKEYS.PERSONAL_DETAILS_LIST Onyx key (stored in the local variable, allPersonalDetails). If it doesn't exist in Onyx, * then a default object is constructed. - * @param {String} login + * @param {String} accountID * @returns {Object} */ -function getPersonalDetailsForLogin(login) { - if (!login) { +function getPersonalDetailsForAccountID(accountID) { + if (!accountID) { return {}; } + if (accountID == CONST.ACCOUNT_ID.CONCIERGE) { + return { + accountID, + displayName: 'Concierge', + avatar: UserUtils.getDefaultAvatar(accountID), + }; + } return ( - (allPersonalDetails && allPersonalDetails[login]) || { - login, - displayName: LocalePhoneNumber.formatPhoneNumber(login), - avatar: UserUtils.getDefaultAvatar(login), + (allPersonalDetails && allPersonalDetails[accountID]) || { + avatar: UserUtils.getDefaultAvatar(accountID), } ); } @@ -848,15 +862,15 @@ function getPersonalDetailsForLogin(login) { /** * Get the displayName for a single report participant. * - * @param {String} login + * @param {String} accountID * @param {Boolean} [shouldUseShortForm] * @returns {String} */ -function getDisplayNameForParticipant(login, shouldUseShortForm = false) { - if (!login) { +function getDisplayNameForParticipant(accountID, shouldUseShortForm = false) { + if (!accountID) { return ''; } - const personalDetails = getPersonalDetailsForLogin(login); + const personalDetails = getPersonalDetailsForAccountID(accountID); const longName = personalDetails.displayName; @@ -872,7 +886,9 @@ function getDisplayNameForParticipant(login, shouldUseShortForm = false) { */ function getDisplayNamesWithTooltips(participants, isMultipleParticipantReport) { return _.map(participants, (participant) => { - const displayName = getDisplayNameForParticipant(participant.login, isMultipleParticipantReport); + const displayName = getDisplayNameForParticipant(participant.accountID, isMultipleParticipantReport); + + // TODO: Maybe get login from personal details via participant accountID? const tooltip = participant.login ? Str.removeSMSDomain(participant.login) : ''; let pronouns = participant.pronouns; @@ -943,7 +959,7 @@ function getMoneyRequestTotal(report, moneyRequestReports = {}) { * @returns {String} */ function getPolicyExpenseChatName(report) { - const reportOwnerDisplayName = getDisplayNameForParticipant(report.ownerEmail) || report.ownerEmail || report.reportName; + const reportOwnerDisplayName = getDisplayNameForParticipant(report.ownerAccountID) || report.ownerEmail || report.reportName; // If the policy expense chat is owned by this user, use the name of the policy as the report name. if (report.isOwnPolicyExpenseChat) { @@ -974,7 +990,7 @@ function getPolicyExpenseChatName(report) { */ function getMoneyRequestReportName(report) { const formattedAmount = CurrencyUtils.convertToDisplayString(getMoneyRequestTotal(report), report.currency); - const payerName = isExpenseReport(report) ? getPolicyName(report) : getDisplayNameForParticipant(report.managerEmail); + const payerName = isExpenseReport(report) ? getPolicyName(report) : getDisplayNameForParticipant(report.managerID); return Localize.translateLocal(report.hasOutstandingIOU ? 'iou.payerOwesAmount' : 'iou.payerPaidAmount', {payer: payerName, amount: formattedAmount}); } @@ -1034,11 +1050,11 @@ function getReportName(report) { } // Not a room or PolicyExpenseChat, generate title from participants - const participants = (report && report.participants) || []; - const participantsWithoutCurrentUser = _.without(participants, sessionEmail); + const participants = (report && report.participantAccountIDs) || []; + const participantsWithoutCurrentUser = _.without(participants, sessionAccountID); const isMultipleParticipantReport = participantsWithoutCurrentUser.length > 1; - return _.map(participantsWithoutCurrentUser, (login) => getDisplayNameForParticipant(login, isMultipleParticipantReport)).join(', '); + return _.map(participantsWithoutCurrentUser, (accountID) => getDisplayNameForParticipant(accountID, isMultipleParticipantReport)).join(', '); } /** @@ -1129,12 +1145,12 @@ function buildOptimisticAddCommentReportAction(text, file) { person: [ { style: 'strong', - text: lodashGet(allPersonalDetails, [currentUserEmail, 'displayName'], currentUserEmail), + text: lodashGet(allPersonalDetails, [currentUserAccountID, 'displayName'], currentUserEmail), type: 'TEXT', }, ], automatic: false, - avatar: lodashGet(allPersonalDetails, [currentUserEmail, 'avatar'], UserUtils.getDefaultAvatar(currentUserEmail)), + avatar: lodashGet(allPersonalDetails, [currentUserAccountID, 'avatar'], UserUtils.getDefaultAvatar(currentUserAccountID)), created: DateUtils.getDBTime(), message: [ { @@ -1156,12 +1172,12 @@ function buildOptimisticAddCommentReportAction(text, file) { * Builds an optimistic reportAction for the parent report when a task is created * @param {String} taskReportID - Report ID of the task * @param {String} taskTitle - Title of the task - * @param {String} taskAssignee - Email of the person assigned to the task + * @param {String} taskAssigneeAccountID - AccountID of the person assigned to the task * @param {String} text - Text of the comment * @param {String} parentReportID - Report ID of the parent report * @returns {Object} */ -function buildOptimisticTaskCommentReportAction(taskReportID, taskTitle, taskAssignee, text, parentReportID) { +function buildOptimisticTaskCommentReportAction(taskReportID, taskTitle, taskAssigneeAccountID, text, parentReportID) { const reportAction = buildOptimisticAddCommentReportAction(text); reportAction.reportAction.message[0].taskReportID = taskReportID; @@ -1176,6 +1192,7 @@ function buildOptimisticTaskCommentReportAction(taskReportID, taskTitle, taskAss reportAction.reportAction.childType = CONST.REPORT.TYPE.TASK; reportAction.reportAction.childReportName = taskTitle; reportAction.reportAction.childManagerEmail = taskAssignee; + reportAction.reportAction.childManagerAccountID = taskAssigneeAccountID; reportAction.reportAction.childStatusNum = CONST.REPORT.STATUS.OPEN; reportAction.reportAction.childStateNum = CONST.REPORT.STATE_NUM.OPEN; @@ -1186,7 +1203,8 @@ function buildOptimisticTaskCommentReportAction(taskReportID, taskTitle, taskAss * Builds an optimistic IOU report with a randomly generated reportID * * @param {String} payeeEmail - Email of the person generating the IOU. - * @param {String} payerEmail - Email of the other person participating in the IOU. + * @param {Number} payeeAccountID - AccountID of the person generating the IOU. + * @param {Number} payerAccountID - AccountID of the other person participating in the IOU. * @param {Number} total - IOU amount in the smallest unit of the currency. * @param {String} chatReportID - Report ID of the chat where the IOU is. * @param {String} currency - IOU currency. @@ -1194,8 +1212,10 @@ function buildOptimisticTaskCommentReportAction(taskReportID, taskTitle, taskAss * * @returns {Object} */ -function buildOptimisticIOUReport(payeeEmail, payerEmail, total, chatReportID, currency, isSendingMoney = false) { +function buildOptimisticIOUReport(payeeEmail, payeeAccountID, payerAccountID, total, chatReportID, currency, isSendingMoney = false) { const formattedTotal = CurrencyUtils.convertToDisplayString(total, currency); + // TODO: GET ME + const payerEmail = 'GET ME WITH PERSONAL DETAILS'; return { // If we're sending money, hasOutstandingIOU should be false hasOutstandingIOU: !isSendingMoney, @@ -1203,8 +1223,9 @@ function buildOptimisticIOUReport(payeeEmail, payerEmail, total, chatReportID, c cachedTotal: formattedTotal, chatReportID, currency, - managerEmail: payerEmail, + managerID: payerAccountID, ownerEmail: payeeEmail, + ownerAccountID: payeeAccountID, reportID: generateReportID(), state: CONST.REPORT.STATE.SUBMITTED, stateNum: isSendingMoney ? CONST.REPORT.STATE_NUM.SUBMITTED : CONST.REPORT.STATE_NUM.PROCESSING, @@ -1221,12 +1242,13 @@ function buildOptimisticIOUReport(payeeEmail, payerEmail, total, chatReportID, c * @param {String} chatReportID - Report ID of the PolicyExpenseChat where the Expense Report is * @param {String} policyID - The policy ID of the PolicyExpenseChat * @param {String} payeeEmail - Email of the employee (payee) + * @param {Number} payeeAccountID - AccountID of the employee (payee) * @param {Number} total - Amount in cents * @param {String} currency * * @returns {Object} */ -function buildOptimisticExpenseReport(chatReportID, policyID, payeeEmail, total, currency) { +function buildOptimisticExpenseReport(chatReportID, policyID, payeeEmail, payeeAccountID, total, currency) { // The amount for Expense reports are stored as negative value in the database const storedTotal = total * -1; const policyName = getPolicyName(allReports[`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`]); @@ -1241,6 +1263,7 @@ function buildOptimisticExpenseReport(chatReportID, policyID, payeeEmail, total, policyID, type: CONST.REPORT.TYPE.EXPENSE, ownerEmail: payeeEmail, + ownerAccountID: payeeAccountID, hasOutstandingIOU: true, currency: outputCurrency, @@ -1343,6 +1366,7 @@ function buildOptimisticIOUReportAction(type, amount, currency, comment, partici if (type === CONST.IOU.REPORT_ACTION_TYPE.SPLIT) { delete originalMessage.IOUReportID; originalMessage.participants = [currentUserEmail, ..._.pluck(participants, 'login')]; + originalMessage.participantAccountIDs = [currentUserAccountID, ..._.pluck(participants, 'accountID')]; } return { @@ -1350,7 +1374,7 @@ function buildOptimisticIOUReportAction(type, amount, currency, comment, partici actorAccountID: currentUserAccountID, actorEmail: currentUserEmail, automatic: false, - avatar: lodashGet(currentUserPersonalDetails, 'avatar', UserUtils.getDefaultAvatar(currentUserEmail)), + avatar: lodashGet(currentUserPersonalDetails, 'avatar', UserUtils.getDefaultAvatar(currentUserAccountID)), isAttachment: false, originalMessage, message: getIOUReportActionMessage(type, amount, comment, currency, paymentType, isSettlingUp), @@ -1388,6 +1412,7 @@ function buildOptimisticReportPreview(reportID, iouReportID, payeeAccountID) { linkedReportID: iouReportID, }, actorEmail: currentUserEmail, + actorAccountID: currentUserAccountID, }; } @@ -1403,7 +1428,7 @@ function buildOptimisticTaskReportAction(taskReportID, actionName, message = '') actorAccountID: currentUserAccountID, actorEmail: currentUserEmail, automatic: false, - avatar: lodashGet(currentUserPersonalDetails, 'avatar', UserUtils.getDefaultAvatar(currentUserEmail)), + avatar: lodashGet(currentUserPersonalDetails, 'avatar', UserUtils.getDefaultAvatar(currentUserAccountID)), isAttachment: false, originalMessage, message: [ @@ -1416,7 +1441,7 @@ function buildOptimisticTaskReportAction(taskReportID, actionName, message = '') person: [ { style: 'strong', - text: lodashGet(currentUserPersonalDetails, 'displayName', currentUserEmail), + text: lodashGet(currentUserPersonalDetails, 'displayName', currentUserAccountID), type: 'TEXT', }, ], @@ -1431,11 +1456,12 @@ function buildOptimisticTaskReportAction(taskReportID, actionName, message = '') /** * Builds an optimistic chat report with a randomly generated reportID and as much information as we currently have * - * @param {Array} participantList + * @param {Array} participantList Array of participant accountIDs * @param {String} reportName * @param {String} chatType * @param {String} policyID * @param {String} ownerEmail + * @param {Number} ownerAccountID * @param {Boolean} isOwnPolicyExpenseChat * @param {String} oldPolicyName * @param {String} visibility @@ -1450,6 +1476,7 @@ function buildOptimisticChatReport( chatType = '', policyID = CONST.POLICY.OWNER_EMAIL_FAKE, ownerEmail = CONST.REPORT.OWNER_EMAIL_FAKE, + ownerAccountID = 0, isOwnPolicyExpenseChat = false, oldPolicyName = '', visibility = undefined, @@ -1465,6 +1492,7 @@ function buildOptimisticChatReport( isOwnPolicyExpenseChat, isPinned: reportName === CONST.REPORT.WORKSPACE_CHAT_ROOMS.ADMINS, lastActorEmail: '', + lastActorAccountID: 0, lastMessageHtml: '', lastMessageText: null, lastReadTime: currentTime, @@ -1472,9 +1500,10 @@ function buildOptimisticChatReport( notificationPreference, oldPolicyName, ownerEmail: ownerEmail || CONST.REPORT.OWNER_EMAIL_FAKE, + ownerAccountID: ownerAccountID || 0, parentReportActionID, parentReportID, - participants: participantList, + participantAccountIDs: participantList, policyID, reportID: generateReportID(), reportName, @@ -1513,11 +1542,11 @@ function buildOptimisticCreatedReportAction(ownerEmail) { { type: CONST.REPORT.MESSAGE.TYPE.TEXT, style: 'strong', - text: lodashGet(allPersonalDetails, [currentUserEmail, 'displayName'], currentUserEmail), + text: lodashGet(allPersonalDetails, [currentUserAccountID, 'displayName'], currentUserEmail), }, ], automatic: false, - avatar: lodashGet(allPersonalDetails, [currentUserEmail, 'avatar'], UserUtils.getDefaultAvatar(currentUserEmail)), + avatar: lodashGet(allPersonalDetails, [currentUserAccountID, 'avatar'], UserUtils.getDefaultAvatar(currentUserAccountID)), created: DateUtils.getDBTime(), shouldShow: true, }; @@ -1553,11 +1582,11 @@ function buildOptimisticEditedTaskReportAction(ownerEmail) { { type: CONST.REPORT.MESSAGE.TYPE.TEXT, style: 'strong', - text: lodashGet(allPersonalDetails, [currentUserEmail, 'displayName'], currentUserEmail), + text: lodashGet(allPersonalDetails, [currentUserAccountID, 'displayName'], currentUserEmail), }, ], automatic: false, - avatar: lodashGet(allPersonalDetails, [currentUserEmail, 'avatar'], UserUtils.getDefaultAvatar(currentUserEmail)), + avatar: lodashGet(allPersonalDetails, [currentUserAccountID, 'avatar'], UserUtils.getDefaultAvatar(currentUserAccountID)), created: DateUtils.getDBTime(), shouldShow: false, }; @@ -1576,7 +1605,7 @@ function buildOptimisticClosedReportAction(ownerEmail, policyName, reason = CONS actionName: CONST.REPORT.ACTIONS.TYPE.CLOSED, actorAccountID: currentUserAccountID, automatic: false, - avatar: lodashGet(allPersonalDetails, [currentUserEmail, 'avatar'], UserUtils.getDefaultAvatar(currentUserEmail)), + avatar: lodashGet(allPersonalDetails, [currentUserAccountID, 'avatar'], UserUtils.getDefaultAvatar(currentUserAccountID)), created: DateUtils.getDBTime(), message: [ { @@ -1599,7 +1628,7 @@ function buildOptimisticClosedReportAction(ownerEmail, policyName, reason = CONS { type: CONST.REPORT.MESSAGE.TYPE.TEXT, style: 'strong', - text: lodashGet(allPersonalDetails, [currentUserEmail, 'displayName'], currentUserEmail), + text: lodashGet(allPersonalDetails, [currentUserAccountID, 'displayName'], currentUserEmail), }, ], reportActionID: NumberUtils.rand64(), @@ -1614,11 +1643,12 @@ function buildOptimisticClosedReportAction(ownerEmail, policyName, reason = CONS */ function buildOptimisticWorkspaceChats(policyID, policyName) { const announceChatData = buildOptimisticChatReport( - [currentUserEmail], + [currentUserAccountID], CONST.REPORT.WORKSPACE_CHAT_ROOMS.ANNOUNCE, CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE, policyID, null, + 0, false, policyName, null, @@ -1632,14 +1662,14 @@ function buildOptimisticWorkspaceChats(policyID, policyName) { [announceCreatedAction.reportActionID]: announceCreatedAction, }; - const adminsChatData = buildOptimisticChatReport([currentUserEmail], CONST.REPORT.WORKSPACE_CHAT_ROOMS.ADMINS, CONST.REPORT.CHAT_TYPE.POLICY_ADMINS, policyID, null, false, policyName); + const adminsChatData = buildOptimisticChatReport([currentUserAccountID], CONST.REPORT.WORKSPACE_CHAT_ROOMS.ADMINS, CONST.REPORT.CHAT_TYPE.POLICY_ADMINS, policyID, null, 0, false, policyName); const adminsChatReportID = adminsChatData.reportID; const adminsCreatedAction = buildOptimisticCreatedReportAction(adminsChatData.ownerEmail); const adminsReportActionData = { [adminsCreatedAction.reportActionID]: adminsCreatedAction, }; - const expenseChatData = buildOptimisticChatReport([currentUserEmail], '', CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT, policyID, currentUserEmail, true, policyName); + const expenseChatData = buildOptimisticChatReport([currentUserAccountID], '', CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT, policyID, currentUserEmail, currentUserAccountID, true, policyName); const expenseChatReportID = expenseChatData.reportID; const expenseReportCreatedAction = buildOptimisticCreatedReportAction(expenseChatData.ownerEmail); const expenseReportActionData = { @@ -1666,7 +1696,8 @@ function buildOptimisticWorkspaceChats(policyID, policyName) { * Builds an optimistic Task Report with a randomly generated reportID * * @param {String} ownerEmail - Email of the person generating the Task. - * @param {String} assignee - Email of the other person participating in the Task. + * @param {Number} ownerAccountID - Account ID of the person generating the Task. + * @param {String} assigneeAccountID - AccountID of the other person participating in the Task. * @param {String} parentReportID - Report ID of the chat where the Task is. * @param {String} title - Task title. * @param {String} description - Task description. @@ -1674,13 +1705,15 @@ function buildOptimisticWorkspaceChats(policyID, policyName) { * @returns {Object} */ -function buildOptimisticTaskReport(ownerEmail, assignee = null, parentReportID, title, description) { +function buildOptimisticTaskReport(ownerEmail, ownerAccountID, assigneeAccountID = 0, parentReportID, title, description) { return { reportID: generateReportID(), reportName: title, description, ownerEmail, - managerEmail: assignee, + ownerAccountID, + // managerEmail: assignee, + managerID: assigneeAccountID, type: CONST.REPORT.TYPE.TASK, parentReportID, stateNum: CONST.REPORT.STATE_NUM.OPEN, @@ -1723,21 +1756,20 @@ function isUnreadWithMention(report) { * * @param {Object} report * @param {String} report.iouReportID - * @param {String} currentUserLogin * @param {Object} iouReports * @returns {boolean} */ -function hasOutstandingIOU(report, currentUserLogin, iouReports) { +function hasOutstandingIOU(report, iouReports) { if (!report || !report.iouReportID || _.isUndefined(report.hasOutstandingIOU)) { return false; } const iouReport = iouReports && iouReports[`${ONYXKEYS.COLLECTION.REPORT}${report.iouReportID}`]; - if (!iouReport || !iouReport.ownerEmail) { + if (!iouReport || !iouReport.ownerAccountID) { return false; } - if (iouReport.ownerEmail === currentUserEmail) { + if (iouReport.ownerAccountID === currentUserAccountID) { return false; } @@ -1754,7 +1786,7 @@ function isIOUOwnedByCurrentUser(report, iouReports = {}) { if (report.hasOutstandingIOU) { const iouReport = iouReports[`${ONYXKEYS.COLLECTION.REPORT}${report.iouReportID}`]; if (iouReport) { - return iouReport.ownerEmail === currentUserEmail; + return iouReport.ownerAccountID === currentUserAccountID; } } return false; @@ -1781,11 +1813,13 @@ function canSeeDefaultRoom(report, policies, betas) { } // Include domain rooms with Partner Managers (Expensify accounts) in them for accounts that are on a domain with an Approved Accountant + // TODO: figure out how we can determine accountIDs of "expensify emails" if (isDomainRoom(report) && doesDomainHaveApprovedAccountant && hasExpensifyEmails(lodashGet(report, ['participants'], []))) { return true; } // If the room has an assigned guide, it can be seen. + // TODO: figure out how we can determine accountIDs of "expensify emails" if (hasExpensifyGuidesEmails(lodashGet(report, ['participants'], []))) { return true; } @@ -1809,13 +1843,12 @@ function canSeeDefaultRoom(report, policies, betas) { * @param {Object} report * @param {String} reportIDFromRoute * @param {Boolean} isInGSDMode - * @param {String} currentUserLogin * @param {Object} iouReports * @param {String[]} betas * @param {Object} policies * @returns {boolean} */ -function shouldReportBeInOptionList(report, reportIDFromRoute, isInGSDMode, currentUserLogin, iouReports, betas, policies) { +function shouldReportBeInOptionList(report, reportIDFromRoute, isInGSDMode, iouReports, betas, policies) { const isInDefaultMode = !isInGSDMode; // Exclude reports that have no data because there wouldn't be anything to show in the option item. @@ -1824,7 +1857,7 @@ function shouldReportBeInOptionList(report, reportIDFromRoute, isInGSDMode, curr if ( !report || !report.reportID || - (_.isEmpty(report.participants) && !isThread(report) && !isPublicRoom(report) && !isArchivedRoom(report) && !isMoneyRequestReport(report) && !isTaskReport(report)) + (_.isEmpty(report.participantAccountIDs) && !isThread(report) && !isPublicRoom(report) && !isArchivedRoom(report) && !isMoneyRequestReport(report) && !isTaskReport(report)) ) { return false; } @@ -1842,7 +1875,7 @@ function shouldReportBeInOptionList(report, reportIDFromRoute, isInGSDMode, curr // Include reports if they have a draft, are pinned, or have an outstanding IOU // These are always relevant to the user no matter what view mode the user prefers - if (report.hasDraft || report.isPinned || hasOutstandingIOU(report, currentUserLogin, iouReports)) { + if (report.hasDraft || report.isPinned || hasOutstandingIOU(report, iouReports)) { return true; } @@ -1879,12 +1912,12 @@ function getChatByParticipants(newParticipantList) { newParticipantList.sort(); return _.find(allReports, (report) => { // If the report has been deleted, or there are no participants (like an empty #admins room) then skip it - if (!report || !report.participants || isThread(report)) { + if (!report || !report.participantAccountIDs || isThread(report)) { return false; } // Only return the room if it has all the participants and is not a policy room - return !isUserCreatedPolicyRoom(report) && _.isEqual(newParticipantList, _.sortBy(report.participants)); + return !isUserCreatedPolicyRoom(report) && _.isEqual(newParticipantList, _.sortBy(report.participantAccountIDs)); }); } @@ -1898,12 +1931,12 @@ function getChatByParticipantsAndPolicy(newParticipantList, policyID) { newParticipantList.sort(); return _.find(allReports, (report) => { // If the report has been deleted, or there are no participants (like an empty #admins room) then skip it - if (!report || !report.participants) { + if (!report || !report.participantAccountIDs) { return false; } // Only return the room if it has all the participants and is not a policy room - return report.policyID === policyID && _.isEqual(newParticipantList, _.sortBy(report.participants)); + return report.policyID === policyID && _.isEqual(newParticipantList, _.sortBy(report.participantAccountIDs)); }); } @@ -1921,7 +1954,7 @@ function getAllPolicyReports(policyID) { * @returns {Boolean} */ function chatIncludesChronos(report) { - return report.participants && _.contains(report.participants, CONST.EMAIL.CHRONOS); + return report.participantAccountIDs && _.contains(report.participantAccountIDs, CONST.ACCOUNT_ID.CHRONOS); } /** @@ -2023,11 +2056,12 @@ function getMoneyRequestOptions(report, reportParticipants, betas) { return []; } - const participants = _.filter(reportParticipants, (email) => currentUserPersonalDetails.login !== email); - const hasExcludedIOUEmails = lodashIntersection(reportParticipants, CONST.EXPENSIFY_EMAILS).length > 0; + const participants = _.filter(reportParticipants, (accountID) => currentUserPersonalDetails.accountID !== accountID); + + const hasExcludedIOUAccountIDs = lodashIntersection(reportParticipants, CONST.EXPENSIFY_ACCOUNT_IDS).length > 0; const hasMultipleParticipants = participants.length > 1; - if (hasExcludedIOUEmails || (participants.length === 0 && !report.isOwnPolicyExpenseChat) || !Permissions.canUseIOU(betas)) { + if (hasExcludedIOUAccountIDs || (participants.length === 0 && !report.isOwnPolicyExpenseChat) || !Permissions.canUseIOU(betas)) { return []; } @@ -2176,6 +2210,7 @@ export { isConciergeChatReport, isCurrentUserTheOnlyParticipant, hasAutomatedExpensifyEmails, + hasAutomatedExpensifyAccountIDs, hasExpensifyGuidesEmails, hasOutstandingIOU, isIOUOwnedByCurrentUser, diff --git a/src/libs/SidebarUtils.js b/src/libs/SidebarUtils.js index 87e8b97d0bd0..333ffd8e3ec0 100644 --- a/src/libs/SidebarUtils.js +++ b/src/libs/SidebarUtils.js @@ -29,7 +29,7 @@ Onyx.connect({ let personalDetails; Onyx.connect({ - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, callback: (val) => (personalDetails = val), }); @@ -78,10 +78,16 @@ Onyx.connect({ callback: (val) => (policies = val), }); -let currentUserLogin; +let currentUserAccountID; Onyx.connect({ key: ONYXKEYS.SESSION, - callback: (val) => (currentUserLogin = val), + callback: (val) => { + if (!val) { + return; + } + + currentUserAccountID = val.accountID; + }, }); let preferredLocale; @@ -99,7 +105,7 @@ function getOrderedReportIDs(reportIDFromRoute) { const isInDefaultMode = !isInGSDMode; // Filter out all the reports that shouldn't be displayed - const reportsToDisplay = _.filter(allReports, (report) => ReportUtils.shouldReportBeInOptionList(report, reportIDFromRoute, isInGSDMode, currentUserLogin, allReports, betas, policies)); + const reportsToDisplay = _.filter(allReports, (report) => ReportUtils.shouldReportBeInOptionList(report, reportIDFromRoute, isInGSDMode, allReports, betas, policies)); if (_.isEmpty(reportsToDisplay)) { // Display Concierge chat report when there is no report to be displayed const conciergeChatReport = _.find(allReports, ReportUtils.isConciergeChatReport); @@ -209,9 +215,11 @@ function getOptionData(reportID) { icons: null, tooltipText: null, ownerEmail: null, + ownerAccountID: null, subtitle: null, participantsList: null, login: null, + accountID: null, reportID: null, phoneNumber: null, payPalMeAddress: null, @@ -232,7 +240,7 @@ function getOptionData(reportID) { isMoneyRequestReport: false, }; - const participantPersonalDetailList = _.values(OptionsListUtils.getPersonalDetailsForLogins(report.participants, personalDetails)); + const participantPersonalDetailList = _.values(OptionsListUtils.getPersonalDetailsForAccountIDs(report.participantAccountIDs, personalDetails)); const personalDetail = participantPersonalDetailList[0] || {}; result.isThread = ReportUtils.isThread(report); @@ -246,6 +254,7 @@ function getOptionData(reportID) { result.allReportErrors = OptionsListUtils.getAllReportErrors(report, reportActions); result.brickRoadIndicator = !_.isEmpty(result.allReportErrors) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''; result.ownerEmail = report.ownerEmail; + result.ownerAccountID = report.ownerAccountID; result.reportID = report.reportID; result.isUnread = ReportUtils.isUnread(report); result.isUnreadWithMention = ReportUtils.isUnreadWithMention(report); @@ -253,7 +262,7 @@ function getOptionData(reportID) { result.isPinned = report.isPinned; result.iouReportID = report.iouReportID; result.keyForList = String(report.reportID); - result.tooltipText = ReportUtils.getReportParticipantsTitle(report.participants || []); + result.tooltipText = ReportUtils.getReportParticipantsTitle(report.participantAccountIDs || []); result.hasOutstandingIOU = report.hasOutstandingIOU; result.parentReportID = report.parentReportID || null; const parentReport = result.parentReportID ? allReports[`${ONYXKEYS.COLLECTION.REPORT}${result.parentReportID}`] : null; @@ -276,17 +285,18 @@ function getOptionData(reportID) { // If the last actor's details are not currently saved in Onyx Collection, // then try to get that from the last report action if that action is valid // to get data from. - let lastActorDetails = personalDetails[report.lastActorEmail] || null; + let lastActorDetails = personalDetails[report.lastActorAccountID] || null; if (!lastActorDetails && visibleReportActionItems[report.reportID]) { const lastActorDisplayName = lodashGet(visibleReportActionItems[report.reportID], 'person[0].text'); lastActorDetails = lastActorDisplayName ? { displayName: lastActorDisplayName, login: report.lastActorEmail, + accountID: report.lastActorAccountID, } : null; } - let lastMessageText = hasMultipleParticipants && lastActorDetails && lastActorDetails.login !== currentUserLogin.email ? `${lastActorDetails.displayName}: ` : ''; + let lastMessageText = hasMultipleParticipants && lastActorDetails && lastActorDetails.accountID !== currentUserAccountID ? `${lastActorDetails.displayName}: ` : ''; lastMessageText += report ? lastMessageTextFromReport : ''; if (result.isArchivedRoom) { @@ -329,6 +339,7 @@ function getOptionData(reportID) { result.iouReportAmount = ReportUtils.getMoneyRequestTotal(result, allReports); if (!hasMultipleParticipants) { + result.accountID = personalDetail.accountID; result.login = personalDetail.login; result.phoneNumber = personalDetail.phoneNumber; result.payPalMeAddress = personalDetail.payPalMeAddress; @@ -340,7 +351,7 @@ function getOptionData(reportID) { result.subtitle = subtitle; result.participantsList = participantPersonalDetailList; - result.icons = ReportUtils.getIcons(result.isTaskReport ? parentReport : report, personalDetails, UserUtils.getAvatar(personalDetail.avatar, personalDetail.login), true); + result.icons = ReportUtils.getIcons(result.isTaskReport ? parentReport : report, personalDetails, UserUtils.getAvatar(personalDetail.avatar, personalDetail.accountID), true); result.searchText = OptionsListUtils.getSearchText(report, reportName, participantPersonalDetailList, result.isChatRoom || result.isPolicyExpenseChat, result.isThread); result.displayNamesWithTooltips = displayNamesWithTooltips; return result; diff --git a/src/libs/UserUtils.js b/src/libs/UserUtils.js index e7d8235f0004..315448d4c549 100644 --- a/src/libs/UserUtils.js +++ b/src/libs/UserUtils.js @@ -66,52 +66,52 @@ function getLoginListBrickRoadIndicator(loginList) { /** * Hashes provided string and returns a value between [0, range) - * @param {String} login + * @param {String} text * @param {Number} range * @returns {Number} */ -function hashLogin(login, range) { - return Math.abs(hashCode(login.toLowerCase())) % range; +function hashText(text, range) { + return Math.abs(hashCode(text.toLowerCase())) % range; } /** - * Helper method to return the default avatar associated with the given login - * @param {String} [login] + * Helper method to return the default avatar associated with the given accountID + * @param {Number} [accountID] * @returns {String} */ -function getDefaultAvatar(login = '') { - if (!login) { +function getDefaultAvatar(accountID = -1) { + if (accountID <= 0) { return Expensicons.FallbackAvatar; } - if (login === CONST.EMAIL.CONCIERGE) { + if (accountID == CONST.ACCOUNT_ID.CONCIERGE) { return Expensicons.ConciergeAvatar; } // There are 24 possible default avatars, so we choose which one this user has based // on a simple hash of their login. Note that Avatar count starts at 1. - const loginHashBucket = hashLogin(login, CONST.DEFAULT_AVATAR_COUNT) + 1; + const accountIDHashBucket = hashText(accountID.toString(), CONST.DEFAULT_AVATAR_COUNT) + 1; - return defaultAvatars[`Avatar${loginHashBucket}`]; + return defaultAvatars[`Avatar${accountIDHashBucket}`]; } /** * Helper method to return default avatar URL associated with login * - * @param {String} [login] + * @param {String} [accountID] * @param {Boolean} [isNewDot] * @returns {String} */ -function getDefaultAvatarURL(login = '', isNewDot = false) { - if (login === CONST.EMAIL.CONCIERGE) { +function getDefaultAvatarURL(accountID = '', isNewDot = false) { + if (accountID === CONST.EMAIL.CONCIERGE) { return CONST.CONCIERGE_ICON_URL; } - // The default avatar for a user is based on a simple hash of their login. + // The default avatar for a user is based on a simple hash of their accountID. // Note that Avatar count starts at 1 which is why 1 has to be added to the result (or else 0 would result in a broken avatar link) - const loginHashBucket = hashLogin(login, isNewDot ? CONST.DEFAULT_AVATAR_COUNT : CONST.OLD_DEFAULT_AVATAR_COUNT) + 1; + const accountIDHashBucket = hashText(accountID, isNewDot ? CONST.DEFAULT_AVATAR_COUNT : CONST.OLD_DEFAULT_AVATAR_COUNT) + 1; const avatarPrefix = isNewDot ? `default-avatar` : `avatar`; - return `${CONST.CLOUDFRONT_URL}/images/avatars/${avatarPrefix}_${loginHashBucket}.png`; + return `${CONST.CLOUDFRONT_URL}/images/avatars/${avatarPrefix}_${accountIDHashBucket}.png`; } /** @@ -139,11 +139,11 @@ function isDefaultAvatar(avatarURL) { * Otherwise, return the URL pointing to a user-uploaded avatar. * * @param {String} avatarURL - the avatar source from user's personalDetails - * @param {String} login - the email of the user + * @param {Number} accountID - the accountID of the user * @returns {String|Function} */ -function getAvatar(avatarURL, login) { - return isDefaultAvatar(avatarURL) ? getDefaultAvatar(login) : avatarURL; +function getAvatar(avatarURL, accountID) { + return isDefaultAvatar(avatarURL) ? getDefaultAvatar(accountID) : avatarURL; } /** @@ -151,11 +151,11 @@ function getAvatar(avatarURL, login) { * Otherwise, return the URL pointing to a user-uploaded avatar. * * @param {String} avatarURL - the avatar source from user's personalDetails - * @param {String} login - the email of the user + * @param {Number} accountID - the accountID of the user * @returns {String} */ -function getAvatarUrl(avatarURL, login) { - return isDefaultAvatar(avatarURL) ? getDefaultAvatarURL(login, true) : avatarURL; +function getAvatarUrl(avatarURL, accountID) { + return isDefaultAvatar(avatarURL) ? getDefaultAvatarURL(accountID, true) : avatarURL; } /** @@ -163,11 +163,11 @@ function getAvatarUrl(avatarURL, login) { * This removes that part of the URL so the full version of the image can load. * * @param {String} [avatarURL] - * @param {String} [login] + * @param {Number} [accountID] * @returns {String|Function} */ -function getFullSizeAvatar(avatarURL, login) { - const source = getAvatar(avatarURL, login); +function getFullSizeAvatar(avatarURL, accountID) { + const source = getAvatar(avatarURL, accountID); if (!_.isString(source)) { return source; } @@ -179,11 +179,11 @@ function getFullSizeAvatar(avatarURL, login) { * source URL (before the file type) if it doesn't exist there already. * * @param {String} avatarURL - * @param {String} login + * @param {Number} accountID * @returns {String|Function} */ -function getSmallSizeAvatar(avatarURL, login) { - const source = getAvatar(avatarURL, login); +function getSmallSizeAvatar(avatarURL, accountID) { + const source = getAvatar(avatarURL, accountID); if (!_.isString(source)) { return source; } @@ -201,8 +201,18 @@ function getSmallSizeAvatar(avatarURL, login) { return `${source.substring(0, lastPeriodIndex)}_128${source.substring(lastPeriodIndex)}`; } +/** + * Generate a random accountID. + * Uses the same approach of 'generateReportID'. + * + * @returns {String} + */ +function generateAccountID() { + return (Math.floor(Math.random() * 2 ** 21) * 2 ** 32 + Math.floor(Math.random() * 2 ** 32)).toString(); +} + export { - hashLogin, + hashText, hasLoginListError, hasLoginListInfo, getLoginListBrickRoadIndicator, @@ -213,4 +223,5 @@ export { getAvatarUrl, getSmallSizeAvatar, getFullSizeAvatar, + generateAccountID, }; diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 936e3edaf170..4fe53aedb646 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -257,11 +257,13 @@ function buildOnyxDataForMoneyRequest( * @param {Number} amount - always in the smallest unit of the currency * @param {String} currency * @param {String} payeeEmail + * @param {Number} payeeAccountID * @param {Object} participant * @param {String} comment */ -function requestMoney(report, amount, currency, payeeEmail, participant, comment) { +function requestMoney(report, amount, currency, payeeEmail, payeeAccountID, participant, comment) { const payerEmail = OptionsListUtils.addSMSDomainIfPhoneNumber(participant.login); + const payerAccountID = participant.accountID; const isPolicyExpenseChat = participant.isPolicyExpenseChat || participant.isOwnPolicyExpenseChat; // STEP 1: Get existing chat report OR build a new optimistic one @@ -275,13 +277,13 @@ function requestMoney(report, amount, currency, payeeEmail, participant, comment } if (!chatReport) { - chatReport = ReportUtils.getChatByParticipants([payerEmail]); + chatReport = ReportUtils.getChatByParticipants([payerAccountID]); } // If we still don't have a report, it likely doens't exist and we need to build an optimistic one if (!chatReport) { isNewChatReport = true; - chatReport = ReportUtils.buildOptimisticChatReport([payerEmail]); + chatReport = ReportUtils.buildOptimisticChatReport([payerAccountID]); } // STEP 2: Get existing IOU report and update its total OR build a new optimistic one @@ -295,12 +297,12 @@ function requestMoney(report, amount, currency, payeeEmail, participant, comment // Because of the Expense reports are stored as negative values, we substract the total from the amount iouReport.total -= amount; } else { - iouReport = IOUUtils.updateIOUOwnerAndTotal(iouReports[`${ONYXKEYS.COLLECTION.REPORT}${chatReport.iouReportID}`], payeeEmail, amount, currency); + iouReport = IOUUtils.updateIOUOwnerAndTotal(iouReports[`${ONYXKEYS.COLLECTION.REPORT}${chatReport.iouReportID}`], payeeAccountID, amount, currency); } } else { iouReport = isPolicyExpenseChat - ? ReportUtils.buildOptimisticExpenseReport(chatReport.reportID, chatReport.policyID, payeeEmail, amount, currency) - : ReportUtils.buildOptimisticIOUReport(payeeEmail, payerEmail, amount, chatReport.reportID, currency); + ? ReportUtils.buildOptimisticExpenseReport(chatReport.reportID, chatReport.policyID, payeeEmail, payeeAccountID, amount, currency) + : ReportUtils.buildOptimisticIOUReport(payeeEmail, payeeAccountID, payerAccountID, amount, chatReport.reportID, currency); } // STEP 3: Build optimistic transaction @@ -380,6 +382,7 @@ function requestMoney(report, amount, currency, payeeEmail, participant, comment * ] * @param {Array} participants * @param {String} currentUserLogin + * @param {Number} currentUserAccountID * @param {Number} amount - always in the smallest unit of the currency * @param {String} comment * @param {String} currency @@ -387,13 +390,13 @@ function requestMoney(report, amount, currency, payeeEmail, participant, comment * * @return {Object} */ -function createSplitsAndOnyxData(participants, currentUserLogin, amount, comment, currency, existingGroupChatReportID = '') { +function createSplitsAndOnyxData(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, existingGroupChatReportID = '') { const currentUserEmail = OptionsListUtils.addSMSDomainIfPhoneNumber(currentUserLogin); - const participantLogins = _.map(participants, (participant) => OptionsListUtils.addSMSDomainIfPhoneNumber(participant.login).toLowerCase()); + const participantAccountIDs = _.map(participants, (participant) => participant.accountID); const existingGroupChatReport = existingGroupChatReportID ? chatReports[`${ONYXKEYS.COLLECTION.REPORT}${existingGroupChatReportID}`] - : ReportUtils.getChatByParticipants(participantLogins); - const groupChatReport = existingGroupChatReport || ReportUtils.buildOptimisticChatReport(participantLogins); + : ReportUtils.getChatByParticipants(participantAccountIDs); + const groupChatReport = existingGroupChatReport || ReportUtils.buildOptimisticChatReport(participantAccountIDs); // ReportID is -2 (aka "deleted") on the group transaction: https://github.com/Expensify/Auth/blob/3fa2698654cd4fbc30f9de38acfca3fbeb7842e4/auth/command/SplitTransaction.cpp#L24-L27 const formattedParticipants = Localize.arrayToString([currentUserLogin, ..._.map(participants, (participant) => participant.login)]); @@ -502,11 +505,12 @@ function createSplitsAndOnyxData(participants, currentUserLogin, amount, comment // Loop through participants creating individual chats, iouReports and reportActionIDs as needed const splitAmount = IOUUtils.calculateAmount(participants.length, amount, false); - const splits = [{email: currentUserEmail, amount: IOUUtils.calculateAmount(participants.length, amount, true)}]; + const splits = [{email: currentUserEmail, accountID: currentUserAccountID, amount: IOUUtils.calculateAmount(participants.length, amount, true)}]; const hasMultipleParticipants = participants.length > 1; _.each(participants, (participant) => { const email = OptionsListUtils.addSMSDomainIfPhoneNumber(participant.login).toLowerCase(); + const accountID = participant.accountID; if (email === currentUserEmail) { return; } @@ -515,20 +519,20 @@ function createSplitsAndOnyxData(participants, currentUserLogin, amount, comment // If we only have one participant and the request was initiated from the global create menu, i.e. !existingGroupChatReportID, the oneOnOneChatReport is the groupChatReport let oneOnOneChatReport; let isNewOneOnOneChatReport = false; - oneOnOneChatReport = !hasMultipleParticipants && !existingGroupChatReportID ? groupChatReport : ReportUtils.getChatByParticipants([email]); + oneOnOneChatReport = !hasMultipleParticipants && !existingGroupChatReportID ? groupChatReport : ReportUtils.getChatByParticipants([accountID]); if (!oneOnOneChatReport) { isNewOneOnOneChatReport = true; - oneOnOneChatReport = ReportUtils.buildOptimisticChatReport([email]); + oneOnOneChatReport = ReportUtils.buildOptimisticChatReport([accountID]); } // STEP 2: Get existing IOU report and update its total OR build a new optimistic one const isNewOneOnOneIOUReport = !oneOnOneChatReport.iouReportID; let oneOnOneIOUReport; if (!isNewOneOnOneIOUReport) { - oneOnOneIOUReport = IOUUtils.updateIOUOwnerAndTotal(iouReports[`${ONYXKEYS.COLLECTION.REPORT}${oneOnOneChatReport.iouReportID}`], currentUserEmail, splitAmount, currency); + oneOnOneIOUReport = IOUUtils.updateIOUOwnerAndTotal(iouReports[`${ONYXKEYS.COLLECTION.REPORT}${oneOnOneChatReport.iouReportID}`], currentUserAccountID, splitAmount, currency); } else { - oneOnOneIOUReport = ReportUtils.buildOptimisticIOUReport(currentUserEmail, email, splitAmount, oneOnOneChatReport.reportID, currency); + oneOnOneIOUReport = ReportUtils.buildOptimisticIOUReport(currentUserEmail, currentUserAccountID, accountID, splitAmount, oneOnOneChatReport.reportID, currency); } // STEP 3: Build optimistic transaction @@ -583,6 +587,7 @@ function createSplitsAndOnyxData(participants, currentUserLogin, amount, comment const splitData = { email, + accountID, amount: splitAmount, iouReportID: oneOnOneIOUReport.reportID, chatReportID: oneOnOneChatReport.reportID, @@ -619,13 +624,14 @@ function createSplitsAndOnyxData(participants, currentUserLogin, amount, comment /** * @param {Array} participants * @param {String} currentUserLogin + * @param {Number} currentUserAccountID * @param {Number} amount - always in smallest currency unit * @param {String} comment * @param {String} currency * @param {String} existingGroupChatReportID */ -function splitBill(participants, currentUserLogin, amount, comment, currency, existingGroupChatReportID = '') { - const {groupData, splits, onyxData} = createSplitsAndOnyxData(participants, currentUserLogin, amount, comment, currency, existingGroupChatReportID); +function splitBill(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, existingGroupChatReportID = '') { + const {groupData, splits, onyxData} = createSplitsAndOnyxData(participants, currentUserLogin, currentUserAccountID, amount, comment, currency, existingGroupChatReportID); API.write( 'SplitBill', @@ -648,12 +654,13 @@ function splitBill(participants, currentUserLogin, amount, comment, currency, ex /** * @param {Array} participants * @param {String} currentUserLogin + * @param {Number} currentUserAccountID * @param {Number} amount - always in smallest currency unit * @param {String} comment * @param {String} currency */ -function splitBillAndOpenReport(participants, currentUserLogin, amount, comment, currency) { - const {groupData, splits, onyxData} = createSplitsAndOnyxData(participants, currentUserLogin, amount, comment, currency); +function splitBillAndOpenReport(participants, currentUserLogin, currentUserAccountID, amount, comment, currency) { + const {groupData, splits, onyxData} = createSplitsAndOnyxData(participants, currentUserLogin, currentUserAccountID, amount, comment, currency); API.write( 'SplitBillAndOpenReport', @@ -696,7 +703,7 @@ function deleteMoneyRequest(chatReportID, iouReportID, moneyRequestAction, shoul iouReportID, ); - const currentUserEmail = optimisticIOUAction.actorEmail; + const currentUserAccountID = optimisticIOUAction.actorAccountID; let updatedIOUReport = {}; if (ReportUtils.isExpenseReport(iouReportID)) { updatedIOUReport = {...iouReport}; @@ -704,7 +711,7 @@ function deleteMoneyRequest(chatReportID, iouReportID, moneyRequestAction, shoul // Because of the Expense reports are stored as negative values, we add the total from the amount updatedIOUReport.total += amount; } else { - updatedIOUReport = IOUUtils.updateIOUOwnerAndTotal(iouReport, currentUserEmail, amount, moneyRequestAction.originalMessage.currency, CONST.IOU.REPORT_ACTION_TYPE.DELETE); + updatedIOUReport = IOUUtils.updateIOUOwnerAndTotal(iouReport, currentUserAccountID, amount, moneyRequestAction.originalMessage.currency, CONST.IOU.REPORT_ACTION_TYPE.DELETE); } updatedIOUReport.lastMessageText = optimisticIOUAction.message[0].text; updatedIOUReport.lastMessageHtml = optimisticIOUAction.message[0].html; @@ -815,16 +822,18 @@ function buildPayPalPaymentUrl(amount, submitterPayPalMeAddress, currency) { * @param {String} currency * @param {String} comment * @param {String} paymentMethodType - * @param {String} managerEmail - Email of the person sending the money + * @param {String} managerID - Account ID of the person sending the money * @param {Object} recipient - The user receiving the money * @returns {Object} */ -function getSendMoneyParams(report, amount, currency, comment, paymentMethodType, managerEmail, recipient) { +function getSendMoneyParams(report, amount, currency, comment, paymentMethodType, managerID, recipient) { const recipientEmail = OptionsListUtils.addSMSDomainIfPhoneNumber(recipient.login); + const recipientAccountID = recipient.accountID; const newIOUReportDetails = JSON.stringify({ amount, currency, requestorEmail: recipientEmail, + requestorAccountID: recipientAccountID, comment, idempotencyKey: Str.guid(), }); @@ -832,13 +841,13 @@ function getSendMoneyParams(report, amount, currency, comment, paymentMethodType let chatReport = report.reportID ? report : null; let isNewChat = false; if (!chatReport) { - chatReport = ReportUtils.getChatByParticipants([recipientEmail]); + chatReport = ReportUtils.getChatByParticipants([recipientAccountID]); } if (!chatReport) { - chatReport = ReportUtils.buildOptimisticChatReport([recipientEmail]); + chatReport = ReportUtils.buildOptimisticChatReport([recipientAccountID]); isNewChat = true; } - const optimisticIOUReport = ReportUtils.buildOptimisticIOUReport(recipientEmail, managerEmail, amount, chatReport.reportID, currency, true); + const optimisticIOUReport = ReportUtils.buildOptimisticIOUReport(recipientEmail, recipientAccountID, managerID, amount, chatReport.reportID, currency, true); const optimisticTransaction = TransactionUtils.buildOptimisticTransaction(amount * 100, currency, optimisticIOUReport.reportID, comment); const optimisticTransactionData = { @@ -1102,11 +1111,11 @@ function getPayMoneyRequestParams(chatReport, iouReport, recipient, paymentMetho * @param {Number} amount * @param {String} currency * @param {String} comment - * @param {String} managerEmail - Email of the person sending the money + * @param {String} managerID - Account ID of the person sending the money * @param {Object} recipient - The user receiving the money */ -function sendMoneyElsewhere(report, amount, currency, comment, managerEmail, recipient) { - const {params, optimisticData, successData, failureData} = getSendMoneyParams(report, amount, currency, comment, CONST.IOU.PAYMENT_TYPE.ELSEWHERE, managerEmail, recipient); +function sendMoneyElsewhere(report, amount, currency, comment, managerID, recipient) { + const {params, optimisticData, successData, failureData} = getSendMoneyParams(report, amount, currency, comment, CONST.IOU.PAYMENT_TYPE.ELSEWHERE, managerID, recipient); API.write('SendMoneyElsewhere', params, {optimisticData, successData, failureData}); @@ -1118,11 +1127,11 @@ function sendMoneyElsewhere(report, amount, currency, comment, managerEmail, rec * @param {Number} amount * @param {String} currency * @param {String} comment - * @param {String} managerEmail - Email of the person sending the money + * @param {String} managerID - Account ID of the person sending the money * @param {Object} recipient - The user receiving the money */ -function sendMoneyWithWallet(report, amount, currency, comment, managerEmail, recipient) { - const {params, optimisticData, successData, failureData} = getSendMoneyParams(report, amount, currency, comment, CONST.IOU.PAYMENT_TYPE.EXPENSIFY, managerEmail, recipient); +function sendMoneyWithWallet(report, amount, currency, comment, managerID, recipient) { + const {params, optimisticData, successData, failureData} = getSendMoneyParams(report, amount, currency, comment, CONST.IOU.PAYMENT_TYPE.EXPENSIFY, managerID, recipient); API.write('SendMoneyWithWallet', params, {optimisticData, successData, failureData}); @@ -1134,11 +1143,11 @@ function sendMoneyWithWallet(report, amount, currency, comment, managerEmail, re * @param {Number} amount * @param {String} currency * @param {String} comment - * @param {String} managerEmail - Email of the person sending the money + * @param {String} managerID - Account ID of the person sending the money * @param {Object} recipient - The user receiving the money */ -function sendMoneyViaPaypal(report, amount, currency, comment, managerEmail, recipient) { - const {params, optimisticData, successData, failureData} = getSendMoneyParams(report, amount, currency, comment, CONST.IOU.PAYMENT_TYPE.PAYPAL_ME, managerEmail, recipient); +function sendMoneyViaPaypal(report, amount, currency, comment, managerID, recipient) { + const {params, optimisticData, successData, failureData} = getSendMoneyParams(report, amount, currency, comment, CONST.IOU.PAYMENT_TYPE.PAYPAL_ME, managerID, recipient); API.write('SendMoneyViaPaypal', params, {optimisticData, successData, failureData}); @@ -1156,6 +1165,7 @@ function sendMoneyViaPaypal(report, amount, currency, comment, managerEmail, rec function payMoneyRequest(paymentType, chatReport, iouReport) { const recipient = { login: iouReport.ownerEmail, + accountID: iouReport.ownerAccountID, payPalMeAddress: iouReport.submitterPayPalMeAddress, }; const {params, optimisticData, successData, failureData} = getPayMoneyRequestParams(chatReport, iouReport, recipient, paymentType); diff --git a/src/libs/actions/PersonalDetails.js b/src/libs/actions/PersonalDetails.js index b362436def5c..fc0eb5414610 100644 --- a/src/libs/actions/PersonalDetails.js +++ b/src/libs/actions/PersonalDetails.js @@ -11,14 +11,18 @@ import ROUTES from '../../ROUTES'; import Navigation from '../Navigation/Navigation'; let currentUserEmail = ''; +let currentUserAccountID; Onyx.connect({ key: ONYXKEYS.SESSION, - callback: (val) => (currentUserEmail = val ? val.email : ''), + callback: (val) => { + currentUserEmail = val ? val.email : ''; + currentUserAccountID = val ? val.accountID : -1; + }, }); let personalDetails; Onyx.connect({ - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, callback: (val) => (personalDetails = val), }); @@ -102,9 +106,9 @@ function updatePronouns(pronouns) { optimisticData: [ { onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, value: { - [currentUserEmail]: { + [currentUserAccountID]: { pronouns, }, }, @@ -127,9 +131,9 @@ function updateDisplayName(firstName, lastName) { optimisticData: [ { onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, value: { - [currentUserEmail]: { + [currentUserAccountID]: { firstName, lastName, displayName: getDisplayName(currentUserEmail, { @@ -252,9 +256,9 @@ function updateAutomaticTimezone(timezone) { optimisticData: [ { onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, value: { - [currentUserEmail]: { + [currentUserAccountID]: { timezone, }, }, @@ -283,7 +287,7 @@ function updateSelectedTimezone(selectedTimezone) { optimisticData: [ { onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, value: { [currentUserEmail]: { timezone, @@ -362,9 +366,9 @@ function updateAvatar(file) { const optimisticData = [ { onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, value: { - [currentUserEmail]: { + [currentUserAccountID]: { avatar: file.uri, avatarThumbnail: file.uri, originalFileName: file.name, @@ -382,9 +386,9 @@ function updateAvatar(file) { const successData = [ { onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, value: { - [currentUserEmail]: { + [currentUserAccountID]: { pendingFields: { avatar: null, }, @@ -395,11 +399,11 @@ function updateAvatar(file) { const failureData = [ { onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, value: { - [currentUserEmail]: { - avatar: personalDetails[currentUserEmail].avatar, - avatarThumbnail: personalDetails[currentUserEmail].avatarThumbnail || personalDetails[currentUserEmail].avatar, + [currentUserAccountID]: { + avatar: personalDetails[currentUserAccountID].avatar, + avatarThumbnail: personalDetails[currentUserAccountID].avatarThumbnail || personalDetails[currentUserAccountID].avatar, pendingFields: { avatar: null, }, @@ -416,7 +420,7 @@ function updateAvatar(file) { */ function deleteAvatar() { // We want to use the old dot avatar here as this affects both platforms. - const defaultAvatar = UserUtils.getDefaultAvatarURL(currentUserEmail); + const defaultAvatar = UserUtils.getDefaultAvatarURL(currentUserAccountID); API.write( 'DeleteUserAvatar', @@ -425,9 +429,9 @@ function deleteAvatar() { optimisticData: [ { onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, value: { - [currentUserEmail]: { + [currentUserAccountID]: { avatar: defaultAvatar, }, }, @@ -436,10 +440,10 @@ function deleteAvatar() { failureData: [ { onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, value: { - [currentUserEmail]: { - avatar: personalDetails[currentUserEmail].avatar, + [currentUserAccountID]: { + avatar: personalDetails[currentUserAccountID].avatar, }, }, }, @@ -452,8 +456,8 @@ function deleteAvatar() { * Clear error and pending fields for the current user's avatar */ function clearAvatarErrors() { - Onyx.merge(ONYXKEYS.PERSONAL_DETAILS, { - [currentUserEmail]: { + Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, { + [currentUserAccountID]: { errorFields: { avatar: null, }, diff --git a/src/libs/actions/Policy.js b/src/libs/actions/Policy.js index 66bcf0ba1392..833b7359acb6 100644 --- a/src/libs/actions/Policy.js +++ b/src/libs/actions/Policy.js @@ -51,10 +51,12 @@ Onyx.connect({ }); let sessionEmail = ''; +let sessionAccountID = 0; Onyx.connect({ key: ONYXKEYS.SESSION, callback: (val) => { sessionEmail = lodashGet(val, 'email', ''); + sessionAccountID = lodashGet(val, 'accountID', 0); }, }); @@ -186,34 +188,35 @@ function hasActiveFreePolicy(policies) { * Remove the passed members from the policy employeeList * * @param {Array} members + * @param {Array} accountIDs * @param {String} policyID */ -function removeMembers(members, policyID) { +function removeMembers(members, accountIDs, policyID) { // In case user selects only themselves (admin), their email will be filtered out and the members // array passed will be empty, prevent the function from proceeding in that case as there is noone to remove if (members.length === 0) { return; } - const membersListKey = `${ONYXKEYS.COLLECTION.POLICY_MEMBER_LIST}${policyID}`; + const membersListKey = `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`; const optimisticData = [ { onyxMethod: Onyx.METHOD.MERGE, key: membersListKey, - value: _.object(members, Array(members.length).fill({pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE})), + value: _.object(accountIDs, Array(members.length).fill({pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE})), }, ]; const successData = [ { onyxMethod: Onyx.METHOD.MERGE, key: membersListKey, - value: _.object(members, Array(members.length).fill(null)), + value: _.object(accountIDs, Array(members.length).fill(null)), }, ]; const failureData = [ { onyxMethod: Onyx.METHOD.MERGE, key: membersListKey, - value: _.object(members, Array(members.length).fill({errors: ErrorUtils.getMicroSecondOnyxError('workspace.people.error.genericRemove')})), + value: _.object(accountIDs, Array(members.length).fill({errors: ErrorUtils.getMicroSecondOnyxError('workspace.people.error.genericRemove')})), }, ]; API.write( @@ -230,11 +233,11 @@ function removeMembers(members, policyID) { * Optimistically create a chat for each member of the workspace, creates both optimistic and success data for onyx. * * @param {String} policyID - * @param {Array} members + * @param {Object} invitedEmailsToAccountIDs * @param {Array} betas * @returns {Object} - object with onyxSuccessData, onyxOptimisticData, and optimisticReportIDs (map login to reportID) */ -function createPolicyExpenseChats(policyID, members, betas) { +function createPolicyExpenseChats(policyID, invitedEmailsToAccountIDs, betas) { const workspaceMembersChats = { onyxSuccessData: [], onyxOptimisticData: [], @@ -247,11 +250,12 @@ function createPolicyExpenseChats(policyID, members, betas) { return workspaceMembersChats; } - _.each(members, (login) => { - const oldChat = ReportUtils.getChatByParticipantsAndPolicy([sessionEmail, login], policyID); + _.each(invitedEmailsToAccountIDs, (accountID, login) => { + const oldChat = ReportUtils.getChatByParticipantsAndPolicy([sessionAccountID, accountID], policyID); // If the chat already exists, we don't want to create a new one - just make sure it's not archived if (oldChat) { + // TODO: figure out if we need to use accountID keys here workspaceMembersChats.reportCreationData[login] = { reportID: oldChat.reportID, }; @@ -265,7 +269,7 @@ function createPolicyExpenseChats(policyID, members, betas) { }); return; } - const optimisticReport = ReportUtils.buildOptimisticChatReport([sessionEmail, login], undefined, CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT, policyID, login); + const optimisticReport = ReportUtils.buildOptimisticChatReport([sessionAccountID, accountID], undefined, CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT, policyID, login, accountID); const optimisticCreatedAction = ReportUtils.buildOptimisticCreatedReportAction(optimisticReport.ownerEmail); workspaceMembersChats.reportCreationData[login] = { @@ -323,17 +327,17 @@ function createPolicyExpenseChats(policyID, members, betas) { /** * Adds members to the specified workspace/policyID * - * @param {Array} memberLogins + * @param {Object} invitedEmailsToAccountIDs * @param {String} welcomeNote * @param {String} policyID * @param {Array} betas */ -function addMembersToWorkspace(memberLogins, welcomeNote, policyID, betas) { +function addMembersToWorkspace(invitedEmailsToAccountIDs, welcomeNote, policyID, betas) { const membersListKey = `${ONYXKEYS.COLLECTION.POLICY_MEMBER_LIST}${policyID}`; - const logins = _.map(memberLogins, (memberLogin) => OptionsListUtils.addSMSDomainIfPhoneNumber(memberLogin)); + const accountIDs = _.values(invitedEmailsToAccountIDs); // create onyx data for policy expense chats for each new member - const membersChats = createPolicyExpenseChats(policyID, logins, betas); + const membersChats = createPolicyExpenseChats(policyID, invitedEmailsToAccountIDs, betas); const optimisticData = [ { @@ -341,7 +345,7 @@ function addMembersToWorkspace(memberLogins, welcomeNote, policyID, betas) { key: membersListKey, // Convert to object with each key containing {pendingAction: ‘add’} - value: _.object(logins, Array(logins.length).fill({pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD})), + value: _.object(accountIDs, Array(accountIDs.length).fill({pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD})), }, ...membersChats.onyxOptimisticData, ]; @@ -353,7 +357,7 @@ function addMembersToWorkspace(memberLogins, welcomeNote, policyID, betas) { // Convert to object with each key clearing pendingAction. We don’t // need to remove the members since that will be handled by onClose of OfflineWithFeedback. - value: _.object(logins, Array(logins.length).fill({pendingAction: null, errors: null})), + value: _.object(accountIDs, Array(accountIDs.length).fill({pendingAction: null, errors: null})), }, ...membersChats.onyxSuccessData, ]; @@ -366,8 +370,8 @@ function addMembersToWorkspace(memberLogins, welcomeNote, policyID, betas) { // Convert to object with each key containing the error. We don’t // need to remove the members since that is handled by onClose of OfflineWithFeedback. value: _.object( - logins, - Array(logins.length).fill({ + accountIDs, + Array(accountIDs.length).fill({ errors: ErrorUtils.getMicroSecondOnyxError('workspace.people.error.genericAdd'), }), ), @@ -375,6 +379,7 @@ function addMembersToWorkspace(memberLogins, welcomeNote, policyID, betas) { ...membersChats.onyxFailureData, ]; + const logins = _.map(_.keys(invitedEmailsToAccountIDs), (memberLogin) => OptionsListUtils.addSMSDomainIfPhoneNumber(memberLogin)); API.write( 'AddMembersToWorkspace', { @@ -1079,8 +1084,13 @@ function openWorkspaceInvitePage(policyID, clientMemberEmails) { }); } -function setWorkspaceInviteMembersDraft(policyID, memberEmails) { - Onyx.set(`${ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MEMBERS_DRAFT}${policyID}`, memberEmails); + +/** + * @param {String} policyID + * @param {Object} invitedEmailsToAccountIDs + */ +function setWorkspaceInviteMembersDraft(policyID, invitedEmailsToAccountIDs) { + Onyx.set(`${ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MEMBERS_DRAFT}${policyID}`, invitedEmailsToAccountIDs); } export { diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 3c955109c94d..152a56f56853 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -280,7 +280,7 @@ function addActions(reportID, text = '', file) { parameters.timezone = JSON.stringify(timezone); optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, value: {[currentUserEmail]: {timezone}}, }); DateUtils.setTimezoneUpdated(); @@ -448,14 +448,13 @@ function openReport(reportID, participantList = [], newReportObject = {}, parent /** * This will find an existing chat, or create a new one if none exists, for the given user or set of users. It will then navigate to this chat. * - * @param {Array} userLogins list of user logins. + * @param {Array} userAccountIDs list of user accountIDs. */ -function navigateToAndOpenReport(userLogins) { - const formattedUserLogins = _.map(userLogins, (login) => OptionsListUtils.addSMSDomainIfPhoneNumber(login).toLowerCase()); +function navigateToAndOpenReport(userAccountIDs) { let newChat = {}; - const chat = ReportUtils.getChatByParticipants(formattedUserLogins); + const chat = ReportUtils.getChatByParticipants(userAccountIDs); if (!chat) { - newChat = ReportUtils.buildOptimisticChatReport(formattedUserLogins); + newChat = ReportUtils.buildOptimisticChatReport(userAccountIDs); } const reportID = chat ? chat.reportID : newChat.reportID; @@ -1150,7 +1149,7 @@ function navigateToConciergeChat() { // we need to ensure that the server data has been successfully pulled Welcome.serverDataIsReadyPromise().then(() => { // If we don't have a chat with Concierge then create it - navigateToAndOpenReport([CONST.EMAIL.CONCIERGE]); + navigateToAndOpenReport([CONST.ACCOUNT_ID.CONCIERGE]); }); } else { Navigation.navigate(ROUTES.getReportRoute(conciergeChatReportID)); diff --git a/src/libs/actions/Task.js b/src/libs/actions/Task.js index ec150a332155..9f3abcfcf5f4 100644 --- a/src/libs/actions/Task.js +++ b/src/libs/actions/Task.js @@ -23,29 +23,30 @@ function clearOutTaskInfo() { * Function title is createTask for consistency with the rest of the actions * and also because we can create a task without assigning it to anyone * @param {String} currentUserEmail + * @param {Number} currentUserAccountID * @param {String} parentReportID * @param {String} title * @param {String} description - * @param {String} assignee + * @param {Number} assigneeAccountID * */ -function createTaskAndNavigate(currentUserEmail, parentReportID, title, description, assignee = '') { +function createTaskAndNavigate(currentUserEmail, currentUserAccountID, parentReportID, title, description, assigneeAccountID = 0) { // Create the task report - const optimisticTaskReport = ReportUtils.buildOptimisticTaskReport(currentUserEmail, assignee, parentReportID, title, description); + const optimisticTaskReport = ReportUtils.buildOptimisticTaskReport(currentUserEmail, currentUserAccountID, assigneeAccountID, parentReportID, title, description); // Grab the assigneeChatReportID if there is an assignee and if it's not the same as the parentReportID // then we create an optimistic add comment report action on the assignee's chat to notify them of the task - const assigneeChatReportID = lodashGet(ReportUtils.getChatByParticipants([assignee]), 'reportID'); + const assigneeChatReportID = lodashGet(ReportUtils.getChatByParticipants([assigneeAccountID]), 'reportID'); const taskReportID = optimisticTaskReport.reportID; let optimisticAssigneeAddComment; if (assigneeChatReportID && assigneeChatReportID !== parentReportID) { - optimisticAssigneeAddComment = ReportUtils.buildOptimisticTaskCommentReportAction(taskReportID, title, assignee, `Assigned a task to you: ${title}`, parentReportID); + optimisticAssigneeAddComment = ReportUtils.buildOptimisticTaskCommentReportAction(taskReportID, title, assigneeAccountID, `Assigned a task to you: ${title}`, parentReportID); } // Create the CreatedReportAction on the task const optimisticTaskCreatedAction = ReportUtils.buildOptimisticCreatedReportAction(optimisticTaskReport.reportID); - const optimisticAddCommentReport = ReportUtils.buildOptimisticTaskCommentReportAction(taskReportID, title, assignee, `Created a task: ${title}`, parentReportID); + const optimisticAddCommentReport = ReportUtils.buildOptimisticTaskCommentReportAction(taskReportID, title, assigneeAccountID, `Created a task: ${title}`, parentReportID); const currentTime = DateUtils.getDBTime(); @@ -55,6 +56,7 @@ function createTaskAndNavigate(currentUserEmail, parentReportID, title, descript lastVisibleActionCreated: currentTime, lastMessageText: Str.htmlDecode(lastCommentText), lastActorEmail: currentUserEmail, + lastActorAccountID: currentUserAccountID, lastReadTime: currentTime, }; @@ -125,6 +127,7 @@ function createTaskAndNavigate(currentUserEmail, parentReportID, title, descript lastVisibleActionCreated: currentTime, lastMessageText: Str.htmlDecode(lastAssigneeCommentText), lastActorEmail: currentUserEmail, + lastActorAccountID: currentUserAccountID, lastReadTime: currentTime, }; @@ -158,7 +161,7 @@ function createTaskAndNavigate(currentUserEmail, parentReportID, title, descript reportName: optimisticTaskReport.reportName, title: optimisticTaskReport.reportName, description: optimisticTaskReport.description, - assignee, + assigneeAccountID, assigneeChatReportID, assigneeChatReportActionID: optimisticAssigneeAddComment ? optimisticAssigneeAddComment.reportAction.reportActionID : 0, }, @@ -237,6 +240,7 @@ function reopenTask(taskReportID, taskTitle) { lastVisibleActionCreated: reopenedTaskReportAction.created, lastMessageText: message, lastActorEmail: reopenedTaskReportAction.actorEmail, + lastActorAccountID: reopenedTaskReportAction.actorAccountID, lastReadTime: reopenedTaskReportAction.created, }, }, @@ -277,15 +281,16 @@ function reopenTask(taskReportID, taskTitle) { /** * @function editTask * @param {object} report - * @param {string} ownerEmail + * @param {String} ownerEmail + * @param {Number} ownerAccountID * @param {string} title * @param {string} description - * @param {string} assignee + * @param {Number} assigneeAccountID * @returns {object} action * */ -function editTaskAndNavigate(report, ownerEmail, title, description, assignee) { +function editTaskAndNavigate(report, ownerEmail, ownerAccountID, title, description, assigneeAccountID) { // Create the EditedReportAction on the task const editTaskReportAction = ReportUtils.buildOptimisticEditedTaskReportAction(ownerEmail); @@ -295,9 +300,9 @@ function editTaskAndNavigate(report, ownerEmail, title, description, assignee) { // If we make a change to the assignee, we want to add a comment to the assignee's chat let optimisticAssigneeAddComment; let assigneeChatReportID; - if (assignee && assignee !== report.managerEmail) { - assigneeChatReportID = ReportUtils.getChatByParticipants([assignee]).reportID; - optimisticAssigneeAddComment = ReportUtils.buildOptimisticTaskCommentReportAction(report.reportID, reportName, assignee, `Assigned a task to you: ${reportName}`); + if (assigneeAccountID && assigneeAccountID !== report.managerID) { + assigneeChatReportID = ReportUtils.getChatByParticipants([assigneeAccountID]).reportID; + optimisticAssigneeAddComment = ReportUtils.buildOptimisticTaskCommentReportAction(report.reportID, reportName, assigneeAccountID, `Assigned a task to you: ${reportName}`); } const optimisticData = [ @@ -312,7 +317,7 @@ function editTaskAndNavigate(report, ownerEmail, title, description, assignee) { value: { reportName, description: description.trim(), - managerEmail: assignee || report.managerEmail, + managerID: assigneeAccountID || report.managerID, }, }, ]; @@ -326,7 +331,7 @@ function editTaskAndNavigate(report, ownerEmail, title, description, assignee) { { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`, - value: {reportName: report.reportName, description: report.description, assignee: report.managerEmail}, + value: {reportName: report.reportName, description: report.description, assignee: report.managerEmail, assigneeAccountID: report.managerID}, }, ]; @@ -337,7 +342,7 @@ function editTaskAndNavigate(report, ownerEmail, title, description, assignee) { const optimisticAssigneeReport = { lastVisibleActionCreated: currentTime, lastMessageText: Str.htmlDecode(lastAssigneeCommentText), - lastActorEmail: ownerEmail, + lastActorAccountID: ownerAccountID, lastReadTime: currentTime, }; @@ -368,6 +373,7 @@ function editTaskAndNavigate(report, ownerEmail, title, description, assignee) { title: reportName, description: (description || report.description).trim(), assignee: assignee || report.managerEmail, + assigneeAccountID: assigneeAccountID || report.managerID, editedTaskReportActionID: editTaskReportAction.reportActionID, assigneeChatReportActionID: optimisticAssigneeAddComment ? optimisticAssigneeAddComment.reportAction.reportActionID : 0, }, @@ -391,7 +397,6 @@ function setTaskReport(report) { * @param {string} title * @param {string} description */ - function setDetailsValue(title, description) { // This is only needed for creation of a new task and so it should only be stored locally Onyx.merge(ONYXKEYS.TASK, {title: title.trim(), description: description.trim()}); @@ -455,7 +460,6 @@ function setAssigneeValue(assignee, shareDestination, isCurrentUser = false) { * Sets the parentReportID value for the task * @param {string} parentReportID */ - function setParentReportID(parentReportID) { // This is only needed for creation of a new task and so it should only be stored locally Onyx.merge(ONYXKEYS.TASK, {parentReportID}); @@ -485,7 +489,7 @@ function getAssignee(details) { subtitle: '', }; } - const source = UserUtils.getAvatar(lodashGet(details, 'avatar', ''), lodashGet(details, 'login', '')); + const source = UserUtils.getAvatar(lodashGet(details, 'avatar', ''), lodashGet(details, 'accountID', -1)); return { icons: [{source, type: 'avatar', name: details.login}], displayName: details.displayName, diff --git a/src/libs/actions/User.js b/src/libs/actions/User.js index b90a22ceac8a..9e2ae4abd637 100644 --- a/src/libs/actions/User.js +++ b/src/libs/actions/User.js @@ -24,20 +24,20 @@ let currentEmail = ''; Onyx.connect({ key: ONYXKEYS.SESSION, callback: (val) => { - currentUserAccountID = lodashGet(val, 'accountID', ''); + currentUserAccountID = lodashGet(val, 'accountID', -1); currentEmail = lodashGet(val, 'email', ''); }, }); let myPersonalDetails = {}; Onyx.connect({ - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, callback: (val) => { - if (!val || !currentEmail) { + if (!val || !currentUserAccountID) { return; } - myPersonalDetails = val[currentEmail]; + myPersonalDetails = val[currentUserAccountID]; }, }); @@ -785,7 +785,7 @@ function setContactMethodAsDefault(newDefaultContactMethod) { }, { onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, value: { [newDefaultContactMethod]: { ...myPersonalDetails, @@ -833,7 +833,7 @@ function setContactMethodAsDefault(newDefaultContactMethod) { }, { onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, value: { [newDefaultContactMethod]: null, [oldDefaultContactMethod]: {...myPersonalDetails}, diff --git a/src/pages/DetailsPage.js b/src/pages/DetailsPage.js index 909b52da89b3..dff2260201ee 100755 --- a/src/pages/DetailsPage.js +++ b/src/pages/DetailsPage.js @@ -90,19 +90,31 @@ const getPhoneNumber = (details) => { class DetailsPage extends React.PureComponent { render() { const login = lodashGet(this.props.route.params, 'login', ''); - let details = lodashGet(this.props.personalDetails, login); + let details = _.find(this.props.personalDetails, (detail) => (detail.login === login.toLowerCase())); if (!details) { - details = { - login, - displayName: ReportUtils.getDisplayNameForParticipant(login), - avatar: UserUtils.getAvatar(lodashGet(details, 'avatar', ''), login), - }; + // TODO: these personal details aren't in my local test account but are in + // my staging account, i wonder why! + if (login == CONST.EMAIL.CONCIERGE) { + details = { + accountID: CONST.ACCOUNT_ID.CONCIERGE, + login, + displayName: 'Concierge', + avatar: UserUtils.getDefaultAvatar(CONST.ACCOUNT_ID.CONCIERGE), + }; + } else { + details = { + accountID: -1, + login, + displayName: login, + avatar: UserUtils.getDefaultAvatar(), + }; + } } const isSMSLogin = details.login ? Str.isSMSLogin(details.login) : false; - const shouldShowLocalTime = !ReportUtils.hasAutomatedExpensifyEmails([details.login]) && details.timezone; + const shouldShowLocalTime = !ReportUtils.hasAutomatedExpensifyAccountIDs([details.accountID]) && details.timezone; let pronouns = details.pronouns; if (pronouns && pronouns.startsWith(CONST.PRONOUNS.PREFIX)) { @@ -191,7 +203,7 @@ class DetailsPage extends React.PureComponent { Report.navigateToAndOpenReport([details.login])} + onPress={() => Report.navigateToAndOpenReport([details.accountID])} wrapperStyle={styles.breakAll} shouldShowRightIcon /> @@ -212,7 +224,7 @@ export default compose( withLocalize, withOnyx({ personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, }, loginList: { key: ONYXKEYS.LOGIN_LIST, diff --git a/src/pages/NewChatPage.js b/src/pages/NewChatPage.js index a5b9d8f39f2f..c4f4b81ade64 100755 --- a/src/pages/NewChatPage.js +++ b/src/pages/NewChatPage.js @@ -51,7 +51,7 @@ class NewChatPage extends Component { this.createChat = this.createChat.bind(this); this.createGroup = this.createGroup.bind(this); this.updateOptionsWithSearchTerm = this.updateOptionsWithSearchTerm.bind(this); - this.excludedGroupEmails = _.without(CONST.EXPENSIFY_EMAILS, CONST.EMAIL.CONCIERGE); + this.excludedGroupEmails = _.without(CONST.EXPENSIFY_ACCOUNT_IDS, CONST.ACCOUNT_ID.CONCIERGE); const {recentReports, personalDetails, userToInvite} = OptionsListUtils.getNewChatOptions( props.reports, @@ -194,7 +194,8 @@ class NewChatPage extends Component { * @param {Object} option */ createChat(option) { - Report.navigateToAndOpenReport([option.login]); + // TODO: do we need to generate an optimistic accountID here? + Report.navigateToAndOpenReport([option.accountID]); } /** @@ -206,11 +207,12 @@ class NewChatPage extends Component { return; } - const userLogins = _.pluck(this.state.selectedOptions, 'login'); - if (userLogins.length < 1) { + // TODO: do we need to generate an optimistic accountID here? + const userAccountIDs = _.pluck(this.state.selectedOptions, 'accountID'); + if (userAccountIDs.length < 1) { return; } - Report.navigateToAndOpenReport(userLogins); + Report.navigateToAndOpenReport(userAccountIDs); } render() { @@ -270,7 +272,7 @@ export default compose( key: ONYXKEYS.COLLECTION.REPORT, }, personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, }, betas: { key: ONYXKEYS.BETAS, diff --git a/src/pages/ProfilePage.js b/src/pages/ProfilePage.js index 932f489be5f4..f2b8d66e5e87 100755 --- a/src/pages/ProfilePage.js +++ b/src/pages/ProfilePage.js @@ -202,7 +202,7 @@ function ProfilePage(props) { Report.navigateToAndOpenReport([login])} + onPress={() => Report.navigateToAndOpenReport([accountID])} wrapperStyle={styles.breakAll} shouldShowRightIcon /> diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js index 98f7694533e4..7c515009abb4 100644 --- a/src/pages/ReportDetailsPage.js +++ b/src/pages/ReportDetailsPage.js @@ -69,7 +69,7 @@ const ReportDetailsPage = (props) => { // eslint-disable-next-line react-hooks/exhaustive-deps -- policy is a dependency because `getChatRoomSubtitle` calls `getPolicyName` which in turn retrieves the value from the `policy` value stored in Onyx const chatRoomSubtitle = useMemo(() => ReportUtils.getChatRoomSubtitle(props.report), [props.report, policy]); const canLeaveRoom = useMemo(() => ReportUtils.canLeaveRoom(props.report, !_.isEmpty(policy)), [policy, props.report]); - const participants = useMemo(() => lodashGet(props.report, 'participants', []), [props.report]); + const participants = useMemo(() => lodashGet(props.report, 'participantAccountIDs', []), [props.report]); const menuItems = useMemo(() => { if (isArchivedRoom) { @@ -122,7 +122,7 @@ const ReportDetailsPage = (props) => { const displayNamesWithTooltips = useMemo(() => { const hasMultipleParticipants = participants.length > 1; - return ReportUtils.getDisplayNamesWithTooltips(OptionsListUtils.getPersonalDetailsForLogins(participants, props.personalDetails), hasMultipleParticipants); + return ReportUtils.getDisplayNamesWithTooltips(OptionsListUtils.getPersonalDetailsForAccountIDs(participants, props.personalDetails), hasMultipleParticipants); }, [participants, props.personalDetails]); const chatRoomSubtitleText = chatRoomSubtitle ? ( @@ -197,7 +197,7 @@ export default compose( withReportOrNotFound, withOnyx({ personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, }, policies: { key: ONYXKEYS.COLLECTION.POLICY, diff --git a/src/pages/ReportParticipantsPage.js b/src/pages/ReportParticipantsPage.js index 0f9d070aaec3..3bf5763f91db 100755 --- a/src/pages/ReportParticipantsPage.js +++ b/src/pages/ReportParticipantsPage.js @@ -54,12 +54,12 @@ const defaultProps = { * @return {Array} */ const getAllParticipants = (report, personalDetails) => { - const {participants} = report; + const {participantAccountIDs} = report; - return _.chain(participants) - .map((login) => { - const userLogin = Str.removeSMSDomain(login); - const userPersonalDetail = lodashGet(personalDetails, login, {displayName: userLogin, avatar: ''}); + return _.chain(participantAccountIDs) + .map((accountID) => { + const userLogin = Str.removeSMSDomain(personalDetails.login) || 'Hidden'; + const userPersonalDetail = lodashGet(personalDetails, accountID, {displayName: personalDetails.displayName || 'Hidden', avatar: ''}); return { alternateText: userLogin, @@ -67,16 +67,16 @@ const getAllParticipants = (report, personalDetails) => { accountID: userPersonalDetail.accountID, icons: [ { - source: UserUtils.getAvatar(userPersonalDetail.avatar, login), - name: login, + source: UserUtils.getAvatar(userPersonalDetail.avatar, accountID), + name: userLogin, type: CONST.ICON_TYPE_AVATAR, }, ], - keyForList: userLogin, - login, + keyForList: accountID, + login: userLogin, text: userPersonalDetail.displayName, tooltipText: userLogin, - participantsList: [{login, displayName: userPersonalDetail.displayName}], + participantsList: [{accountID, displayName: userPersonalDetail.displayName}], }; }) .sortBy((participant) => participant.displayName.toLowerCase()) @@ -136,7 +136,7 @@ export default compose( withReportOrNotFound, withOnyx({ personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, }, }), )(ReportParticipantsPage); diff --git a/src/pages/SearchPage.js b/src/pages/SearchPage.js index 713cba5ad665..1911c5787d27 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -160,7 +160,8 @@ class SearchPage extends Component { }, ); } else { - Report.navigateToAndOpenReport([option.login]); + // TODO: do we need to create an optimistic accountID here? + Report.navigateToAndOpenReport([option.accountID]); } } @@ -206,7 +207,7 @@ export default compose( key: ONYXKEYS.COLLECTION.REPORT, }, personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, }, betas: { key: ONYXKEYS.BETAS, diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index c6f312574394..c9e5a5dc70d9 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -68,8 +68,8 @@ const defaultProps = { }; const HeaderView = (props) => { - const participants = lodashGet(props.report, 'participants', []); - const participantPersonalDetails = OptionsListUtils.getPersonalDetailsForLogins(participants, props.personalDetails); + const participants = lodashGet(props.report, 'participantAccountIDs', []); + const participantPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs(participants, props.personalDetails); const isMultipleParticipant = participants.length > 1; const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips(participantPersonalDetails, isMultipleParticipant); const isThread = ReportUtils.isThread(props.report); @@ -79,7 +79,7 @@ const HeaderView = (props) => { const reportHeaderData = (isTaskReport || !isThread) && props.report.parentReportID ? props.parentReport : props.report; const title = ReportUtils.getReportName(reportHeaderData); const subtitle = ReportUtils.getChatRoomSubtitle(reportHeaderData, props.parentReport); - const isConcierge = participants.length === 1 && _.contains(participants, CONST.EMAIL.CONCIERGE); + const isConcierge = participants.length === 1 && _.contains(participants, CONST.ACCOUNT_ID.CONCIERGE); const isAutomatedExpensifyAccount = participants.length === 1 && ReportUtils.hasAutomatedExpensifyEmails(participants); const guideCalendarLink = lodashGet(props.account, 'guideCalendarLink'); diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index 652ea5eb5d2c..9eb907fb18e6 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -393,7 +393,7 @@ export default compose( key: ONYXKEYS.ACCOUNT_MANAGER_REPORT_ID, }, personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, }, }), )(ReportScreen); diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js index 30ab83916f38..9681ae4ea6a6 100644 --- a/src/pages/home/report/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose.js @@ -432,7 +432,7 @@ class ReportActionCompose extends React.Component { // We only prevent the task option from showing if it's a DM and the other user is an Expensify default email if ( !Permissions.canUseTasks(this.props.betas) || - (lodashGet(this.props.report, 'participants', []).length === 1 && _.some(reportParticipants, (email) => _.contains(CONST.EXPENSIFY_EMAILS, email))) + (lodashGet(this.props.report, 'participantAccountIDs', []).length === 1 && _.some(reportParticipants, (accountID) => _.contains(CONST.EXPENSIFY_ACCOUNT_IDS, accountID))) ) { return []; } @@ -910,9 +910,9 @@ class ReportActionCompose extends React.Component { } render() { - const reportParticipants = _.without(lodashGet(this.props.report, 'participants', []), this.props.currentUserPersonalDetails.login); - const participantsWithoutExpensifyEmails = _.difference(reportParticipants, CONST.EXPENSIFY_EMAILS); - const reportRecipient = this.props.personalDetails[participantsWithoutExpensifyEmails[0]]; + const reportParticipants = _.without(lodashGet(this.props.report, 'participantAccountIDs', []), this.props.currentUserPersonalDetails.accountID); + const participantsWithoutExpensifyAccountIDs = _.difference(reportParticipants, CONST.EXPENSIFY_ACCOUNT_IDS); + const reportRecipient = this.props.personalDetails[participantsWithoutExpensifyAccountIDs[0]]; const shouldShowReportRecipientLocalTime = ReportUtils.canShowReportRecipientLocalTime(this.props.personalDetails, this.props.report, this.props.currentUserPersonalDetails.login) && !this.props.isComposerFullSize; @@ -1258,7 +1258,7 @@ export default compose( selector: EmojiUtils.getPreferredSkinToneIndex, }, personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, }, shouldShowComposeInput: { key: ONYXKEYS.SHOULD_SHOW_COMPOSE_INPUT, diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index e7c704fd8c8e..2949bd7bfe86 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -321,7 +321,7 @@ function ReportActionItem(props) { const hasReplies = numberOfThreadReplies > 0; const shouldDisplayThreadReplies = hasReplies && props.action.childCommenterCount && !ReportUtils.isThreadFirstChat(props.action, props.report.reportID); - const oldestFourEmails = lodashGet(props.action, 'childOldestFourEmails', '').split(','); + const oldestFourAccountIDs = lodashGet(props.action, 'childOldestFourAccountIDs', '').split(','); const draftMessageRightAlign = props.draftMessage ? styles.chatItemReactionsDraftRight : {}; return ( @@ -353,7 +353,7 @@ function ReportActionItem(props) { numberOfReplies={numberOfThreadReplies} mostRecentReply={`${props.action.childLastVisibleActionCreated}`} isHovered={hovered} - icons={ReportUtils.getIconsForParticipants(oldestFourEmails, props.personalDetails)} + icons={ReportUtils.getIconsForParticipants(oldestFourAccountIDs, props.personalDetails)} /> )} diff --git a/src/pages/home/report/ReportActionItemCreated.js b/src/pages/home/report/ReportActionItemCreated.js index a00e557ea613..5f8455d25495 100644 --- a/src/pages/home/report/ReportActionItemCreated.js +++ b/src/pages/home/report/ReportActionItemCreated.js @@ -89,7 +89,7 @@ export default compose( key: ({reportID}) => `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, }, personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, }, }), )(ReportActionItemCreated); diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js index eb5f2fa07434..0b2c56a275c8 100644 --- a/src/pages/home/report/ReportActionItemSingle.js +++ b/src/pages/home/report/ReportActionItemSingle.js @@ -67,8 +67,9 @@ const showUserDetails = (accountID) => { const ReportActionItemSingle = (props) => { const actorEmail = props.action.actorEmail.replace(CONST.REGEX.MERGED_ACCOUNT_PREFIX, ''); - const {accountID, avatar, displayName, pendingFields} = props.personalDetails[actorEmail] || {}; - const avatarSource = UserUtils.getAvatar(avatar, actorEmail); + const actorAccountID = props.action.actorAccountID; + const {avatar, displayName, pendingFields} = props.personalDetails[actorAccountID] || {}; + const avatarSource = UserUtils.getAvatar(avatar, actorAccountID); // Since the display name for a report action message is delivered with the report history as an array of fragments // we'll need to take the displayName from personal details and have it be in the same format for now. Eventually, @@ -88,7 +89,7 @@ const ReportActionItemSingle = (props) => { style={[styles.alignSelfStart, styles.mr3]} onPressIn={ControlSelection.block} onPressOut={ControlSelection.unblock} - onPress={() => showUserDetails(accountID)} + onPress={() => showUserDetails(actorAccountID)} > {props.shouldShowSubscriptAvatar ? ( @@ -118,7 +119,7 @@ const ReportActionItemSingle = (props) => { style={[styles.flexShrink1, styles.mr1]} onPressIn={ControlSelection.block} onPressOut={ControlSelection.unblock} - onPress={() => showUserDetails(accountID)} + onPress={() => showUserDetails(actorAccountID)} > {_.map(personArray, (fragment, index) => ( @@ -262,14 +265,15 @@ const chatReportSelector = (report) => const personalDetailsSelector = (personalDetails) => _.reduce( personalDetails, - (finalPersonalDetails, personalData, login) => { + (finalPersonalDetails, personalData, accountID) => { // It's OK to do param-reassignment in _.reduce() because we absolutely know the starting state of finalPersonalDetails // eslint-disable-next-line no-param-reassign - finalPersonalDetails[login] = { + finalPersonalDetails[accountID] = { + accountID, login: personalData.login, displayName: personalData.displayName, firstName: personalData.firstName, - avatar: UserUtils.getAvatar(personalData.avatar, personalData.login), + avatar: UserUtils.getAvatar(personalData.avatar, personalData.accountID), }; return finalPersonalDetails; }, @@ -315,7 +319,7 @@ export default compose( selector: chatReportSelector, }, personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, selector: personalDetailsSelector, }, priorityMode: { diff --git a/src/pages/iou/MoneyRequestModal.js b/src/pages/iou/MoneyRequestModal.js index 729a4ed27227..9eb264b5ecd3 100644 --- a/src/pages/iou/MoneyRequestModal.js +++ b/src/pages/iou/MoneyRequestModal.js @@ -249,20 +249,20 @@ const MoneyRequestModal = (props) => { const participant = selectedOptions[0]; if (paymentMethodType === CONST.IOU.PAYMENT_TYPE.ELSEWHERE) { - IOU.sendMoneyElsewhere(props.report, amount, currency, trimmedComment, props.currentUserPersonalDetails.login, participant); + IOU.sendMoneyElsewhere(props.report, amount, currency, trimmedComment, props.currentUserPersonalDetails.accountID, participant); return; } if (paymentMethodType === CONST.IOU.PAYMENT_TYPE.PAYPAL_ME) { - IOU.sendMoneyViaPaypal(props.report, amount, currency, trimmedComment, props.currentUserPersonalDetails.login, participant); + IOU.sendMoneyViaPaypal(props.report, amount, currency, trimmedComment, props.currentUserPersonalDetails.accountID, participant); return; } if (paymentMethodType === CONST.IOU.PAYMENT_TYPE.EXPENSIFY) { - IOU.sendMoneyWithWallet(props.report, amount, currency, trimmedComment, props.currentUserPersonalDetails.login, participant); + IOU.sendMoneyWithWallet(props.report, amount, currency, trimmedComment, props.currentUserPersonalDetails.accountID, participant); } }, - [amount, props.iou.comment, selectedOptions, props.currentUserPersonalDetails.login, props.iou.selectedCurrencyCode, props.report], + [amount, props.iou.comment, selectedOptions, props.currentUserPersonalDetails.accountID, props.iou.selectedCurrencyCode, props.report], ); /** @@ -276,17 +276,17 @@ const MoneyRequestModal = (props) => { // IOUs created from a group report will have a reportID param in the route. // Since the user is already viewing the report, we don't need to navigate them to the report if (props.hasMultipleParticipants && CONST.REGEX.NUMBER.test(reportID)) { - IOU.splitBill(selectedParticipants, props.currentUserPersonalDetails.login, amount, trimmedComment, props.iou.selectedCurrencyCode, reportID); + IOU.splitBill(selectedParticipants, props.currentUserPersonalDetails.login, props.currentUserPersonalDetails.accountID, amount, trimmedComment, props.iou.selectedCurrencyCode, reportID); return; } // If the request is created from the global create menu, we also navigate the user to the group report if (props.hasMultipleParticipants) { - IOU.splitBillAndOpenReport(selectedParticipants, props.currentUserPersonalDetails.login, amount, trimmedComment, props.iou.selectedCurrencyCode); + IOU.splitBillAndOpenReport(selectedParticipants, props.currentUserPersonalDetails.login, props.currentUserPersonalDetails.accountID, amount, trimmedComment, props.iou.selectedCurrencyCode); return; } - IOU.requestMoney(props.report, amount, props.iou.selectedCurrencyCode, props.currentUserPersonalDetails.login, selectedParticipants[0], trimmedComment); + IOU.requestMoney(props.report, amount, props.iou.selectedCurrencyCode, props.currentUserPersonalDetails.login, props.currentUserPersonalDetails.accountID, selectedParticipants[0], trimmedComment); }, [amount, props.iou.comment, props.currentUserPersonalDetails.login, props.hasMultipleParticipants, props.iou.selectedCurrencyCode, props.report, props.route], ); @@ -372,7 +372,7 @@ const MoneyRequestModal = (props) => { ReportScrollManager.scrollToBottom(); }} hasMultipleParticipants={props.hasMultipleParticipants} - participants={_.filter(selectedOptions, (email) => props.currentUserPersonalDetails.login !== email.login)} + participants={_.filter(selectedOptions, (option) => props.currentUserPersonalDetails.accountID !== option.accountID)} iouAmount={amount} iouType={props.iouType} // The participants can only be modified when the action is initiated from directly within a group chat and not the floating-action-button. @@ -411,7 +411,7 @@ export default compose( key: ONYXKEYS.IOU, }, personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, }, }), )(MoneyRequestModal); diff --git a/src/pages/iou/SplitBillDetailsPage.js b/src/pages/iou/SplitBillDetailsPage.js index d4e5cc85d3fb..9ce377c77267 100644 --- a/src/pages/iou/SplitBillDetailsPage.js +++ b/src/pages/iou/SplitBillDetailsPage.js @@ -64,10 +64,10 @@ function getReportID(route) { const SplitBillDetailsPage = (props) => { const reportAction = props.reportActions[`${props.route.params.reportActionID.toString()}`]; - const personalDetails = OptionsListUtils.getPersonalDetailsForLogins(reportAction.originalMessage.participants, props.personalDetails); + const personalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs(reportAction.originalMessage.participantAccountIDs, props.personalDetails); const participants = OptionsListUtils.getParticipantsOptions(reportAction.originalMessage, personalDetails); - const payeePersonalDetails = _.filter(participants, (participant) => participant.login === reportAction.actorEmail)[0]; - const participantsExcludingPayee = _.filter(participants, (participant) => participant.login !== reportAction.actorEmail); + const payeePersonalDetails = _.filter(participants, (participant) => participant.accountID === reportAction.actorAccountID)[0]; + const participantsExcludingPayee = _.filter(participants, (participant) => participant.accountID !== reportAction.actorAccountID); const splitAmount = parseInt(lodashGet(reportAction, 'originalMessage.amount', 0), 10); const splitComment = lodashGet(reportAction, 'originalMessage.comment'); const splitCurrency = lodashGet(reportAction, 'originalMessage.currency'); @@ -108,7 +108,7 @@ export default compose( withReportOrNotFound, withOnyx({ personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, }, reportActions: { key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getReportID(route)}`, diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js index 90613ef78b86..1849329a4f1d 100755 --- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js +++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js @@ -79,7 +79,7 @@ class MoneyRequestParticipantsSelector extends Component { this.props.betas, searchTerm, [], - CONST.EXPENSIFY_EMAILS, + CONST.EXPENSIFY_ACCOUNT_IDS, // If we are using this component in the "Request money" flow then we pass the includeOwnedWorkspaceChats argument so that the current user // sees the option to request money from their admin on their own Workspace Chat. @@ -175,7 +175,7 @@ export default compose( withLocalize, withOnyx({ personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, }, reports: { key: ONYXKEYS.COLLECTION.REPORT, diff --git a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSplitSelector.js b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSplitSelector.js index 28bdc2365622..2582bacd6506 100755 --- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSplitSelector.js +++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSplitSelector.js @@ -29,6 +29,7 @@ const propTypes = { participants: PropTypes.arrayOf( PropTypes.shape({ login: PropTypes.string.isRequired, + accountID: PropTypes.number.isRequired, alternateText: PropTypes.string, hasDraftComment: PropTypes.bool, icons: PropTypes.arrayOf(avatarPropTypes), @@ -73,7 +74,7 @@ class MoneyRequestParticipantsSplitSelector extends Component { props.betas, '', props.participants, - CONST.EXPENSIFY_EMAILS, + CONST.EXPENSIFY_ACCOUNT_IDS, ); this.state = { @@ -148,7 +149,7 @@ class MoneyRequestParticipantsSplitSelector extends Component { this.props.betas, searchTerm, this.props.participants, - CONST.EXPENSIFY_EMAILS, + CONST.EXPENSIFY_ACCOUNT_IDS, ); this.setState({ searchTerm, @@ -170,12 +171,12 @@ class MoneyRequestParticipantsSplitSelector extends Component { * @param {Object} option */ toggleOption(option) { - const isOptionInList = _.some(this.props.participants, (selectedOption) => selectedOption.login === option.login); + const isOptionInList = _.some(this.props.participants, (selectedOption) => selectedOption.accountID === option.accountID); let newSelectedOptions; if (isOptionInList) { - newSelectedOptions = _.reject(this.props.participants, (selectedOption) => selectedOption.login === option.login); + newSelectedOptions = _.reject(this.props.participants, (selectedOption) => selectedOption.accountID === option.accountID); } else { newSelectedOptions = [...this.props.participants, option]; } @@ -189,7 +190,7 @@ class MoneyRequestParticipantsSplitSelector extends Component { this.props.betas, isOptionInList ? prevState.searchTerm : '', newSelectedOptions, - CONST.EXPENSIFY_EMAILS, + CONST.EXPENSIFY_ACCOUNT_IDS, ); return { recentReports, @@ -241,7 +242,7 @@ export default compose( withLocalize, withOnyx({ personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, }, reports: { key: ONYXKEYS.COLLECTION.REPORT, diff --git a/src/pages/personalDetailsPropType.js b/src/pages/personalDetailsPropType.js index a86e42ae6fa8..e0f906f137d2 100644 --- a/src/pages/personalDetailsPropType.js +++ b/src/pages/personalDetailsPropType.js @@ -16,6 +16,9 @@ export default PropTypes.shape({ // Flag to set when Avatar uploading avatarUploading: PropTypes.bool, + // accountID of the current user from their personal details + accountID: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + // login of the current user from their personal details login: PropTypes.string, diff --git a/src/pages/reportPropTypes.js b/src/pages/reportPropTypes.js index bb7c197e8bf2..4b50909032a1 100644 --- a/src/pages/reportPropTypes.js +++ b/src/pages/reportPropTypes.js @@ -28,6 +28,9 @@ export default PropTypes.shape({ /** The email of the last message's actor */ lastActorEmail: PropTypes.string, + /** The accountID of the last message's actor */ + lastActorAccountID: PropTypes.number, + /** The text of the last message on the report */ lastMessageText: PropTypes.string, @@ -50,8 +53,14 @@ export default PropTypes.shape({ /** The email address of the report owner */ ownerEmail: PropTypes.string, + /** The accountID of the report owner */ + ownerAccountID: PropTypes.number, + /** List of primarylogins of participants of the report */ - participants: PropTypes.arrayOf(PropTypes.string), + participants: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])), + + /** List of accountIDs of participants of the report */ + participantAccountIDs: PropTypes.arrayOf(PropTypes.number), /** Linked policy's ID */ policyID: PropTypes.string, diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.js index f2c58e5aa2ac..83e8f2d8abde 100755 --- a/src/pages/settings/InitialSettingsPage.js +++ b/src/pages/settings/InitialSettingsPage.js @@ -99,8 +99,8 @@ const propTypes = { errorFields: PropTypes.objectOf(PropTypes.objectOf(PropTypes.string)), }), - /** List of policy members */ - policyMembers: PropTypes.objectOf(policyMemberPropType), + /** Members keyed by accountID for all policies */ + allPolicyMembers: PropTypes.objectOf(PropTypes.objectOf(policyMemberPropType)), ...withLocalizePropTypes, ...withCurrentUserPersonalDetailsPropTypes, @@ -118,7 +118,7 @@ const defaultProps = { bankAccountList: {}, cardList: {}, loginList: {}, - policyMembers: {}, + allPolicyMembers: {}, ...withCurrentUserPersonalDetailsDefaultProps, }; @@ -170,7 +170,7 @@ class InitialSettingsPage extends React.Component { !_.isEmpty(this.props.reimbursementAccount.errors) || _.chain(this.props.policies) .filter((policy) => policy && policy.type === CONST.POLICY.TYPE.FREE && policy.role === CONST.POLICY.ROLE.ADMIN) - .some((policy) => PolicyUtils.hasPolicyError(policy) || PolicyUtils.getPolicyBrickRoadIndicatorStatus(policy, this.props.policyMembers)) + .some((policy) => PolicyUtils.hasPolicyError(policy) || PolicyUtils.getPolicyBrickRoadIndicatorStatus(policy, this.props.allPolicyMembers)) .value() ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : null; @@ -393,8 +393,8 @@ export default compose( policies: { key: ONYXKEYS.COLLECTION.POLICY, }, - policyMembers: { - key: ONYXKEYS.COLLECTION.POLICY_MEMBER_LIST, + allPolicyMembers: { + key: ONYXKEYS.COLLECTION.POLICY_MEMBERS, }, userWallet: { key: ONYXKEYS.USER_WALLET, diff --git a/src/pages/tasks/NewTaskPage.js b/src/pages/tasks/NewTaskPage.js index e9ba781f046f..3ceb54892e65 100644 --- a/src/pages/tasks/NewTaskPage.js +++ b/src/pages/tasks/NewTaskPage.js @@ -77,7 +77,7 @@ const NewTaskPage = (props) => { // If we have an assignee, we want to set the assignee data // If there's an issue with the assignee chosen, we want to notify the user if (props.task.assignee) { - const assigneeDetails = lodashGet(OptionsListUtils.getPersonalDetailsForLogins([props.task.assignee], props.personalDetails), props.task.assignee); + const assigneeDetails = lodashGet(OptionsListUtils.getPersonalDetailsForAccountIDs([props.task.assigneeAccountID], props.personalDetails), props.task.assignee); if (!assigneeDetails) { return setErrorMessage(props.translate('newTaskPage.assigneeError')); } @@ -119,7 +119,7 @@ const NewTaskPage = (props) => { return; } - TaskUtils.createTaskAndNavigate(props.session.email, parentReport.reportID, props.task.title, props.task.description, props.task.assignee); + TaskUtils.createTaskAndNavigate(props.session.email, props.session.accountID, parentReport.reportID, props.task.title, props.task.description, props.task.assignee); } if (!Permissions.canUseTasks(props.betas)) { @@ -201,7 +201,7 @@ export default compose( key: ONYXKEYS.COLLECTION.REPORT, }, personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, }, session: { key: ONYXKEYS.SESSION, diff --git a/src/pages/tasks/TaskAssigneeSelectorModal.js b/src/pages/tasks/TaskAssigneeSelectorModal.js index 63a79c6ce1d1..5b434c8167f8 100644 --- a/src/pages/tasks/TaskAssigneeSelectorModal.js +++ b/src/pages/tasks/TaskAssigneeSelectorModal.js @@ -78,7 +78,7 @@ const TaskAssigneeSelectorModal = (props) => { const [filteredCurrentUserOption, setFilteredCurrentUserOption] = useState(null); useEffect(() => { - const results = OptionsListUtils.getNewChatOptions(props.reports, props.personalDetails, props.betas, '', [], CONST.EXPENSIFY_EMAILS, false); + const results = OptionsListUtils.getNewChatOptions(props.reports, props.personalDetails, props.betas, '', [], CONST.EXPENSIFY_ACCOUNT_IDS, false); setFilteredRecentReports(results.recentReports); setFilteredPersonalDetails(results.personalDetails); @@ -93,7 +93,7 @@ const TaskAssigneeSelectorModal = (props) => { props.betas, searchValue.trim(), [], - CONST.EXPENSIFY_EMAILS, + CONST.EXPENSIFY_ACCOUNT_IDS, false, ); @@ -170,10 +170,11 @@ const TaskAssigneeSelectorModal = (props) => { // Check to see if we're creating a new task // If there's no route params, we're creating a new task - if (!props.route.params && option.login) { + if (!props.route.params && option.accountID) { // Clear out the state value, set the assignee and navigate back to the NewTaskPage setSearchValue(''); - TaskUtils.setAssigneeValue(option.login, props.task.shareDestination, OptionsListUtils.isCurrentUser(option)); + // TODO: create optimistic accountID if we don't have an accountID? + TaskUtils.setAssigneeValue(option.accountID, props.task.shareDestination, OptionsListUtils.isCurrentUser(option)); return Navigation.goBack(); } @@ -181,9 +182,10 @@ const TaskAssigneeSelectorModal = (props) => { if (props.route.params.reportID && props.task.report.reportID === props.route.params.reportID) { // There was an issue where sometimes a new assignee didn't have a DM thread // This would cause the app to crash, so we need to make sure we have a DM thread - TaskUtils.setAssigneeValue(option.login, props.task.shareDestination, OptionsListUtils.isCurrentUser(option)); + // TODO: create optimistic accountID if we don't have an accountID? + TaskUtils.setAssigneeValue(option.accountID, props.task.shareDestination, OptionsListUtils.isCurrentUser(option)); // Pass through the selected assignee - TaskUtils.editTaskAndNavigate(props.task.report, props.session.email, '', '', option.login); + TaskUtils.editTaskAndNavigate(props.task.report, props.session.email, props.session.accountID, '', '', option.accountID); } }; @@ -229,7 +231,7 @@ export default compose( key: ONYXKEYS.COLLECTION.REPORT, }, personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, }, betas: { key: ONYXKEYS.BETAS, diff --git a/src/pages/tasks/TaskDescriptionPage.js b/src/pages/tasks/TaskDescriptionPage.js index 1c894886d291..bad08e76ab17 100644 --- a/src/pages/tasks/TaskDescriptionPage.js +++ b/src/pages/tasks/TaskDescriptionPage.js @@ -41,7 +41,7 @@ function TaskDescriptionPage(props) { (values) => { // Set the description of the report in the store and then call TaskUtils.editTaskReport // to update the description of the report on the server - TaskUtils.editTaskAndNavigate(props.task.report, props.session.email, '', values.description, ''); + TaskUtils.editTaskAndNavigate(props.task.report, props.session.email, props.session.accountID, '', values.description, ''); }, [props], ); diff --git a/src/pages/tasks/TaskShareDestinationSelectorModal.js b/src/pages/tasks/TaskShareDestinationSelectorModal.js index ed4fadd906d9..f41ccfd69521 100644 --- a/src/pages/tasks/TaskShareDestinationSelectorModal.js +++ b/src/pages/tasks/TaskShareDestinationSelectorModal.js @@ -182,7 +182,7 @@ export default compose( key: ONYXKEYS.COLLECTION.REPORT, }, personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, }, betas: { key: ONYXKEYS.BETAS, diff --git a/src/pages/tasks/TaskTitlePage.js b/src/pages/tasks/TaskTitlePage.js index 333c7135e46b..fb55acdaffd7 100644 --- a/src/pages/tasks/TaskTitlePage.js +++ b/src/pages/tasks/TaskTitlePage.js @@ -59,7 +59,7 @@ function TaskTitlePage(props) { // Set the description of the report in the store and then call TaskUtils.editTaskReport // to update the description of the report on the server - TaskUtils.editTaskAndNavigate(props.task.report, props.session.email, values.title, '', ''); + TaskUtils.editTaskAndNavigate(props.task.report, props.session.email, props.session.accountID, values.title, '', ''); }, [props], ); diff --git a/src/pages/workspace/WorkspaceInitialPage.js b/src/pages/workspace/WorkspaceInitialPage.js index 92d8e0fe0f3d..06e6b0a2231a 100644 --- a/src/pages/workspace/WorkspaceInitialPage.js +++ b/src/pages/workspace/WorkspaceInitialPage.js @@ -91,7 +91,7 @@ const WorkspaceInitialPage = (props) => { ); const policyName = lodashGet(policy, 'name', ''); - const hasMembersError = PolicyUtils.hasPolicyMemberError(props.policyMemberList); + const hasMembersError = PolicyUtils.hasPolicyMemberError(props.policyMembers); const hasGeneralSettingsError = !_.isEmpty(lodashGet(policy, 'errorFields.generalSettings', {})) || !_.isEmpty(lodashGet(policy, 'errorFields.avatar', {})); const hasCustomUnitsError = PolicyUtils.hasCustomUnitsError(policy); const menuItems = [ diff --git a/src/pages/workspace/WorkspaceInviteMessagePage.js b/src/pages/workspace/WorkspaceInviteMessagePage.js index eb53319ffd38..a95f2b6f84d4 100644 --- a/src/pages/workspace/WorkspaceInviteMessagePage.js +++ b/src/pages/workspace/WorkspaceInviteMessagePage.js @@ -44,7 +44,7 @@ const propTypes = { /** Beta features list */ betas: PropTypes.arrayOf(PropTypes.string), - invitedMembersDraft: PropTypes.arrayOf(PropTypes.string), + invitedEmailsToAccountIDsDraft: PropTypes.objectOf(PropTypes.number), /** URL Route params */ route: PropTypes.shape({ @@ -63,7 +63,7 @@ const defaultProps = { ...policyDefaultProps, personalDetails: {}, betas: [], - invitedMembersDraft: [], + invitedEmailsToAccountIDsDraft: {}, }; class WorkspaceInviteMessagePage extends React.Component { @@ -115,7 +115,7 @@ class WorkspaceInviteMessagePage extends React.Component { } sendInvitation() { - Policy.addMembersToWorkspace(this.props.invitedMembersDraft, this.state.welcomeNote, this.props.route.params.policyID, this.props.betas); + Policy.addMembersToWorkspace(this.props.invitedEmailsToAccountIDsDraft, this.state.welcomeNote, this.props.route.params.policyID, this.props.betas); Policy.setWorkspaceInviteMembersDraft(this.props.route.params.policyID, []); Navigation.navigate(ROUTES.getWorkspaceMembersRoute(this.props.route.params.policyID)); } @@ -131,7 +131,7 @@ class WorkspaceInviteMessagePage extends React.Component { validate() { const errorFields = {}; - if (_.isEmpty(this.props.invitedMembersDraft)) { + if (_.isEmpty(this.props.invitedEmailsToAccountIDsDraft)) { errorFields.welcomeMessage = this.props.translate('workspace.inviteMessage.inviteNoMembersError'); } return errorFields; @@ -178,7 +178,7 @@ class WorkspaceInviteMessagePage extends React.Component { @@ -216,12 +216,12 @@ export default compose( withPolicyAndFullscreenLoading, withOnyx({ personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, }, betas: { key: ONYXKEYS.BETAS, }, - invitedMembersDraft: { + invitedEmailsToAccountIDsDraft: { key: ({route}) => `${ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MEMBERS_DRAFT}${route.params.policyID.toString()}`, }, }), diff --git a/src/pages/workspace/WorkspaceInvitePage.js b/src/pages/workspace/WorkspaceInvitePage.js index 19a5e9375206..92d5543030bb 100644 --- a/src/pages/workspace/WorkspaceInvitePage.js +++ b/src/pages/workspace/WorkspaceInvitePage.js @@ -24,6 +24,7 @@ import {withNetwork} from '../../components/OnyxProvider'; import FullPageNotFoundView from '../../components/BlockingViews/FullPageNotFoundView'; import networkPropTypes from '../../components/networkPropTypes'; import ROUTES from '../../ROUTES'; +import * as PolicyUtils from '../../libs/PolicyUtils'; const personalDetailsPropTypes = PropTypes.shape({ /** The login of the person (either email or phone number) */ @@ -86,16 +87,15 @@ class WorkspaceInvitePage extends React.Component { componentDidMount() { this.clearErrors(); - - const clientPolicyMembers = _.keys(this.props.policyMemberList); - Policy.openWorkspaceInvitePage(this.props.route.params.policyID, clientPolicyMembers); + const policyMemberEmailsToAccountIDs = PolicyUtils.getClientPolicyMemberEmailsToAccountIDs(this.props.policyMembers, this.props.personalDetails); + Policy.openWorkspaceInvitePage(this.props.route.params.policyID, _.keys(policyMemberEmailsToAccountIDs)); } componentDidUpdate(prevProps) { if (!_.isEqual(prevProps.personalDetails, this.props.personalDetails)) { this.updateOptionsWithSearchTerm(this.props.searchTerm); } - if (!_.isEqual(prevProps.policyMemberList, this.props.policyMemberList)) { + if (!_.isEqual(prevProps.policyMembers, this.props.policyMembers)) { this.updateOptionsWithSearchTerm(this.state.searchTerm); } @@ -104,20 +104,17 @@ class WorkspaceInvitePage extends React.Component { return; } - const clientPolicyMembers = _.keys(this.props.policyMemberList); - Policy.openWorkspaceInvitePage(this.props.route.params.policyID, clientPolicyMembers); + const policyMemberEmailsToAccountIDs = PolicyUtils.getClientPolicyMemberEmailsToAccountIDs(this.props.policyMembers, this.props.personalDetails); + Policy.openWorkspaceInvitePage(this.props.route.params.policyID, _.keys(policyMemberEmailsToAccountIDs)); } getExcludedUsers() { - const policyMemberList = lodashGet(this.props, 'policyMemberList', {}); - const usersToExclude = _.filter( - _.keys(policyMemberList), - (policyMember) => - this.props.network.isOffline || - policyMemberList[policyMember].pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || - !_.isEmpty(policyMemberList[policyMember].errors), - ); - return [...CONST.EXPENSIFY_EMAILS, ...usersToExclude]; + const policyMemberEmailsToAccountIDs = PolicyUtils.getClientPolicyMemberEmailsToAccountIDs(this.props.policyMembers, this.props.personalDetails); + let usersToExclude = [...CONST.EXPENSIFY_EMAILS, ...(_.keys(policyMemberEmailsToAccountIDs))]; + if (!this.props.network.isOffline) { + usersToExclude = _.filter(this.props.policyMembers, (policyMember) => policyMember.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || !_.isEmpty(policyMember.errors)); + } + return usersToExclude; } /** @@ -230,7 +227,9 @@ class WorkspaceInvitePage extends React.Component { .compact() .uniq() .value(); - Policy.setWorkspaceInviteMembersDraft(this.props.route.params.policyID, filteredLogins); + const policyMemberEmailsToAccountIDs = PolicyUtils.getClientPolicyMemberEmailsToAccountIDs(this.props.policyMembers, this.props.personalDetails); + const invitedEmailsToAccountIDs = _.reduce(filteredLogins, (result, login) => ({...result, [login]: policyMemberEmailsToAccountIDs[login]}), {}); + Policy.setWorkspaceInviteMembersDraft(this.props.route.params.policyID, invitedEmailsToAccountIDs); Navigation.navigate(ROUTES.getWorkspaceInviteMessageRoute(this.props.route.params.policyID)); } @@ -318,7 +317,7 @@ export default compose( withNetwork(), withOnyx({ personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, }, betas: { key: ONYXKEYS.BETAS, diff --git a/src/pages/workspace/WorkspaceMembersPage.js b/src/pages/workspace/WorkspaceMembersPage.js index 19db6d28bb70..476652734d5a 100644 --- a/src/pages/workspace/WorkspaceMembersPage.js +++ b/src/pages/workspace/WorkspaceMembersPage.js @@ -35,6 +35,7 @@ import KeyboardDismissingFlatList from '../../components/KeyboardDismissingFlatL import withCurrentUserPersonalDetails from '../../components/withCurrentUserPersonalDetails'; import * as PolicyUtils from '../../libs/PolicyUtils'; import PressableWithFeedback from '../../components/Pressable/PressableWithFeedback'; +import Log from '../../libs/Log'; const propTypes = { /** The personal details of the person who is logged in */ @@ -98,9 +99,9 @@ class WorkspaceMembersPage extends React.Component { this.validate(); } - if (prevProps.policyMemberList !== this.props.policyMemberList) { + if (prevProps.policyMembers !== this.props.policyMembers) { this.setState((prevState) => ({ - selectedEmployees: _.intersection(prevState.selectedEmployees, _.keys(this.props.policyMemberList)), + selectedEmployees: _.intersection(prevState.selectedEmployees, _.keys(PolicyUtils.getClientPolicyMemberEmailsToAccountIDs(this.props.policyMembers, this.props.personalDetails))), })); } @@ -116,12 +117,7 @@ class WorkspaceMembersPage extends React.Component { * Get members for the current workspace */ getWorkspaceMembers() { - /** - * We filter clientMemberEmails to only pass members without errors - * Otherwise, the members with errors would immediately be removed before the user has a chance to read the error - */ - const clientMemberEmails = _.keys(_.pick(this.props.policyMemberList, (member) => _.isEmpty(member.errors))); - Policy.openWorkspaceMembersPage(this.props.route.params.policyID, clientMemberEmails); + Policy.openWorkspaceMembersPage(this.props.route.params.policyID, _.keys(PolicyUtils.getClientPolicyMemberEmailsToAccountIDs(this.props.policyMembers, this.props.personalDetails))); } /** @@ -184,7 +180,11 @@ class WorkspaceMembersPage extends React.Component { // Remove the admin from the list const membersToRemove = _.without(this.state.selectedEmployees, this.props.session.email); - Policy.removeMembers(membersToRemove, this.props.route.params.policyID); + + // It's a pain, but we need to map the emails back to accountIDs now so we can set optimistic data. TODO removeMembers using accountIDs only. + const emailsToAccountIDs = PolicyUtils.getClientPolicyMemberEmailsToAccountIDs(this.props.policyMembers, this.props.personalDetails); + const accountIDsToRemove = _.map(membersToRemove, (email) => emailsToAccountIDs[email]); + Policy.removeMembers(membersToRemove, accountIDsToRemove, this.props.route.params.policyID); this.setState({ selectedEmployees: [], isRemoveMembersConfirmModalVisible: false, @@ -379,16 +379,19 @@ class WorkspaceMembersPage extends React.Component { } render() { - const policyMemberList = lodashGet(this.props, 'policyMemberList', {}); const policyOwner = lodashGet(this.props.policy, 'owner'); const currentUserLogin = lodashGet(this.props.currentUserPersonalDetails, 'login'); const removableMembers = {}; let data = []; - _.each(policyMemberList, (policyMember, email) => { + _.each(this.props.policyMembers, (policyMember, accountID) => { if (this.isDeletedPolicyMember(policyMember)) { return; } - const details = lodashGet(this.props.personalDetails, email, {displayName: email, login: email}); + const details = this.props.personalDetails[accountID]; + if (!details) { + Log.hmmm(`[WorkspaceMembersPage] no personal details found for policy member with accountID: ${accountID}`); + return; + } data.push({ ...policyMember, ...details, @@ -513,7 +516,7 @@ export default compose( withNetwork(), withOnyx({ personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, }, session: { key: ONYXKEYS.SESSION, diff --git a/src/pages/workspace/WorkspacesListPage.js b/src/pages/workspace/WorkspacesListPage.js index bf362168ad99..d6c8cdb53a06 100755 --- a/src/pages/workspace/WorkspacesListPage.js +++ b/src/pages/workspace/WorkspacesListPage.js @@ -54,8 +54,8 @@ const propTypes = { /** Bank account attached to free plan */ reimbursementAccount: ReimbursementAccountProps.reimbursementAccountPropTypes, - /** List of policy members */ - policyMembers: PropTypes.objectOf(policyMemberPropType), + /** A collection of objects for all policies which key policy member objects by accountIDs */ + allPolicyMembers: PropTypes.objectOf(PropTypes.objectOf(policyMemberPropType)), /** The user's wallet account */ userWallet: PropTypes.shape({ @@ -71,7 +71,7 @@ const propTypes = { const defaultProps = { policies: {}, - policyMembers: {}, + allPolicyMembers: {}, reimbursementAccount: {}, userWallet: { currentBalance: 0, @@ -130,7 +130,7 @@ class WorkspacesListPage extends Component { action: () => Navigation.navigate(ROUTES.getWorkspaceInitialRoute(policy.id)), iconFill: themeColors.textLight, fallbackIcon: Expensicons.FallbackWorkspaceAvatar, - brickRoadIndicator: reimbursementAccountBrickRoadIndicator || PolicyUtils.getPolicyBrickRoadIndicatorStatus(policy, this.props.policyMembers), + brickRoadIndicator: reimbursementAccountBrickRoadIndicator || PolicyUtils.getPolicyBrickRoadIndicatorStatus(policy, this.props.allPolicyMembers), pendingAction: policy.pendingAction, errors: policy.errors, dismissError: () => dismissWorkspaceError(policy.id, policy.pendingAction), @@ -216,8 +216,8 @@ export default compose( policies: { key: ONYXKEYS.COLLECTION.POLICY, }, - policyMembers: { - key: ONYXKEYS.COLLECTION.POLICY_MEMBER_LIST, + allPolicyMembers: { + key: ONYXKEYS.COLLECTION.POLICY_MEMBERS, }, reimbursementAccount: { key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, diff --git a/src/pages/workspace/withPolicy.js b/src/pages/workspace/withPolicy.js index 4458cc45ece9..7520f58c8c5d 100644 --- a/src/pages/workspace/withPolicy.js +++ b/src/pages/workspace/withPolicy.js @@ -58,12 +58,12 @@ const policyPropTypes = { }), /** The employee list of this policy */ - policyMemberList: PropTypes.objectOf(policyMemberPropType), + policyMembers: PropTypes.objectOf(policyMemberPropType), }; const policyDefaultProps = { policy: {}, - policyMemberList: {}, + policyMembers: {}, }; /* @@ -117,8 +117,8 @@ export default function (WrappedComponent) { policy: { key: (props) => `${ONYXKEYS.COLLECTION.POLICY}${getPolicyIDFromRoute(props.route)}`, }, - policyMemberList: { - key: (props) => `${ONYXKEYS.COLLECTION.POLICY_MEMBER_LIST}${getPolicyIDFromRoute(props.route)}`, + policyMembers: { + key: (props) => `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${getPolicyIDFromRoute(props.route)}`, }, })(withPolicy); } diff --git a/src/styles/StyleUtils.js b/src/styles/StyleUtils.js index b7b81488d71f..2feed7b9b8d8 100644 --- a/src/styles/StyleUtils.js +++ b/src/styles/StyleUtils.js @@ -190,7 +190,7 @@ function getAvatarBorderStyle(size, type) { * @returns {Object} */ function getDefaultWorkspaceAvatarColor(workspaceName) { - const colorHash = UserUtils.hashLogin(workspaceName.trim(), workspaceColorOptions.length); + const colorHash = UserUtils.hashText(workspaceName.trim(), workspaceColorOptions.length); return workspaceColorOptions[colorHash]; } diff --git a/tests/actions/IOUTest.js b/tests/actions/IOUTest.js index 071ac2300f92..e44e34dfd8b4 100644 --- a/tests/actions/IOUTest.js +++ b/tests/actions/IOUTest.js @@ -11,9 +11,13 @@ import * as ReportActions from '../../src/libs/actions/ReportActions'; import * as Report from '../../src/libs/actions/Report'; const CARLOS_EMAIL = 'cmartins@expensifail.com'; +const CARLOS_ACCOUNT_ID = 1; const JULES_EMAIL = 'jules@expensifail.com'; +const JULES_ACCOUNT_ID = 2; const RORY_EMAIL = 'rory@expensifail.com'; +const RORY_ACCOUNT_ID = 3; const VIT_EMAIL = 'vit@expensifail.com'; +const VIT_ACCOUNT_ID = 4; describe('actions/IOU', () => { beforeAll(() => { @@ -36,7 +40,7 @@ describe('actions/IOU', () => { let iouAction; let transactionID; fetch.pause(); - IOU.requestMoney({}, amount, CONST.CURRENCY.USD, RORY_EMAIL, {login: CARLOS_EMAIL}, comment); + IOU.requestMoney({}, amount, CONST.CURRENCY.USD, RORY_EMAIL, RORY_ACCOUNT_ID, {login: CARLOS_EMAIL, accountID: CARLOS_ACCOUNT_ID}, comment); return waitForPromisesToResolve() .then( () => @@ -57,7 +61,7 @@ describe('actions/IOU', () => { iouReportID = iouReport.reportID; // They should be linked together - expect(chatReport.participants).toEqual([CARLOS_EMAIL]); + expect(chatReport.participantAccountIDs).toEqual([CARLOS_ACCOUNT_ID]); expect(chatReport.iouReportID).toBe(iouReport.reportID); expect(chatReport.hasOutstandingIOU).toBe(true); @@ -183,7 +187,7 @@ describe('actions/IOU', () => { reportID: 1234, type: CONST.REPORT.TYPE.CHAT, hasOutstandingIOU: false, - participants: [CARLOS_EMAIL], + participantAccountIDs: [CARLOS_ACCOUNT_ID], }; const createdAction = { reportActionID: NumberUtils.rand64(), @@ -201,7 +205,7 @@ describe('actions/IOU', () => { }), ) .then(() => { - IOU.requestMoney(chatReport, amount, CONST.CURRENCY.USD, RORY_EMAIL, {login: CARLOS_EMAIL}, comment); + IOU.requestMoney(chatReport, amount, CONST.CURRENCY.USD, RORY_EMAIL, RORY_ACCOUNT_ID, {login: CARLOS_EMAIL, accountID: CARLOS_ACCOUNT_ID}, comment); return waitForPromisesToResolve(); }) .then( @@ -343,7 +347,7 @@ describe('actions/IOU', () => { type: CONST.REPORT.TYPE.CHAT, hasOutstandingIOU: true, iouReportID, - participants: [CARLOS_EMAIL], + participantAccountIDs: [CARLOS_ACCOUNT_ID], }; const createdAction = { reportActionID: NumberUtils.rand64(), @@ -360,8 +364,8 @@ describe('actions/IOU', () => { reportID: iouReportID, chatReportID, type: CONST.REPORT.TYPE.IOU, - ownerEmail: RORY_EMAIL, - managerEmail: CARLOS_EMAIL, + ownerAccountID: RORY_ACCOUNT_ID, + managerID: CARLOS_ACCOUNT_ID, currency: CONST.CURRENCY.USD, total: existingTransaction.amount, }; @@ -369,6 +373,7 @@ describe('actions/IOU', () => { reportActionID: NumberUtils.rand64(), actionName: CONST.REPORT.ACTIONS.TYPE.IOU, actorEmail: RORY_EMAIL, + actorAccountID: RORY_ACCOUNT_ID, created: DateUtils.getDBTime(), originalMessage: { IOUReportID: iouReportID, @@ -377,6 +382,7 @@ describe('actions/IOU', () => { currency: CONST.CURRENCY.USD, type: CONST.IOU.REPORT_ACTION_TYPE.CREATE, participants: [RORY_EMAIL, CARLOS_EMAIL], + participantAccountIDs: [RORY_ACCOUNT_ID, CARLOS_ACCOUNT_ID], }, }; let newIOUAction; @@ -392,7 +398,7 @@ describe('actions/IOU', () => { ) .then(() => Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION}${existingTransaction.transactionID}`, existingTransaction)) .then(() => { - IOU.requestMoney(chatReport, amount, CONST.CURRENCY.USD, RORY_EMAIL, {login: CARLOS_EMAIL}, comment); + IOU.requestMoney(chatReport, amount, CONST.CURRENCY.USD, RORY_EMAIL, RORY_ACCOUNT_ID, {login: CARLOS_EMAIL, accountID: CARLOS_ACCOUNT_ID}, comment); return waitForPromisesToResolve(); }) .then( @@ -524,7 +530,7 @@ describe('actions/IOU', () => { let iouAction; let transactionID; fetch.pause(); - IOU.requestMoney({}, amount, CONST.CURRENCY.USD, RORY_EMAIL, {login: CARLOS_EMAIL}, comment); + IOU.requestMoney({}, amount, CONST.CURRENCY.USD, RORY_EMAIL, RORY_ACCOUNT_ID, {login: CARLOS_EMAIL, accountID: CARLOS_ACCOUNT_ID}, comment); return ( waitForPromisesToResolve() .then( @@ -547,7 +553,7 @@ describe('actions/IOU', () => { iouReportID = iouReport.reportID; // They should be linked together - expect(chatReport.participants).toEqual([CARLOS_EMAIL]); + expect(chatReport.participantAccountIDs).toEqual([CARLOS_ACCOUNT_ID]); expect(chatReport.iouReportID).toBe(iouReport.reportID); expect(chatReport.hasOutstandingIOU).toBe(true); @@ -770,6 +776,7 @@ describe('actions/IOU', () => { describe('split bill', () => { it('creates and updates new chats and IOUs as needed', () => { + jest.setTimeout(10 * 1000); /* * Given that: * - Rory and Carlos have chatted before @@ -784,6 +791,7 @@ describe('actions/IOU', () => { type: CONST.REPORT.TYPE.CHAT, hasOutstandingIOU: false, participants: [CARLOS_EMAIL], + participantAccountIDs: [CARLOS_ACCOUNT_ID], }; const carlosCreatedAction = { reportActionID: NumberUtils.rand64(), @@ -796,7 +804,7 @@ describe('actions/IOU', () => { type: CONST.REPORT.TYPE.CHAT, hasOutstandingIOU: true, iouReportID: julesIOUReportID, - participants: [JULES_EMAIL], + participantAccountIDs: [JULES_ACCOUNT_ID], }; const julesChatCreatedAction = { reportActionID: NumberUtils.rand64(), @@ -819,8 +827,8 @@ describe('actions/IOU', () => { reportID: julesIOUReportID, chatReportID: julesChatReport.reportID, type: CONST.REPORT.TYPE.IOU, - ownerEmail: RORY_EMAIL, - managerEmail: JULES_EMAIL, + ownerAccountID: RORY_ACCOUNT_ID, + managerID: JULES_ACCOUNT_ID, currency: CONST.CURRENCY.USD, total: julesExistingTransaction.amount, }; @@ -828,6 +836,7 @@ describe('actions/IOU', () => { reportActionID: NumberUtils.rand64(), actionName: CONST.REPORT.ACTIONS.TYPE.IOU, actorEmail: RORY_EMAIL, + actorAccountID: RORY_ACCOUNT_ID, created: DateUtils.getDBTime(), originalMessage: { IOUReportID: julesIOUReportID, @@ -836,6 +845,7 @@ describe('actions/IOU', () => { currency: CONST.CURRENCY.USD, type: CONST.IOU.REPORT_ACTION_TYPE.CREATE, participants: [RORY_EMAIL, JULES_EMAIL], + participantAccountIDs: [RORY_ACCOUNT_ID, JULES_ACCOUNT_ID], }, }; @@ -881,8 +891,12 @@ describe('actions/IOU', () => { // When we split a bill offline fetch.pause(); IOU.splitBill( - _.map([CARLOS_EMAIL, JULES_EMAIL, VIT_EMAIL], (email) => ({login: email})), + // TODO: Migrate after the backend accepts accountIDs + _.map([ + [CARLOS_EMAIL, CARLOS_ACCOUNT_ID], [JULES_EMAIL, JULES_ACCOUNT_ID], [VIT_EMAIL, VIT_ACCOUNT_ID] + ], ([email, accountID]) => ({login: email, accountID})), RORY_EMAIL, + RORY_ACCOUNT_ID, amount, comment, CONST.CURRENCY.USD, @@ -901,13 +915,15 @@ describe('actions/IOU', () => { // There should now be 7 reports expect(_.size(allReports)).toBe(7); + console.log('ALL REPORTS', allReports); + // 1. The chat report with Rory + Carlos carlosChatReport = _.find(allReports, (report) => report.reportID === carlosChatReport.reportID); expect(_.isEmpty(carlosChatReport)).toBe(false); expect(carlosChatReport.pendingFields).toBeFalsy(); // 2. The IOU report with Rory + Carlos (new) - carlosIOUReport = _.find(allReports, (report) => report.type === CONST.REPORT.TYPE.IOU && report.managerEmail === CARLOS_EMAIL); + carlosIOUReport = _.find(allReports, (report) => report.type === CONST.REPORT.TYPE.IOU && report.managerID === CARLOS_ACCOUNT_ID); expect(_.isEmpty(carlosIOUReport)).toBe(false); expect(carlosIOUReport.total).toBe(amount / 4); @@ -923,19 +939,19 @@ describe('actions/IOU', () => { expect(julesIOUReport.total).toBe(julesExistingTransaction.amount + amount / 4); // 5. The chat report with Rory + Vit (new) - vitChatReport = _.find(allReports, (report) => report.type === CONST.REPORT.TYPE.CHAT && _.isEqual(report.participants, [VIT_EMAIL])); + vitChatReport = _.find(allReports, (report) => report.type === CONST.REPORT.TYPE.CHAT && _.isEqual(report.participantAccountIDs, [VIT_ACCOUNT_ID])); expect(_.isEmpty(vitChatReport)).toBe(false); expect(vitChatReport.pendingFields).toStrictEqual({createChat: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}); // 6. The IOU report with Rory + Vit (new) - vitIOUReport = _.find(allReports, (report) => report.type === CONST.REPORT.TYPE.IOU && report.managerEmail === VIT_EMAIL); + vitIOUReport = _.find(allReports, (report) => report.type === CONST.REPORT.TYPE.IOU && report.managerID === VIT_ACCOUNT_ID); expect(_.isEmpty(vitIOUReport)).toBe(false); expect(vitIOUReport.total).toBe(amount / 4); // 7. The group chat with everyone groupChat = _.find( allReports, - (report) => report.type === CONST.REPORT.TYPE.CHAT && _.isEqual(report.participants, [CARLOS_EMAIL, JULES_EMAIL, VIT_EMAIL]), + (report) => report.type === CONST.REPORT.TYPE.CHAT && _.isEqual(report.participantAccountIDs, [CARLOS_ACCOUNT_ID, JULES_ACCOUNT_ID, VIT_ACCOUNT_ID]), ); expect(_.isEmpty(groupChat)).toBe(false); expect(groupChat.pendingFields).toStrictEqual({createChat: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}); @@ -1149,7 +1165,7 @@ describe('actions/IOU', () => { let createIOUAction; let payIOUAction; let transaction; - IOU.requestMoney({}, amount, CONST.CURRENCY.USD, RORY_EMAIL, {login: CARLOS_EMAIL}, comment); + IOU.requestMoney({}, amount, CONST.CURRENCY.USD, RORY_EMAIL, RORY_ACCOUNT_ID, {login: CARLOS_EMAIL, accountID: CARLOS_ACCOUNT_ID}, comment); return waitForPromisesToResolve() .then( () => diff --git a/tests/perf-test/SidebarLinks.perf-test.js b/tests/perf-test/SidebarLinks.perf-test.js index eb2a243d1c06..4600f42bfd1d 100644 --- a/tests/perf-test/SidebarLinks.perf-test.js +++ b/tests/perf-test/SidebarLinks.perf-test.js @@ -49,7 +49,7 @@ test('simple Sidebar render with hundred of reports', () => { .then(() => Onyx.multiSet({ [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, ...mockOnyxReports, }), ) diff --git a/tests/ui/UnreadIndicatorsTest.js b/tests/ui/UnreadIndicatorsTest.js index aac173d7e1e4..c12b03c6ae18 100644 --- a/tests/ui/UnreadIndicatorsTest.js +++ b/tests/ui/UnreadIndicatorsTest.js @@ -153,6 +153,7 @@ function signInAndGetAppWithUnreadChat() { lastVisibleActionCreated: reportAction9CreatedDate, lastMessageText: 'Test', participants: [USER_B_EMAIL], + participantAccountIDs: [USER_B_ACCOUNT_ID], type: CONST.REPORT.TYPE.CHAT, }); const createdReportActionID = NumberUtils.rand64(); @@ -185,8 +186,8 @@ function signInAndGetAppWithUnreadChat() { 8: TestHelper.buildTestReportComment(USER_B_EMAIL, MOMENT_TEN_MINUTES_AGO.clone().add(80, 'seconds').format(MOMENT_FORMAT), USER_B_ACCOUNT_ID, '8'), 9: TestHelper.buildTestReportComment(USER_B_EMAIL, reportAction9CreatedDate, USER_B_ACCOUNT_ID, '9'), }); - Onyx.merge(ONYXKEYS.PERSONAL_DETAILS, { - [USER_B_EMAIL]: TestHelper.buildPersonalDetails(USER_B_EMAIL, USER_B_ACCOUNT_ID, 'B'), + Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, { + [USER_B_ACCOUNT_ID]: TestHelper.buildPersonalDetails(USER_B_EMAIL, USER_B_ACCOUNT_ID, 'B'), }); // We manually setting the sidebar as loaded since the onLayout event does not fire in tests @@ -305,6 +306,7 @@ describe('Unread Indicators', () => { lastVisibleActionCreated: DateUtils.getDBTime(NEW_REPORT_FIST_MESSAGE_CREATED_MOMENT.utc().valueOf()), lastMessageText: 'Comment 1', participants: [USER_C_EMAIL], + participantAccountIDs: [USER_C_ACCOUNT_ID], }, }, { @@ -331,9 +333,9 @@ describe('Unread Indicators', () => { }, { onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, value: { - [USER_C_EMAIL]: TestHelper.buildPersonalDetails(USER_C_EMAIL, USER_C_ACCOUNT_ID, 'C'), + [USER_C_ACCOUNT_ID]: TestHelper.buildPersonalDetails(USER_C_EMAIL, USER_C_ACCOUNT_ID, 'C'), }, }, ]); diff --git a/tests/unit/IOUUtilsTest.js b/tests/unit/IOUUtilsTest.js index 2f6901ca47a2..d080a1e900c9 100644 --- a/tests/unit/IOUUtilsTest.js +++ b/tests/unit/IOUUtilsTest.js @@ -10,7 +10,9 @@ import currencyList from './currencyList.json'; let iouReport; let reportActions; const ownerEmail = 'owner@iou.com'; +const ownerAccountID = 5; const managerEmail = 'manager@iou.com'; +const managerID = 10; function createIOUReportAction(type, amount, currency, isOffline = false, IOUTransactionID = NumberUtils.rand64()) { const moneyRequestAction = ReportUtils.buildOptimisticIOUReportAction(type, amount, currency, 'Test comment', [managerEmail], IOUTransactionID, '', iouReport.reportID); @@ -52,7 +54,7 @@ describe('IOUUtils', () => { const amount = 1000; const currency = 'USD'; - iouReport = ReportUtils.buildOptimisticIOUReport(ownerEmail, managerEmail, amount, chatReportID, currency); + iouReport = ReportUtils.buildOptimisticIOUReport(ownerEmail, ownerAccountID, managerID, amount, chatReportID, currency); // The starting point of all tests is the IOUReport containing a single non-pending transaction in USD // All requests in the tests are assumed to be online, unless isOffline is specified diff --git a/tests/unit/OptionsListUtilsTest.js b/tests/unit/OptionsListUtilsTest.js index 7e6fb4146ca6..75c7927888eb 100644 --- a/tests/unit/OptionsListUtilsTest.js +++ b/tests/unit/OptionsListUtilsTest.js @@ -14,6 +14,7 @@ describe('OptionsListUtils', () => { isPinned: false, reportID: 1, participants: ['tonystark@expensify.com', 'reedrichards@expensify.com'], + participantAccountIDs: [2, 1], reportName: 'Iron Man, Mister Fantastic', hasDraft: true, }, @@ -23,6 +24,7 @@ describe('OptionsListUtils', () => { isPinned: false, reportID: 2, participants: ['peterparker@expensify.com'], + participantAccountIDs: [3], reportName: 'Spider-Man', }, @@ -33,6 +35,7 @@ describe('OptionsListUtils', () => { isPinned: true, reportID: 3, participants: ['reedrichards@expensify.com'], + participantAccountIDs: [1], reportName: 'Mister Fantastic', }, 4: { @@ -41,6 +44,7 @@ describe('OptionsListUtils', () => { isPinned: false, reportID: 4, participants: ['tchalla@expensify.com'], + participantAccountIDs: [4], reportName: 'Black Panther', }, 5: { @@ -49,6 +53,7 @@ describe('OptionsListUtils', () => { isPinned: false, reportID: 5, participants: ['suestorm@expensify.com'], + participantAccountIDs: [5], reportName: 'Invisible Woman', }, 6: { @@ -57,6 +62,7 @@ describe('OptionsListUtils', () => { isPinned: false, reportID: 6, participants: ['thor@expensify.com'], + participantAccountIDs: [6], reportName: 'Thor', }, @@ -67,6 +73,7 @@ describe('OptionsListUtils', () => { isPinned: false, reportID: 7, participants: ['steverogers@expensify.com'], + participantAccountIDs: [7], reportName: 'Captain America', }, @@ -77,6 +84,7 @@ describe('OptionsListUtils', () => { isPinned: false, reportID: 8, participants: ['galactus_herald@expensify.com'], + participantAccountIDs: [12], reportName: 'Silver Surfer', }, @@ -87,6 +95,7 @@ describe('OptionsListUtils', () => { isPinned: false, reportID: 9, participants: ['mistersinister@marauders.com'], + participantAccountIDs: [8], reportName: 'Mister Sinister', iouReportID: 100, hasOutstandingIOU: true, @@ -99,6 +108,7 @@ describe('OptionsListUtils', () => { reportID: 10, isPinned: false, participants: ['tonystark@expensify.com', 'steverogers@expensify.com'], + participantAccountIDs: [2, 7], reportName: '', oldPolicyName: "SHIELD's workspace", chatType: CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT, @@ -109,45 +119,55 @@ describe('OptionsListUtils', () => { // And a set of personalDetails some with existing reports and some without const PERSONAL_DETAILS = { // These exist in our reports - 'reedrichards@expensify.com': { + 1: { + accountID: 1, displayName: 'Mister Fantastic', login: 'reedrichards@expensify.com', }, - 'tonystark@expensify.com': { + 2: { + accountID: 2, displayName: 'Iron Man', login: 'tonystark@expensify.com', }, - 'peterparker@expensify.com': { + 3: { + accountID: 3, displayName: 'Spider-Man', login: 'peterparker@expensify.com', }, - 'tchalla@expensify.com': { + 4: { + accountID: 4, displayName: 'Black Panther', login: 'tchalla@expensify.com', }, - 'suestorm@expensify.com': { + 5: { + accountID: 5, displayName: 'Invisible Woman', login: 'suestorm@expensify.com', }, - 'thor@expensify.com': { + 6: { + accountID: 6, displayName: 'Thor', login: 'thor@expensify.com', }, - 'steverogers@expensify.com': { + 7: { + accountID: 7, displayName: 'Captain America', login: 'steverogers@expensify.com', }, - 'mistersinister@marauders.com': { + 8: { + accountID: 8, displayName: 'Mr Sinister', login: 'mistersinister@marauders.com', }, // These do not exist in reports at all - 'natasharomanoff@expensify.com': { + 9: { + accountID: 9, displayName: 'Black Widow', login: 'natasharomanoff@expensify.com', }, - 'brucebanner@expensify.com': { + 10: { + accountID: 10, displayName: 'The Incredible Hulk', login: 'brucebanner@expensify.com', }, @@ -161,7 +181,9 @@ describe('OptionsListUtils', () => { lastVisibleActionCreated: '2022-11-22 03:26:02.022', isPinned: false, reportID: 11, + // NOTE: possibly remove 'participants' field in the future participants: ['concierge@expensify.com'], + participantAccountIDs: [999], reportName: 'Concierge', }, }; @@ -174,6 +196,7 @@ describe('OptionsListUtils', () => { isPinned: false, reportID: 12, participants: ['chronos@expensify.com'], + participantAccountIDs: [1000], reportName: 'Chronos', }, }; @@ -186,6 +209,7 @@ describe('OptionsListUtils', () => { isPinned: false, reportID: 13, participants: ['receipts@expensify.com'], + participantAccountIDs: [1001], reportName: 'Receipts', }, }; @@ -198,6 +222,7 @@ describe('OptionsListUtils', () => { isPinned: false, reportID: 14, participants: ['reedrichards@expensify.com', 'brucebanner@expensify.com', 'peterparker@expensify.com'], + participantAccountIDs: [1, 10, 3], reportName: '', oldPolicyName: 'Avengers Room', isArchivedRoom: false, @@ -209,7 +234,8 @@ describe('OptionsListUtils', () => { const PERSONAL_DETAILS_WITH_CONCIERGE = { ...PERSONAL_DETAILS, - 'concierge@expensify.com': { + 999: { + accountID: 999, displayName: 'Concierge', login: 'concierge@expensify.com', }, @@ -218,7 +244,8 @@ describe('OptionsListUtils', () => { const PERSONAL_DETAILS_WITH_CHRONOS = { ...PERSONAL_DETAILS, - 'chronos@expensify.com': { + 1000: { + accountID: 1000, displayName: 'Chronos', login: 'chronos@expensify.com', }, @@ -227,7 +254,8 @@ describe('OptionsListUtils', () => { const PERSONAL_DETAILS_WITH_RECEIPTS = { ...PERSONAL_DETAILS, - 'receipts@expensify.com': { + 1001: { + accountID: 1001, displayName: 'Receipts', login: 'receipts@expensify.com', }, @@ -236,7 +264,8 @@ describe('OptionsListUtils', () => { const PERSONAL_DETAILS_WITH_PERIODS = { ...PERSONAL_DETAILS, - 'barry.allen@expensify.com': { + 1002: { + accountID: 1002, displayName: 'The Flash', login: 'barry.allen@expensify.com', }, @@ -252,16 +281,17 @@ describe('OptionsListUtils', () => { Onyx.init({ keys: ONYXKEYS, initialKeyStates: { - [ONYXKEYS.SESSION]: {email: 'tonystark@expensify.com'}, + [ONYXKEYS.SESSION]: {accountID: 2, email: 'tonystark@expensify.com'}, [`${ONYXKEYS.COLLECTION.REPORT}100`]: { ownerEmail: 'mistersinister@marauders.com', + ownerAccountID: 8, total: '1000', }, [`${ONYXKEYS.COLLECTION.POLICY}${POLICY.policyID}`]: POLICY, }, }); Onyx.registerLogger(() => {}); - return waitForPromisesToResolve().then(() => Onyx.set(ONYXKEYS.PERSONAL_DETAILS, PERSONAL_DETAILS)); + return waitForPromisesToResolve().then(() => Onyx.set(ONYXKEYS.PERSONAL_DETAILS_LIST, PERSONAL_DETAILS)); }); it('getSearchOptions()', () => { @@ -290,10 +320,10 @@ describe('OptionsListUtils', () => { expect(results.recentReports[1].text).toBe('Mister Fantastic'); return waitForPromisesToResolve() - .then(() => Onyx.set(ONYXKEYS.PERSONAL_DETAILS, PERSONAL_DETAILS_WITH_PERIODS)) + .then(() => Onyx.set(ONYXKEYS.PERSONAL_DETAILS_LIST, PERSONAL_DETAILS_WITH_PERIODS)) .then(() => { // When we filter again but provide a searchValue that should match with periods - results = OptionsListUtils.getSearchOptions(REPORTS, PERSONAL_DETAILS_WITH_PERIODS, 'barryallen@expensify.com'); + results = OptionsListUtils.getSearchOptions(REPORTS, PERSONAL_DETAILS_WITH_PERIODS, 'barry.allen@expensify.com'); // Then we expect to have the personal detail with period filtered expect(results.recentReports.length).toBe(1); diff --git a/tests/unit/ReportUtilsTest.js b/tests/unit/ReportUtilsTest.js index f0f4cc85b8f2..1fb9f4f3fc64 100644 --- a/tests/unit/ReportUtilsTest.js +++ b/tests/unit/ReportUtilsTest.js @@ -10,23 +10,28 @@ import * as LHNTestUtils from '../utils/LHNTestUtils'; jest.mock('../../src/libs/Permissions'); const currentUserEmail = 'bjorn@vikings.net'; +const currentUserAccountID = 5; const participantsPersonalDetails = { - 'ragnar@vikings.net': { + 1: { + accountID: 1, displayName: 'Ragnar Lothbrok', firstName: 'Ragnar', login: 'ragnar@vikings.net', }, - 'floki@vikings.net': { + 2: { + accountID: 2, login: 'floki@vikings.net', displayName: 'floki@vikings.net', }, - 'lagertha@vikings.net': { + 3: { + accountID: 3, displayName: 'Lagertha Lothbrok', firstName: 'Lagertha', login: 'lagertha@vikings.net', pronouns: 'She/her', }, - '+18332403627@expensify.sms': { + 4: { + accountID: 4, login: '+18332403627@expensify.sms', displayName: '(833) 240-3627', }, @@ -41,8 +46,8 @@ Onyx.init({keys: ONYXKEYS}); describe('ReportUtils', () => { beforeAll(() => { Onyx.multiSet({ - [ONYXKEYS.PERSONAL_DETAILS]: participantsPersonalDetails, - [ONYXKEYS.SESSION]: {email: currentUserEmail}, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: participantsPersonalDetails, + [ONYXKEYS.SESSION]: {email: currentUserEmail, accountID: currentUserAccountID}, [ONYXKEYS.COUNTRY_CODE]: 1, [`${ONYXKEYS.COLLECTION.POLICY}${policy.policyID}`]: policy, }); @@ -108,6 +113,7 @@ describe('ReportUtils', () => { expect( ReportUtils.getReportName({ participants: [currentUserEmail, 'ragnar@vikings.net'], + participantAccountIDs: [currentUserAccountID, 1], }), ).toBe('Ragnar Lothbrok'); }); @@ -116,6 +122,7 @@ describe('ReportUtils', () => { expect( ReportUtils.getReportName({ participants: [currentUserEmail, 'floki@vikings.net'], + participantAccountIDs: [currentUserAccountID, 2], }), ).toBe('floki@vikings.net'); }); @@ -124,6 +131,7 @@ describe('ReportUtils', () => { expect( ReportUtils.getReportName({ participants: [currentUserEmail, '+18332403627@expensify.sms'], + participantAccountIDs: [currentUserAccountID, 4], }), ).toBe('(833) 240-3627'); }); @@ -133,6 +141,7 @@ describe('ReportUtils', () => { expect( ReportUtils.getReportName({ participants: [currentUserEmail, 'ragnar@vikings.net', 'floki@vikings.net', 'lagertha@vikings.net', '+18332403627@expensify.sms'], + participantAccountIDs: [currentUserAccountID, 1, 2, 3, 4], }), ).toBe('Ragnar, floki@vikings.net, Lagertha, (833) 240-3627'); }); @@ -192,6 +201,7 @@ describe('ReportUtils', () => { policyID: policy.policyID, isOwnPolicyExpenseChat: true, ownerEmail: 'ragnar@vikings.net', + ownerAccountID: 1, }), ).toBe('Vikings Policy'); }); @@ -203,6 +213,7 @@ describe('ReportUtils', () => { policyID: policy.policyID, isOwnPolicyExpenseChat: false, ownerEmail: 'ragnar@vikings.net', + ownerAccountID: 1, }), ).toBe('Ragnar Lothbrok'); }); @@ -212,6 +223,7 @@ describe('ReportUtils', () => { const baseArchivedPolicyExpenseChat = { chatType: CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT, ownerEmail: 'ragnar@vikings.net', + ownerAccountID: 1, policyID: policy.policyID, oldPolicyName: policy.name, statusNum: CONST.REPORT.STATUS.CLOSED, @@ -268,7 +280,7 @@ describe('ReportUtils', () => { iouReportID: '1', }; const iouReports = {}; - expect(ReportUtils.hasOutstandingIOU(report, undefined, iouReports)).toBe(false); + expect(ReportUtils.hasOutstandingIOU(report, iouReports)).toBe(false); }); it('returns false when the matched IOU report does not have an owner email', () => { const report = { @@ -280,7 +292,7 @@ describe('ReportUtils', () => { reportID: '1', }, }; - expect(ReportUtils.hasOutstandingIOU(report, undefined, iouReports)).toBe(false); + expect(ReportUtils.hasOutstandingIOU(report, iouReports)).toBe(false); }); it('returns false when the matched IOU report does not have an owner email', () => { const report = { @@ -290,10 +302,10 @@ describe('ReportUtils', () => { const iouReports = { report_1: { reportID: '1', - ownerEmail: 'a@a.com', + ownerAccountID: 99, }, }; - expect(ReportUtils.hasOutstandingIOU(report, 'b@b.com', iouReports)).toBe(false); + expect(ReportUtils.hasOutstandingIOU(report, iouReports)).toBe(false); }); it('returns true when the report has an oustanding IOU', () => { const report = { @@ -304,10 +316,10 @@ describe('ReportUtils', () => { const iouReports = { report_1: { reportID: '1', - ownerEmail: 'a@a.com', + ownerAccountID: 99, }, }; - expect(ReportUtils.hasOutstandingIOU(report, 'b@b.com', iouReports)).toBe(true); + expect(ReportUtils.hasOutstandingIOU(report, iouReports)).toBe(true); }); it('returns false when the report has no oustanding IOU', () => { const report = { @@ -318,10 +330,10 @@ describe('ReportUtils', () => { const iouReports = { report_1: { reportID: '1', - ownerEmail: 'a@a.com', + ownerAccountID: 99, }, }; - expect(ReportUtils.hasOutstandingIOU(report, 'b@b.com', iouReports)).toBe(false); + expect(ReportUtils.hasOutstandingIOU(report, iouReports)).toBe(false); }); }); @@ -329,8 +341,9 @@ describe('ReportUtils', () => { const participants = _.keys(participantsPersonalDetails); beforeAll(() => { - Onyx.merge(ONYXKEYS.PERSONAL_DETAILS, { - [currentUserEmail]: { + Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, { + [currentUserAccountID]: { + accountID: currentUserAccountID, login: currentUserEmail, }, }); @@ -340,20 +353,20 @@ describe('ReportUtils', () => { describe('return empty iou options if', () => { it('participants contains excluded iou emails', () => { - const allEmpty = _.every(CONST.EXPENSIFY_EMAILS, (email) => { - const moneyRequestOptions = ReportUtils.getMoneyRequestOptions({}, [currentUserEmail, email], [CONST.BETAS.IOU]); + const allEmpty = _.every(CONST.EXPENSIFY_ACCOUNT_IDS, (accountID) => { + const moneyRequestOptions = ReportUtils.getMoneyRequestOptions({}, [currentUserAccountID, accountID], [CONST.BETAS.IOU]); return moneyRequestOptions.length === 0; }); expect(allEmpty).toBe(true); }); it('no participants except self', () => { - const moneyRequestOptions = ReportUtils.getMoneyRequestOptions({}, [currentUserEmail], [CONST.BETAS.IOU]); + const moneyRequestOptions = ReportUtils.getMoneyRequestOptions({}, [currentUserAccountID], [CONST.BETAS.IOU]); expect(moneyRequestOptions.length).toBe(0); }); it('no iou permission', () => { - const moneyRequestOptions = ReportUtils.getMoneyRequestOptions({}, [currentUserEmail, participants], []); + const moneyRequestOptions = ReportUtils.getMoneyRequestOptions({}, [currentUserAccountID, ...participants], []); expect(moneyRequestOptions.length).toBe(0); }); }); @@ -367,7 +380,7 @@ describe('ReportUtils', () => { ...LHNTestUtils.getFakeReport(), chatType, }; - const moneyRequestOptions = ReportUtils.getMoneyRequestOptions(report, [currentUserEmail, participants[0]], [CONST.BETAS.IOU]); + const moneyRequestOptions = ReportUtils.getMoneyRequestOptions(report, [currentUserAccountID, participants[0]], [CONST.BETAS.IOU]); return moneyRequestOptions.length === 1 && moneyRequestOptions.includes(CONST.IOU.MONEY_REQUEST_TYPE.SPLIT); }, ); @@ -375,33 +388,33 @@ describe('ReportUtils', () => { }); it('has multiple participants exclude self', () => { - const moneyRequestOptions = ReportUtils.getMoneyRequestOptions({}, [currentUserEmail, ...participants], [CONST.BETAS.IOU]); + const moneyRequestOptions = ReportUtils.getMoneyRequestOptions({}, [currentUserAccountID, ...participants], [CONST.BETAS.IOU]); expect(moneyRequestOptions.length).toBe(1); expect(moneyRequestOptions.includes(CONST.IOU.MONEY_REQUEST_TYPE.SPLIT)).toBe(true); }); - }); - describe('return only iou request option if', () => { it(' does not have iou send permission', () => { - const moneyRequestOptions = ReportUtils.getMoneyRequestOptions({}, [currentUserEmail, participants], [CONST.BETAS.IOU]); + const moneyRequestOptions = ReportUtils.getMoneyRequestOptions({}, [currentUserAccountID, ...participants], [CONST.BETAS.IOU]); expect(moneyRequestOptions.length).toBe(1); - expect(moneyRequestOptions.includes(CONST.IOU.MONEY_REQUEST_TYPE.REQUEST)).toBe(true); + expect(moneyRequestOptions.includes(CONST.IOU.MONEY_REQUEST_TYPE.SPLIT)).toBe(true); }); + }); + describe('return only iou request option if', () => { it('a policy expense chat', () => { const report = { ...LHNTestUtils.getFakeReport(), chatType: CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT, isOwnPolicyExpenseChat: true, }; - const moneyRequestOptions = ReportUtils.getMoneyRequestOptions(report, [currentUserEmail, participants], [CONST.BETAS.IOU, CONST.BETAS.IOU_SEND]); + const moneyRequestOptions = ReportUtils.getMoneyRequestOptions(report, [currentUserAccountID, ...participants], [CONST.BETAS.IOU, CONST.BETAS.IOU_SEND]); expect(moneyRequestOptions.length).toBe(1); expect(moneyRequestOptions.includes(CONST.IOU.MONEY_REQUEST_TYPE.REQUEST)).toBe(true); }); }); it('return both iou send and request money in DM', () => { - const moneyRequestOptions = ReportUtils.getMoneyRequestOptions({type: 'chat'}, [currentUserEmail, participants[0]], [CONST.BETAS.IOU, CONST.BETAS.IOU_SEND]); + const moneyRequestOptions = ReportUtils.getMoneyRequestOptions({type: 'chat'}, [currentUserAccountID, participants[0]], [CONST.BETAS.IOU, CONST.BETAS.IOU_SEND]); expect(moneyRequestOptions.length).toBe(2); expect(moneyRequestOptions.includes(CONST.IOU.MONEY_REQUEST_TYPE.REQUEST)).toBe(true); expect(moneyRequestOptions.includes(CONST.IOU.MONEY_REQUEST_TYPE.SEND)).toBe(true); diff --git a/tests/unit/SidebarFilterTest.js b/tests/unit/SidebarFilterTest.js index 0c6bc75f5e68..927381177223 100644 --- a/tests/unit/SidebarFilterTest.js +++ b/tests/unit/SidebarFilterTest.js @@ -11,7 +11,7 @@ import * as Localize from '../../src/libs/Localize'; jest.mock('../../src/libs/Permissions'); const ONYXKEYS = { - PERSONAL_DETAILS: 'personalDetails', + PERSONAL_DETAILS_LIST: 'personalDetailsList', NVP_PRIORITY_MODE: 'nvp_priorityMode', SESSION: 'session', BETAS: 'betas', @@ -81,7 +81,7 @@ describe('Sidebar', () => { .then(() => Onyx.multiSet({ [ONYXKEYS.BETAS]: [], - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, }), ) @@ -125,7 +125,7 @@ describe('Sidebar', () => { .then(() => Onyx.multiSet({ [ONYXKEYS.BETAS]: [], - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, }), ) @@ -163,11 +163,11 @@ describe('Sidebar', () => { chatType: CONST.REPORT.CHAT_TYPE.POLICY_ADMINS, }; const report2 = { - ...LHNTestUtils.getFakeReport(['email3@test.com', 'email4@test.com']), + ...LHNTestUtils.getFakeReport([3, 4]), chatType: CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE, }; const report3 = { - ...LHNTestUtils.getFakeReport(['email5@test.com', 'email6@test.com']), + ...LHNTestUtils.getFakeReport([5, 6]), chatType: CONST.REPORT.CHAT_TYPE.DOMAIN_ALL, }; @@ -177,7 +177,7 @@ describe('Sidebar', () => { .then(() => Onyx.multiSet({ [ONYXKEYS.BETAS]: [], - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, @@ -228,7 +228,7 @@ describe('Sidebar', () => { .then(() => Onyx.multiSet({ [ONYXKEYS.BETAS]: [], - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, [`${ONYXKEYS.COLLECTION.POLICY}${policy.policyID}`]: policy, }), @@ -255,7 +255,7 @@ describe('Sidebar', () => { describe('all combinations of isArchived, isUserCreatedPolicyRoom, hasAddWorkspaceError, isUnread, isPinned, hasDraft', () => { // Given a report that is the active report and doesn't change - const report1 = LHNTestUtils.getFakeReport(['email3@test.com', 'email4@test.com']); + const report1 = LHNTestUtils.getFakeReport([3, 4]); // Given a free policy that doesn't change const policy = { @@ -315,7 +315,7 @@ describe('Sidebar', () => { Onyx.multiSet({ [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.BETAS]: betas, - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.POLICY}${policy.policyID}`]: policy, @@ -348,9 +348,9 @@ describe('Sidebar', () => { it('hides unread chats', () => { // Given the sidebar is rendered in #focus mode (hides read chats) // with report 1 and 2 having unread actions - const report1 = LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com'], 0, true); - const report2 = LHNTestUtils.getFakeReport(['email3@test.com', 'email4@test.com'], 0, true); - const report3 = LHNTestUtils.getFakeReport(['email5@test.com', 'email6@test.com']); + const report1 = LHNTestUtils.getFakeReport([1, 2], 0, true); + const report2 = LHNTestUtils.getFakeReport([3, 4], 0, true); + const report3 = LHNTestUtils.getFakeReport([5, 6]); LHNTestUtils.getDefaultRenderedSidebarLinks(report1.reportID); return ( @@ -359,7 +359,7 @@ describe('Sidebar', () => { .then(() => Onyx.multiSet({ [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, @@ -414,11 +414,11 @@ describe('Sidebar', () => { it('always shows pinned and draft chats', () => { // Given a draft report and a pinned report const draftReport = { - ...LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com']), + ...LHNTestUtils.getFakeReport([1, 2]), hasDraft: true, }; const pinnedReport = { - ...LHNTestUtils.getFakeReport(['email3@test.com', 'email4@test.com']), + ...LHNTestUtils.getFakeReport([3, 4]), isPinned: true, }; LHNTestUtils.getDefaultRenderedSidebarLinks(draftReport.reportID); @@ -429,7 +429,7 @@ describe('Sidebar', () => { .then(() => Onyx.multiSet({ [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [`${ONYXKEYS.COLLECTION.REPORT}${draftReport.reportID}`]: draftReport, [`${ONYXKEYS.COLLECTION.REPORT}${pinnedReport.reportID}`]: pinnedReport, }), @@ -449,18 +449,18 @@ describe('Sidebar', () => { it('archived rooms are displayed only when they have unread messages', () => { // Given an archived chat report, an archived default policy room, and an archived user created policy room const archivedReport = { - ...LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com']), + ...LHNTestUtils.getFakeReport([1, 2]), statusNum: CONST.REPORT.STATUS.CLOSED, stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, }; const archivedPolicyRoomReport = { - ...LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com']), + ...LHNTestUtils.getFakeReport([1, 2]), chatType: CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE, statusNum: CONST.REPORT.STATUS.CLOSED, stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, }; const archivedUserCreatedPolicyRoomReport = { - ...LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com']), + ...LHNTestUtils.getFakeReport([1, 2]), chatType: CONST.REPORT.CHAT_TYPE.POLICY_ROOM, statusNum: CONST.REPORT.STATUS.CLOSED, stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, @@ -476,7 +476,7 @@ describe('Sidebar', () => { Onyx.multiSet({ [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.BETAS]: betas, - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [`${ONYXKEYS.COLLECTION.REPORT}${archivedReport.reportID}`]: archivedReport, [`${ONYXKEYS.COLLECTION.REPORT}${archivedPolicyRoomReport.reportID}`]: archivedPolicyRoomReport, [`${ONYXKEYS.COLLECTION.REPORT}${archivedUserCreatedPolicyRoomReport.reportID}`]: archivedUserCreatedPolicyRoomReport, @@ -520,11 +520,11 @@ describe('Sidebar', () => { it('policy rooms are displayed only when they have unread messages', () => { // Given a default policy room and user created policy room const policyRoomReport = { - ...LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com']), + ...LHNTestUtils.getFakeReport([1, 2]), chatType: CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE, }; const userCreatedPolicyRoomReport = { - ...LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com']), + ...LHNTestUtils.getFakeReport([1, 2]), chatType: CONST.REPORT.CHAT_TYPE.POLICY_ROOM, }; LHNTestUtils.getDefaultRenderedSidebarLinks(); @@ -538,7 +538,7 @@ describe('Sidebar', () => { Onyx.multiSet({ [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.BETAS]: betas, - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [`${ONYXKEYS.COLLECTION.REPORT}${policyRoomReport.reportID}`]: policyRoomReport, [`${ONYXKEYS.COLLECTION.REPORT}${userCreatedPolicyRoomReport.reportID}`]: userCreatedPolicyRoomReport, }), @@ -576,7 +576,7 @@ describe('Sidebar', () => { describe('all combinations of isArchived, isUserCreatedPolicyRoom, hasAddWorkspaceError, isUnread, isPinned, hasDraft', () => { // Given a report that is the active report and doesn't change - const report1 = LHNTestUtils.getFakeReport(['email3@test.com', 'email4@test.com']); + const report1 = LHNTestUtils.getFakeReport([3, 4]); // Given a free policy that doesn't change const policy = { @@ -636,7 +636,7 @@ describe('Sidebar', () => { Onyx.multiSet({ [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.BETAS]: betas, - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.POLICY}${policy.policyID}`]: policy, @@ -687,7 +687,7 @@ describe('Sidebar', () => { Onyx.multiSet({ [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, [ONYXKEYS.BETAS]: betas, - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, }), ) @@ -737,7 +737,7 @@ describe('Sidebar', () => { Onyx.multiSet({ [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.BETAS]: betas, - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, }), ) @@ -785,7 +785,7 @@ describe('Sidebar', () => { Onyx.multiSet({ [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.BETAS]: betas, - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, }), ) @@ -829,7 +829,7 @@ describe('Sidebar', () => { Onyx.multiSet({ [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.BETAS]: betas, - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, }), ) diff --git a/tests/unit/SidebarOrderTest.js b/tests/unit/SidebarOrderTest.js index 2f929cb4e1a6..3c3863c411b2 100644 --- a/tests/unit/SidebarOrderTest.js +++ b/tests/unit/SidebarOrderTest.js @@ -12,7 +12,7 @@ jest.mock('../../src/libs/Permissions'); jest.mock('../../src/components/Icon/Expensicons'); const ONYXKEYS = { - PERSONAL_DETAILS: 'personalDetails', + PERSONAL_DETAILS_LIST: 'personalDetailsList', NVP_PRIORITY_MODE: 'nvp_priorityMode', SESSION: 'session', BETAS: 'betas', @@ -60,7 +60,7 @@ describe('Sidebar', () => { // When Onyx is updated with some personal details .then(() => Onyx.multiSet({ - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, }), ) @@ -75,7 +75,7 @@ describe('Sidebar', () => { it('contains one report when a report is in Onyx', () => { // Given a single report - const report = LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com']); + const report = LHNTestUtils.getFakeReport([1, 2]); LHNTestUtils.getDefaultRenderedSidebarLinks(report.reportID); return ( @@ -84,7 +84,7 @@ describe('Sidebar', () => { .then(() => Onyx.multiSet({ [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, }), ) @@ -101,13 +101,13 @@ describe('Sidebar', () => { // Given three unread reports in the recently updated order of 3, 2, 1 const report1 = { - ...LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com'], 3), + ...LHNTestUtils.getFakeReport([1, 2], 3), }; const report2 = { - ...LHNTestUtils.getFakeReport(['email3@test.com', 'email4@test.com'], 2), + ...LHNTestUtils.getFakeReport([3, 4], 2), }; const report3 = { - ...LHNTestUtils.getFakeReport(['email5@test.com', 'email6@test.com'], 1), + ...LHNTestUtils.getFakeReport([5, 6], 1), }; return ( @@ -116,7 +116,7 @@ describe('Sidebar', () => { .then(() => Onyx.multiSet({ [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, @@ -140,11 +140,11 @@ describe('Sidebar', () => { // And the first report has a draft // And the currently viewed report is the first report const report1 = { - ...LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com'], 3), + ...LHNTestUtils.getFakeReport([1, 2], 3), hasDraft: true, }; - const report2 = LHNTestUtils.getFakeReport(['email3@test.com', 'email4@test.com'], 2); - const report3 = LHNTestUtils.getFakeReport(['email5@test.com', 'email6@test.com'], 1); + const report2 = LHNTestUtils.getFakeReport([3, 4], 2); + const report3 = LHNTestUtils.getFakeReport([5, 6], 1); const reportIDFromRoute = report1.reportID; LHNTestUtils.getDefaultRenderedSidebarLinks(reportIDFromRoute); return ( @@ -153,7 +153,7 @@ describe('Sidebar', () => { .then(() => Onyx.multiSet({ [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, @@ -180,9 +180,9 @@ describe('Sidebar', () => { LHNTestUtils.getDefaultRenderedSidebarLinks(); // Given three reports in the recently updated order of 3, 2, 1 - const report1 = LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com'], 3); - const report2 = LHNTestUtils.getFakeReport(['email3@test.com', 'email4@test.com'], 2); - const report3 = LHNTestUtils.getFakeReport(['email5@test.com', 'email6@test.com'], 1); + const report1 = LHNTestUtils.getFakeReport([1, 2], 3); + const report2 = LHNTestUtils.getFakeReport([3, 4], 2); + const report3 = LHNTestUtils.getFakeReport([5, 6], 1); return ( waitForPromisesToResolve() @@ -190,7 +190,7 @@ describe('Sidebar', () => { .then(() => Onyx.multiSet({ [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, @@ -221,12 +221,12 @@ describe('Sidebar', () => { // Given three reports in the recently updated order of 3, 2, 1 // And the second report has a draft // And the currently viewed report is the second report - const report1 = LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com'], 3); + const report1 = LHNTestUtils.getFakeReport([1, 2], 3); const report2 = { - ...LHNTestUtils.getFakeReport(['email3@test.com', 'email4@test.com'], 2), + ...LHNTestUtils.getFakeReport([3, 4], 2), hasDraft: true, }; - const report3 = LHNTestUtils.getFakeReport(['email5@test.com', 'email6@test.com'], 1); + const report3 = LHNTestUtils.getFakeReport([5, 6], 1); const reportIDFromRoute = report2.reportID; LHNTestUtils.getDefaultRenderedSidebarLinks(reportIDFromRoute); @@ -236,7 +236,7 @@ describe('Sidebar', () => { .then(() => Onyx.multiSet({ [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, @@ -270,7 +270,7 @@ describe('Sidebar', () => { // Given a single report // And the report has a draft const report = { - ...LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com']), + ...LHNTestUtils.getFakeReport([1, 2]), hasDraft: true, }; @@ -280,7 +280,7 @@ describe('Sidebar', () => { .then(() => Onyx.multiSet({ [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, }), ) @@ -306,7 +306,7 @@ describe('Sidebar', () => { // Given a single report // And the report is pinned const report = { - ...LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com']), + ...LHNTestUtils.getFakeReport([1, 2]), isPinned: true, }; @@ -316,7 +316,7 @@ describe('Sidebar', () => { .then(() => Onyx.multiSet({ [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, }), ) @@ -342,25 +342,25 @@ describe('Sidebar', () => { // with a report that has a draft, a report that is pinned, and // an outstanding IOU report that doesn't belong to the current user const report1 = { - ...LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com'], 3), + ...LHNTestUtils.getFakeReport([1, 2], 3), isPinned: true, }; const report2 = { - ...LHNTestUtils.getFakeReport(['email3@test.com', 'email4@test.com'], 2), + ...LHNTestUtils.getFakeReport([3, 4], 2), hasDraft: true, }; const report3 = { - ...LHNTestUtils.getFakeReport(['email5@test.com', 'email6@test.com'], 1), + ...LHNTestUtils.getFakeReport([5, 6], 1), hasOutstandingIOU: true, // This has to be added after the IOU report is generated iouReportID: null, }; const iouReport = { - ...LHNTestUtils.getFakeReport(['email7@test.com', 'email8@test.com']), + ...LHNTestUtils.getFakeReport([7, 8]), type: CONST.REPORT.TYPE.IOU, - ownerEmail: 'email2@test.com', - managerEmail: 'email2@test.com', + ownerAccountID: 2, + managerID: 2, hasOutstandingIOU: true, total: 10000, currency: 'USD', @@ -368,7 +368,7 @@ describe('Sidebar', () => { }; report3.iouReportID = iouReport.reportID; const reportIDFromRoute = report2.reportID; - const currentlyLoggedInUserEmail = 'email9@test.com'; + const currentlyLoggedInUserAccountID = 9; LHNTestUtils.getDefaultRenderedSidebarLinks(reportIDFromRoute); return ( @@ -377,8 +377,8 @@ describe('Sidebar', () => { .then(() => Onyx.multiSet({ [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, - [ONYXKEYS.SESSION]: {email: currentlyLoggedInUserEmail}, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.SESSION]: {accountID: currentlyLoggedInUserAccountID}, [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, @@ -407,19 +407,19 @@ describe('Sidebar', () => { // Given three reports in the recently updated order of 3, 2, 1 // and they are all pinned const report1 = { - ...LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com'], 3), + ...LHNTestUtils.getFakeReport([1, 2], 3), isPinned: true, }; const report2 = { - ...LHNTestUtils.getFakeReport(['email3@test.com', 'email4@test.com'], 2), + ...LHNTestUtils.getFakeReport([3, 4], 2), isPinned: true, }; const report3 = { - ...LHNTestUtils.getFakeReport(['email5@test.com', 'email6@test.com'], 1), + ...LHNTestUtils.getFakeReport([5, 6], 1), isPinned: true, }; const report4 = { - ...LHNTestUtils.getFakeReport(['email7@test.com', 'email8@test.com'], 0), + ...LHNTestUtils.getFakeReport([7, 8], 0), isPinned: true, }; LHNTestUtils.getDefaultRenderedSidebarLinks('0'); @@ -429,7 +429,7 @@ describe('Sidebar', () => { .then(() => Onyx.multiSet({ [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, @@ -466,19 +466,19 @@ describe('Sidebar', () => { // Given three reports in the recently updated order of 3, 2, 1 // and they all have drafts const report1 = { - ...LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com'], 3), + ...LHNTestUtils.getFakeReport([1, 2], 3), hasDraft: true, }; const report2 = { - ...LHNTestUtils.getFakeReport(['email3@test.com', 'email4@test.com'], 2), + ...LHNTestUtils.getFakeReport([3, 4], 2), hasDraft: true, }; const report3 = { - ...LHNTestUtils.getFakeReport(['email5@test.com', 'email6@test.com'], 1), + ...LHNTestUtils.getFakeReport([5, 6], 1), hasDraft: true, }; const report4 = { - ...LHNTestUtils.getFakeReport(['email7@test.com', 'email8@test.com'], 0), + ...LHNTestUtils.getFakeReport([7, 8], 0), hasDraft: true, }; LHNTestUtils.getDefaultRenderedSidebarLinks('0'); @@ -488,7 +488,7 @@ describe('Sidebar', () => { .then(() => Onyx.multiSet({ [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, @@ -524,13 +524,13 @@ describe('Sidebar', () => { it('puts archived chats last', () => { // Given three reports, with the first report being archived const report1 = { - ...LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com']), + ...LHNTestUtils.getFakeReport([1, 2]), chatType: CONST.REPORT.CHAT_TYPE.POLICY_ROOM, statusNum: CONST.REPORT.STATUS.CLOSED, stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, }; - const report2 = LHNTestUtils.getFakeReport(['email3@test.com', 'email4@test.com']); - const report3 = LHNTestUtils.getFakeReport(['email5@test.com', 'email6@test.com']); + const report2 = LHNTestUtils.getFakeReport([3, 4]); + const report3 = LHNTestUtils.getFakeReport([5, 6]); // Given the user is in all betas const betas = [CONST.BETAS.DEFAULT_ROOMS, CONST.BETAS.POLICY_ROOMS, CONST.BETAS.POLICY_EXPENSE_CHAT]; @@ -542,7 +542,7 @@ describe('Sidebar', () => { Onyx.multiSet({ [ONYXKEYS.BETAS]: betas, [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, @@ -566,10 +566,10 @@ describe('Sidebar', () => { it('alphabetizes chats', () => { LHNTestUtils.getDefaultRenderedSidebarLinks(); - const report1 = LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com'], 3, true); - const report2 = LHNTestUtils.getFakeReport(['email3@test.com', 'email4@test.com'], 2, true); - const report3 = LHNTestUtils.getFakeReport(['email5@test.com', 'email6@test.com'], 1, true); - const report4 = LHNTestUtils.getFakeReport(['email7@test.com', 'email8@test.com'], 0, true); + const report1 = LHNTestUtils.getFakeReport([1, 2], 3, true); + const report2 = LHNTestUtils.getFakeReport([3, 4], 2, true); + const report3 = LHNTestUtils.getFakeReport([5, 6], 1, true); + const report4 = LHNTestUtils.getFakeReport([7, 8], 0, true); return ( waitForPromisesToResolve() @@ -578,7 +578,7 @@ describe('Sidebar', () => { .then(() => Onyx.multiSet({ [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, @@ -614,13 +614,13 @@ describe('Sidebar', () => { it('puts archived chats last', () => { // Given three unread reports, with the first report being archived const report1 = { - ...LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com'], 3, true), + ...LHNTestUtils.getFakeReport([1, 2], 3, true), chatType: CONST.REPORT.CHAT_TYPE.POLICY_ROOM, statusNum: CONST.REPORT.STATUS.CLOSED, stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, }; - const report2 = LHNTestUtils.getFakeReport(['email3@test.com', 'email4@test.com'], 2, true); - const report3 = LHNTestUtils.getFakeReport(['email5@test.com', 'email6@test.com'], 1, true); + const report2 = LHNTestUtils.getFakeReport([3, 4], 2, true); + const report3 = LHNTestUtils.getFakeReport([5, 6], 1, true); // Given the user is in all betas const betas = [CONST.BETAS.DEFAULT_ROOMS, CONST.BETAS.POLICY_ROOMS, CONST.BETAS.POLICY_EXPENSE_CHAT]; @@ -632,7 +632,7 @@ describe('Sidebar', () => { Onyx.multiSet({ [ONYXKEYS.BETAS]: betas, [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, @@ -654,51 +654,51 @@ describe('Sidebar', () => { it('orders IOU reports by displayName if amounts are the same', () => { // Given three IOU reports containing the same IOU amounts const report1 = { - ...LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com']), + ...LHNTestUtils.getFakeReport([1, 2]), hasOutstandingIOU: true, // This has to be added after the IOU report is generated iouReportID: null, }; const report2 = { - ...LHNTestUtils.getFakeReport(['email3@test.com', 'email4@test.com']), + ...LHNTestUtils.getFakeReport([3, 4]), hasOutstandingIOU: true, // This has to be added after the IOU report is generated iouReportID: null, }; const report3 = { - ...LHNTestUtils.getFakeReport(['email5@test.com', 'email6@test.com']), + ...LHNTestUtils.getFakeReport([5, 6]), hasOutstandingIOU: true, // This has to be added after the IOU report is generated iouReportID: null, }; const iouReport1 = { - ...LHNTestUtils.getFakeReport(['email7@test.com', 'email8@test.com']), + ...LHNTestUtils.getFakeReport([7, 8]), type: CONST.REPORT.TYPE.IOU, - ownerEmail: 'email2@test.com', - managerEmail: 'email2@test.com', + ownerAccountID: 2, + managerID: 2, hasOutstandingIOU: true, total: 10000, currency: 'USD', chatReportID: report3.reportID, }; const iouReport2 = { - ...LHNTestUtils.getFakeReport(['email9@test.com', 'email10@test.com']), + ...LHNTestUtils.getFakeReport([9, 10]), type: CONST.REPORT.TYPE.IOU, - ownerEmail: 'email2@test.com', - managerEmail: 'email2@test.com', + ownerAccountID: 2, + managerID: 2, hasOutstandingIOU: true, total: 10000, currency: 'USD', chatReportID: report3.reportID, }; const iouReport3 = { - ...LHNTestUtils.getFakeReport(['email11@test.com', 'email12@test.com']), + ...LHNTestUtils.getFakeReport([11, 12]), type: CONST.REPORT.TYPE.IOU, - ownerEmail: 'email2@test.com', - managerEmail: 'email2@test.com', + ownerAccountID: 2, + managerID: 2, hasOutstandingIOU: true, total: 10000, currency: 'USD', @@ -709,7 +709,7 @@ describe('Sidebar', () => { report2.iouReportID = iouReport2.reportID; report3.iouReportID = iouReport3.reportID; - const currentlyLoggedInUserEmail = 'email13@test.com'; + const currentlyLoggedInUserAccountID = 13; LHNTestUtils.getDefaultRenderedSidebarLinks('0'); return ( waitForPromisesToResolve() @@ -717,8 +717,8 @@ describe('Sidebar', () => { .then(() => Onyx.multiSet({ [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, - [ONYXKEYS.SESSION]: {email: currentlyLoggedInUserEmail}, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.SESSION]: {accountID: currentlyLoggedInUserAccountID}, [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, @@ -746,15 +746,15 @@ describe('Sidebar', () => { // Given three nonArchived reports created at the same time const lastVisibleActionCreated = DateUtils.getDBTime(); const report1 = { - ...LHNTestUtils.getFakeReport(['email1@test.com', 'email2@test.com']), + ...LHNTestUtils.getFakeReport([1, 2]), lastVisibleActionCreated, }; const report2 = { - ...LHNTestUtils.getFakeReport(['email3@test.com', 'email4@test.com']), + ...LHNTestUtils.getFakeReport([3, 4]), lastVisibleActionCreated, }; const report3 = { - ...LHNTestUtils.getFakeReport(['email5@test.com', 'email6@test.com']), + ...LHNTestUtils.getFakeReport([5, 6]), lastVisibleActionCreated, }; @@ -765,7 +765,7 @@ describe('Sidebar', () => { .then(() => Onyx.multiSet({ [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, diff --git a/tests/unit/SidebarTest.js b/tests/unit/SidebarTest.js index ceff6b0d106f..9b5b021341ac 100644 --- a/tests/unit/SidebarTest.js +++ b/tests/unit/SidebarTest.js @@ -11,7 +11,7 @@ jest.mock('../../src/libs/Permissions'); jest.mock('../../src/components/Icon/Expensicons'); const ONYXKEYS = { - PERSONAL_DETAILS: 'personalDetails', + PERSONAL_DETAILS_LIST: 'personalDetailsList', NVP_PRIORITY_MODE: 'nvp_priorityMode', SESSION: 'session', BETAS: 'betas', @@ -58,7 +58,7 @@ describe('Sidebar', () => { Onyx.multiSet({ [ONYXKEYS.BETAS]: betas, [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, }), ) @@ -100,7 +100,7 @@ describe('Sidebar', () => { Onyx.multiSet({ [ONYXKEYS.BETAS]: betas, [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, - [ONYXKEYS.PERSONAL_DETAILS]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`]: {[action.reportActionId]: action}, }), diff --git a/tests/utils/LHNTestUtils.js b/tests/utils/LHNTestUtils.js index 1540964fcb46..9dd195c094b8 100644 --- a/tests/utils/LHNTestUtils.js +++ b/tests/utils/LHNTestUtils.js @@ -26,55 +26,64 @@ jest.mock('@react-navigation/native', () => { }); const fakePersonalDetails = { - 'email1@test.com': { + 1: { + accountID: 1, login: 'email1@test.com', displayName: 'Email One', avatar: 'none', firstName: 'One', }, - 'email2@test.com': { + 2: { + accountID: 2, login: 'email2@test.com', displayName: 'Email Two', avatar: 'none', firstName: 'Two', }, - 'email3@test.com': { + 3: { + accountID: 3, login: 'email3@test.com', displayName: 'Email Three', avatar: 'none', firstName: 'Three', }, - 'email4@test.com': { + 4: { + accountID: 4, login: 'email4@test.com', displayName: 'Email Four', avatar: 'none', firstName: 'Four', }, - 'email5@test.com': { + 5: { + accountID: 5, login: 'email5@test.com', displayName: 'Email Five', avatar: 'none', firstName: 'Five', }, - 'email6@test.com': { + 6: { + accountID: 6, login: 'email6@test.com', displayName: 'Email Six', avatar: 'none', firstName: 'Six', }, - 'email7@test.com': { + 7: { + accountID: 7, login: 'email7@test.com', displayName: 'Email Seven', avatar: 'none', firstName: 'Seven', }, - 'email8@test.com': { + 8: { + accountID: 8, login: 'email8@test.com', displayName: 'Email Eight', avatar: 'none', firstName: 'Eight', }, - 'email9@test.com': { + 9: { + accountID: 9, login: 'email9@test.com', displayName: 'Email Nine', avatar: 'none', @@ -86,12 +95,12 @@ let lastFakeReportID = 0; let lastFakeReportActionID = 0; /** - * @param {String[]} participants + * @param {Number[]} participants * @param {Number} millisecondsInThePast the number of milliseconds in the past for the last message timestamp (to order reports by most recent messages) * @param {boolean} isUnread * @returns {Object} */ -function getFakeReport(participants = ['email1@test.com', 'email2@test.com'], millisecondsInThePast = 0, isUnread = false) { +function getFakeReport(participants = [1, 2], millisecondsInThePast = 0, isUnread = false) { const lastVisibleActionCreated = DateUtils.getDBTime(Date.now() - millisecondsInThePast); return { type: CONST.REPORT.TYPE.CHAT, @@ -99,7 +108,7 @@ function getFakeReport(participants = ['email1@test.com', 'email2@test.com'], mi reportName: 'Report', lastVisibleActionCreated, lastReadTime: isUnread ? DateUtils.subtractMillisecondsFromDateTime(lastVisibleActionCreated, 1) : lastVisibleActionCreated, - participants, + participantAccountIDs: participants, }; } diff --git a/tests/utils/TestHelper.js b/tests/utils/TestHelper.js index 65bc2c631aae..4f3cf2d61aa6 100644 --- a/tests/utils/TestHelper.js +++ b/tests/utils/TestHelper.js @@ -62,9 +62,9 @@ function signInWithTestUser(accountID = 1, login = 'test@user.com', password = ' }, { onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, value: { - [login]: buildPersonalDetails(login, accountID, firstName), + [accountID]: buildPersonalDetails(login, accountID, firstName), }, }, ], @@ -189,8 +189,8 @@ function getGlobalFetchMock() { * @returns {Promise} */ function setPersonalDetails(login, accountID) { - Onyx.merge(ONYXKEYS.PERSONAL_DETAILS, { - [login]: buildPersonalDetails(login, accountID), + Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, { + [accountID]: buildPersonalDetails(login, accountID), }); return waitForPromisesToResolve(); }