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 374a69ca5824..0a61c455d4f6 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -619,6 +619,7 @@ const CONST = { MAX_ROOM_NAME_LENGTH: 79, LAST_MESSAGE_TEXT_MAX_LENGTH: 200, OWNER_EMAIL_FAKE: '__FAKE__', + OWNER_ACCOUNT_ID_FAKE: 0, DEFAULT_REPORT_NAME: 'Chat Report', }, COMPOSER: { @@ -862,22 +863,41 @@ 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)), }, ENVIRONMENT: { @@ -1059,6 +1079,7 @@ const CONST = { ROOM_PREFIX: '#', CUSTOM_UNIT_RATE_BASE_OFFSET: 100, OWNER_EMAIL_FAKE: '_FAKE_', + OWNER_ACCOUNT_ID_FAKE: 0, }, CUSTOM_UNITS: { @@ -1166,21 +1187,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..b04687967dfc 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', @@ -113,7 +110,7 @@ export default { COLLECTION: { 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 db8deadecd83..a742b97fb0a9 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,14 @@ const defaultProps = { function 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 +82,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 66c2b4821b33..7889dcb0b703 100644 --- a/src/components/AvatarWithDisplayName.js +++ b/src/components/AvatarWithDisplayName.js @@ -50,12 +50,12 @@ const defaultProps = { }; function AvatarWithDisplayName(props) { - const title = props.isAnonymous ? props.report.displayName : ReportUtils.getDisplayNameForParticipant(props.report.ownerEmail, true); + const title = props.isAnonymous ? props.report.displayName : ReportUtils.getDisplayNameForParticipant(props.report.ownerAccountID, true); 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 displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips(ownerPersonalDetails, false); + const ownerPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs([props.report.ownerAccountID], props.personalDetails); + const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips(_.values(ownerPersonalDetails), false); const avatarContainerStyle = StyleUtils.getEmptyAvatarStyle(props.size) || styles.emptyAvatar; return ( diff --git a/src/components/AvatarWithIndicator.js b/src/components/AvatarWithIndicator.js index f59818099914..c81260113ace 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 @@ function 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 @@ function 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/DisplayNames/displayNamesPropTypes.js b/src/components/DisplayNames/displayNamesPropTypes.js index c923b943174f..5ad332f7a117 100644 --- a/src/components/DisplayNames/displayNamesPropTypes.js +++ b/src/components/DisplayNames/displayNamesPropTypes.js @@ -11,7 +11,7 @@ const propTypes = { displayName: PropTypes.string, /** The Account ID for the tooltip */ - accountID: PropTypes.string, + accountID: PropTypes.number, /** The login for the tooltip fallback */ login: PropTypes.string, diff --git a/src/components/MoneyRequestHeader.js b/src/components/MoneyRequestHeader.js index f9d9982e3708..af79fd2b8132 100644 --- a/src/components/MoneyRequestHeader.js +++ b/src/components/MoneyRequestHeader.js @@ -80,16 +80,16 @@ function 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 ( {_.map(reactionsWithCount, (reaction) => { const reactionCount = reaction.users.length; - const reactionUsers = _.map(reaction.users, (sender) => sender.accountID.toString()); + const reactionUsers = _.map(reaction.users, (sender) => sender.accountID); const emoji = _.find(emojis, (e) => e.name === reaction.emoji); const emojiCodes = EmojiUtils.getUniqueEmojiCodes(emoji, reaction.users); const hasUserReacted = Report.hasAccountIDReacted(props.currentUserPersonalDetails.accountID, reactionUsers); @@ -80,7 +80,7 @@ function ReportActionItemReactions(props) { currentUserPersonalDetails={props.currentUserPersonalDetails} /> )} - renderTooltipContentKey={[...reactionUsers, ...emojiCodes]} + renderTooltipContentKey={[..._.map(reactionUsers, (user) => user.toString()), ...emojiCodes]} key={reaction.emoji} > diff --git a/src/components/ReportActionItem/IOUPreview.js b/src/components/ReportActionItem/IOUPreview.js index 6281706da2f3..4292f3616be7 100644 --- a/src/components/ReportActionItem/IOUPreview.js +++ b/src/components/ReportActionItem/IOUPreview.js @@ -60,9 +60,15 @@ const propTypes = { /** Email address of the manager in this iou report */ managerEmail: PropTypes.string, + /** Account ID of the manager in this iou report */ + managerID: PropTypes.number, + /** Email address of the creator of this iou report */ ownerEmail: PropTypes.string, + /** Account ID of the creator of this iou report */ + ownerAccountID: PropTypes.number, + /** Outstanding amount in cents of this transaction */ total: PropTypes.number, @@ -83,7 +89,7 @@ const propTypes = { personalDetails: PropTypes.objectOf( PropTypes.shape({ /** This is either the user's full name, or their login if full name is an empty string */ - displayName: PropTypes.string.isRequired, + displayName: PropTypes.string, }), ), @@ -128,14 +134,14 @@ function IOUPreview(props) { 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 @@ function 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 b9bebeb94193..8bef184bab08 100644 --- a/src/components/ReportActionItem/MoneyRequestAction.js +++ b/src/components/ReportActionItem/MoneyRequestAction.js @@ -15,12 +15,12 @@ import Navigation from '../../libs/Navigation/Navigation'; import ROUTES from '../../ROUTES'; import styles from '../../styles/styles'; import * as IOUUtils from '../../libs/IOUUtils'; -import * as OptionsListUtils from '../../libs/OptionsListUtils'; import * as ReportUtils from '../../libs/ReportUtils'; import * as Report from '../../libs/actions/Report'; import withLocalize, {withLocalizePropTypes} from '../withLocalize'; import * as ReportActionsUtils from '../../libs/ReportActionsUtils'; import refPropTypes from '../refPropTypes'; +import * as PersonalDetailsUtils from '../../libs/PersonalDetailsUtils'; const propTypes = { /** All the data of the action */ @@ -103,10 +103,9 @@ function 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, Number(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 @@ function MoneyRequestAction(props) { '', CONST.POLICY.OWNER_EMAIL_FAKE, CONST.POLICY.OWNER_EMAIL_FAKE, + CONST.POLICY.OWNER_ACCOUNT_ID_FAKE, false, '', undefined, @@ -122,7 +122,8 @@ function MoneyRequestAction(props) { props.requestReportID, ); - Report.openReport(thread.reportID, thread.participants, thread, props.action.reportActionID); + const userLogins = PersonalDetailsUtils.getLoginsByAccountIDs(thread.participantAccountIDs); + Report.openReport(thread.reportID, userLogins, thread, props.action.reportActionID); Navigation.navigate(ROUTES.getReportRoute(thread.reportID)); } else { Report.openReport(childReportID); diff --git a/src/components/ReportWelcomeText.js b/src/components/ReportWelcomeText.js index 0607f0713fbb..c000d88f4c8c 100644 --- a/src/components/ReportWelcomeText.js +++ b/src/components/ReportWelcomeText.js @@ -22,11 +22,11 @@ const personalDetailsPropTypes = PropTypes.shape({ login: PropTypes.string.isRequired, /** The URL of the person's avatar (there should already be a default avatar if - the person doesn't have their own avatar uploaded yet) */ - avatar: PropTypes.string.isRequired, + the person doesn't have their own avatar uploaded yet, except for anon users) */ + avatar: PropTypes.string, /** This is either the user's full name, or their login if full name is an empty string */ - displayName: PropTypes.string.isRequired, + displayName: PropTypes.string, }); const propTypes = { @@ -54,11 +54,14 @@ function 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 isMultipleParticipant = participants.length > 1; - const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips(OptionsListUtils.getPersonalDetailsForLogins(participants, props.personalDetails), isMultipleParticipant); + const participantAccountIDs = lodashGet(props.report, 'participantAccountIDs', []); + 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 ( <> @@ -70,7 +73,7 @@ function 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)} @@ -98,7 +101,7 @@ function ReportWelcomeText(props) { { - const accountDetails = props.personalDetails[participants[index]]; + const accountDetails = props.personalDetails[participantAccountIDs[index]]; if (accountDetails && accountDetails.accountID) { Navigation.navigate(ROUTES.getProfileRoute(accountDetails.accountID)); } @@ -134,7 +137,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 4c07e673a1c5..a44c841b87da 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) && ( <> ({...props.personalDetails[currentUserEmail], accountID}), [props.personalDetails, currentUserEmail, accountID]); + const currentUserPersonalDetails = useMemo(() => ({...props.personalDetails[accountID], accountID}), [props.personalDetails, accountID]); return ( { @@ -20,15 +20,15 @@ Onyx.connect({ return; } - currentUserEmail = val.email; + currentUserAccountID = val.accountID; }, }); let timezone = CONST.DEFAULT_TIME_ZONE; Onyx.connect({ - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, callback: (val) => { - timezone = lodashGet(val, [currentUserEmail, 'timezone'], CONST.DEFAULT_TIME_ZONE); + timezone = lodashGet(val, [currentUserAccountID, 'timezone'], CONST.DEFAULT_TIME_ZONE); }, }); diff --git a/src/libs/IOUUtils.js b/src/libs/IOUUtils.js index 7224e63f31e0..7f7782dee996 100644 --- a/src/libs/IOUUtils.js +++ b/src/libs/IOUUtils.js @@ -29,13 +29,13 @@ function calculateAmount(numberOfParticipants, total, isDefaultUser = false) { * If user1 requests $17 from user2, then we have: {ownerEmail: user1, managerEmail: user2, total: $7 (still a positive amount, but now owed to user1)} * * @param {Object} iouReport - * @param {String} actorEmail + * @param {Number} actorAccountID * @param {Number} amount * @param {String} currency * @param {String} type * @returns {Object} */ -function updateIOUOwnerAndTotal(iouReport, actorEmail, amount, currency, type = CONST.IOU.REPORT_ACTION_TYPE.CREATE) { +function updateIOUOwnerAndTotal(iouReport, actorAccountID, amount, currency, type = CONST.IOU.REPORT_ACTION_TYPE.CREATE) { if (currency !== iouReport.currency) { return iouReport; } @@ -43,7 +43,7 @@ function updateIOUOwnerAndTotal(iouReport, actorEmail, amount, currency, type = // Make a copy so we don't mutate the original object const iouReportUpdate = {...iouReport}; - if (actorEmail === iouReport.ownerEmail) { + if (actorAccountID === iouReport.ownerAccountID) { iouReportUpdate.total += type === CONST.IOU.REPORT_ACTION_TYPE.DELETE ? -amount : amount; } else { iouReportUpdate.total += type === CONST.IOU.REPORT_ACTION_TYPE.DELETE ? amount : -amount; @@ -51,6 +51,8 @@ function updateIOUOwnerAndTotal(iouReport, actorEmail, amount, currency, type = if (iouReportUpdate.total < 0) { // The total sign has changed and hence we need to flip the manager and owner of the report. + iouReportUpdate.ownerAccountID = iouReport.managerID; + iouReportUpdate.managerID = iouReport.ownerAccountID; iouReportUpdate.ownerEmail = iouReport.managerEmail; iouReportUpdate.managerEmail = iouReport.ownerEmail; iouReportUpdate.total = -iouReportUpdate.total; diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index c4a84701a083..fb02a811b0ec 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -48,7 +48,7 @@ Onyx.connect({ let timezone; Onyx.connect({ - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, callback: (val) => { if (!val || timezone) { return; diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 7d1dd8592e67..ea73a28a2e85 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; @@ -143,17 +147,17 @@ function addSMSDomainIfPhoneNumber(login) { } /** - * Returns avatar data for a list of user logins + * Returns avatar data for a list of user accountIDs * - * @param {Array} logins + * @param {Array} accountIDs * @param {Object} personalDetails * @returns {Object} */ -function getAvatarsForLogins(logins, personalDetails) { - return _.map(logins, (login) => { - const userPersonalDetail = lodashGet(personalDetails, login, {login, avatar: ''}); +function getAvatarsForAccountIDs(accountIDs, personalDetails) { + return _.map(accountIDs, (accountID) => { + const userPersonalDetail = lodashGet(personalDetails, accountID, {login: '', accountID, avatar: ''}); return { - source: UserUtils.getAvatar(userPersonalDetail.avatar, userPersonalDetail.login), + source: UserUtils.getAvatar(userPersonalDetail.avatar, userPersonalDetail.accountID), type: CONST.ICON_TYPE_AVATAR, name: userPersonalDetail.login, }; @@ -161,38 +165,34 @@ function getAvatarsForLogins(logins, personalDetails) { } /** - * Returns the personal details for an array of logins + * 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. - .compact() - .each((login) => { - let personalDetail = personalDetails[login]; - if (!personalDetail) { - personalDetail = { - login, - displayName: LocalePhoneNumber.formatPhoneNumber(login), - avatar: UserUtils.getDefaultAvatar(login), - }; - } + _.each(accountIDs, (accountID) => { + const cleanAccountID = Number(accountID); + let personalDetail = personalDetails[accountID]; + if (!personalDetail) { + personalDetail = { + accountID: cleanAccountID, + avatar: UserUtils.getDefaultAvatar(cleanAccountID), + }; + } - if (login === CONST.EMAIL.CONCIERGE) { - personalDetail.avatar = CONST.CONCIERGE_ICON_URL; - } + if (cleanAccountID === CONST.ACCOUNT_ID.CONCIERGE) { + personalDetail.avatar = CONST.CONCIERGE_ICON_URL; + } - personalDetailsForLogins[login] = personalDetail; - }); - return personalDetailsForLogins; + personalDetailsForAccountIDs[cleanAccountID] = personalDetail; + }); + return personalDetailsForAccountIDs; } /** @@ -201,7 +201,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 +211,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 +244,9 @@ function getParticipantNames(personalDetailList) { // `_.contains(Array, value)` for an Array with n members. const participantNames = new Set(); _.each(personalDetailList, (participant) => { + if (participant.accountID) { + participantNames.add(participant.accountID.toString()); + } if (participant.login) { participantNames.add(participant.login.toLowerCase()); } @@ -302,10 +306,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 +368,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 +377,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, @@ -383,6 +389,7 @@ function createOption(logins, personalDetails, report, reportActions = {}, {show ownerEmail: null, subtitle: null, participantsList: null, + accountID: 0, login: null, reportID: null, phoneNumber: null, @@ -402,7 +409,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 +437,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 +450,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 +473,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 = String(accountIDs[0]); + result.alternateText = ''; } result.isIOUReportOwner = ReportUtils.isIOUOwnedByCurrentUser(result, iouReports); @@ -477,13 +483,14 @@ function createOption(logins, personalDetails, report, reportActions = {}, {show if (!hasMultipleParticipants) { result.login = personalDetail.login; + result.accountID = Number(personalDetail.accountID); result.phoneNumber = personalDetail.phoneNumber; result.payPalMeAddress = personalDetail.payPalMeAddress; } 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 +516,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 +581,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 +612,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 +629,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 +639,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 +647,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 +736,29 @@ function getOptions( !_.find(loginOptionsToExclude, (loginOptionToExclude) => loginOptionToExclude.login === addSMSDomainIfPhoneNumber(searchValue).toLowerCase()) && (searchValue !== CONST.EMAIL.CHRONOS || Permissions.canUseChronos(betas)) ) { - userToInvite = createOption([searchValue], personalDetails, null, reportActions, { + // Generates an optimistic account ID for new users not yet saved in Onyx + const optimisticAccountID = UserUtils.generateAccountID(); + const personalDetailsExtended = { + ...personalDetails, + [optimisticAccountID]: { + accountID: optimisticAccountID, + login: searchValue, + avatar: UserUtils.getDefaultAvatar(optimisticAccountID), + }, + }; + userToInvite = createOption([optimisticAccountID], personalDetailsExtended, null, reportActions, { showChatPreviewLine, }); + userToInvite.isOptimisticAccount = true; + userToInvite.login = searchValue; + userToInvite.text = userToInvite.text || searchValue; + userToInvite.alternateText = userToInvite.alternateText || searchValue; // If user doesn't exist, use a default avatar userToInvite.icons = [ { - source: UserUtils.getAvatar('', searchValue), - name: searchValue, + source: UserUtils.getAvatar('', optimisticAccountID), + login: searchValue, type: CONST.ICON_TYPE_AVATAR, }, ]; @@ -821,6 +844,7 @@ function getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetail, amount ], descriptiveText: amountText, login: personalDetail.login, + accountID: personalDetail.accountID, }; } @@ -961,7 +985,7 @@ function shouldOptionShowTooltip(option) { export { addSMSDomainIfPhoneNumber, - getAvatarsForLogins, + getAvatarsForAccountIDs, isCurrentUser, isPersonalDetailsReady, getSearchOptions, @@ -969,7 +993,7 @@ export { getShareDestinationOptions, getMemberInviteOptions, getHeaderMessage, - getPersonalDetailsForLogins, + getPersonalDetailsForAccountIDs, getIOUConfirmationOptionsFromPayeePersonalDetail, getIOUConfirmationOptionsFromParticipants, getSearchText, diff --git a/src/libs/PersonalDetailsUtils.js b/src/libs/PersonalDetailsUtils.js index 2f84e9db5e07..26c0a67aae48 100644 --- a/src/libs/PersonalDetailsUtils.js +++ b/src/libs/PersonalDetailsUtils.js @@ -1,17 +1,31 @@ +import lodashGet from 'lodash/get'; import Onyx from 'react-native-onyx'; import _ from 'underscore'; import ONYXKEYS from '../ONYXKEYS'; import * as Localize from './Localize'; +import * as UserUtils from './UserUtils'; let personalDetails = []; Onyx.connect({ - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, callback: (val) => (personalDetails = _.values(val)), }); /** - * Given a list of account IDs (as string) it will return an array of personal details objects. - * @param {Array} accountIDs - Array of accountIDs + * @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 number) it will return an array of personal details objects. + * @param {Array} accountIDs - Array of accountIDs * @param {Number} currentUserAccountID * @param {Boolean} shouldChangeUserDisplayName - It will replace the current user's personal detail object's displayName with 'You'. * @returns {Array} - Array of personal detail objects @@ -21,7 +35,7 @@ function getPersonalDetailsByIDs(accountIDs, currentUserAccountID, shouldChangeU _.each( _.filter(personalDetails, (detail) => accountIDs.includes(detail.accountID)), (detail) => { - if (shouldChangeUserDisplayName && currentUserAccountID.toString() === detail.accountID) { + if (shouldChangeUserDisplayName && currentUserAccountID === detail.accountID) { result.push({ ...detail, displayName: Localize.translateLocal('common.you'), @@ -34,7 +48,47 @@ function getPersonalDetailsByIDs(accountIDs, currentUserAccountID, shouldChangeU return result; } -export { - // eslint-disable-next-line import/prefer-default-export - getPersonalDetailsByIDs, -}; +/** + * Given a list of logins, find the associated personal detail and return related accountIDs. + * + * @param {Array} logins Array of user logins + * @returns {Array} - Array of accountIDs according to passed logins + */ +function getAccountIDsByLogins(logins) { + return _.reduce( + logins, + (foundAccountIDs, login) => { + const currentDetail = _.find(personalDetails, (detail) => detail.login === login); + if (!currentDetail) { + // generate an account ID because in this case the detail is probably new, so we don't have a real accountID yet + foundAccountIDs.push(UserUtils.generateAccountID()); + } else { + foundAccountIDs.push(Number(currentDetail.accountID)); + } + return foundAccountIDs; + }, + [], + ); +} + +/** + * Given a list of accountIDs, find the associated personal detail and return related logins. + * + * @param {Array} accountIDs Array of user accountIDs + * @returns {Array} - Array of logins according to passed accountIDs + */ +function getLoginsByAccountIDs(accountIDs) { + return _.reduce( + accountIDs, + (foundLogins, accountID) => { + const currentDetail = _.find(personalDetails, (detail) => Number(detail.accountID) === Number(accountID)) || {}; + if (currentDetail.login) { + foundLogins.push(currentDetail.login); + } + return foundLogins; + }, + [], + ); +} + +export {getDisplayNameOrDefault, getPersonalDetailsByIDs, getAccountIDsByLogins, getLoginsByAccountIDs}; diff --git a/src/libs/PolicyUtils.js b/src/libs/PolicyUtils.js index 91396be628a3..22b5d8635f72 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,38 @@ 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. + * + * 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) { + const memberEmailsToAccountIDs = {}; + _.each(policyMembers, (member, accountID) => { + if (!_.isEmpty(member.errors)) { + return; + } + const personalDetail = personalDetails[accountID]; + if (!personalDetail || !personalDetail.login) { + return; + } + memberEmailsToAccountIDs[personalDetail.login] = accountID; + }); + return memberEmailsToAccountIDs; +} + +export { + hasPolicyMemberError, + hasPolicyError, + hasPolicyErrorFields, + hasCustomUnitsError, + getPolicyBrickRoadIndicatorStatus, + shouldShowPolicy, + isExpensifyTeam, + isPolicyAdmin, + getClientPolicyMemberEmailsToAccountIDs, +}; diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 498c519661f2..1af14aa1d2a1 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(', ') ); @@ -359,22 +355,35 @@ function getBankAccountRoute(report) { } /** - * Returns true if there are any guides accounts (team.expensify.com) in emails - * @param {Array} emails + * Only returns true if this is our main 1:1 DM report with Concierge + * + * @param {Object} report * @returns {Boolean} */ -function hasExpensifyGuidesEmails(emails) { - return _.some(emails, (email) => Str.extractEmailDomain(email) === CONST.EMAIL.GUIDES_DOMAIN); +function isConciergeChatReport(report) { + return lodashGet(report, 'participantAccountIDs', []).length === 1 && Number(report.participantAccountIDs[0]) === CONST.ACCOUNT_ID.CONCIERGE; } /** - * Only returns true if this is our main 1:1 DM report with Concierge + * Returns true if there are any Expensify accounts (i.e. with domain 'expensify.com') in the set of accountIDs + * by cross-referencing the accountIDs with personalDetails. * - * @param {Object} report + * @param {Array} accountIDs + * @return {Boolean} + */ +function hasExpensifyEmails(accountIDs) { + return _.some(accountIDs, (accountID) => Str.extractEmailDomain(lodashGet(allPersonalDetails, [accountID, 'login'], '')) === CONST.EXPENSIFY_PARTNER_NAME); +} + +/** + * Returns true if there are any guides accounts (team.expensify.com) in a list of accountIDs + * by cross-referencing the accountIDs with personalDetails since guides that are participants + * of the user's chats should have their personal details in Onyx. + * @param {Array} accountIDs * @returns {Boolean} */ -function isConciergeChatReport(report) { - return lodashGet(report, 'participants', []).length === 1 && report.participants[0] === CONST.EMAIL.CONCIERGE; +function hasExpensifyGuidesEmails(accountIDs) { + return _.some(accountIDs, (accountID) => Str.extractEmailDomain(lodashGet(allPersonalDetails, [accountID, 'login'], '')) === CONST.EMAIL.GUIDES_DOMAIN); } /** @@ -406,7 +415,7 @@ function findLastAccessedReport(reports, ignoreDomainRooms, policies, isFirstTim // Domain rooms are now the only type of default room that are on the defaultRooms beta. sortedReports = _.filter( sortedReports, - (report) => !isDomainRoom(report) || getPolicyType(report, policies) === CONST.POLICY.TYPE.FREE || hasExpensifyGuidesEmails(lodashGet(report, ['participants'], [])), + (report) => !isDomainRoom(report) || getPolicyType(report, policies) === CONST.POLICY.TYPE.FREE || hasExpensifyGuidesEmails(lodashGet(report, ['participantAccountIDs'], [])), ); } @@ -569,40 +578,30 @@ 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); } /** - * Returns true if there is any automated expensify account in emails - * @param {Array} emails + * Returns true if there is any automated expensify account in accountIDs + * @param {Array} accountIDs * @returns {Boolean} */ -function hasAutomatedExpensifyEmails(emails) { - return _.intersection(emails, CONST.EXPENSIFY_EMAILS).length > 0; -} - -/** - * Returns true if there are any Expensify accounts (i.e. with domain 'expensify.com') in the set of emails. - * - * @param {Array} emails - * @return {Boolean} - */ -function hasExpensifyEmails(emails) { - return _.some(emails, (email) => Str.extractEmailDomain(email) === CONST.EXPENSIFY_PARTNER_NAME); +function hasAutomatedExpensifyAccountIDs(accountIDs) { + return _.intersection(accountIDs, CONST.EXPENSIFY_ACCOUNT_IDS).length > 0; } /** * Whether the time row should be shown for a report. * @param {Array} personalDetails * @param {Object} report - * @param {String} login + * @param {Number} accountID * @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]]; +function canShowReportRecipientLocalTime(personalDetails, report, accountID) { + const reportParticipants = _.without(lodashGet(report, 'participantAccountIDs', []), accountID); + 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( @@ -662,9 +661,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([lodashGet(personalDetails, [accountID, 'login'], ''), lodashGet(personalDetails, [accountID, 'firstName'], ''), avatarSource]); } // Sort all logins by first name (which is the second element in the array) @@ -718,8 +717,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, }; @@ -729,7 +729,7 @@ function getIcons(report, personalDetails, defaultIcon = null, isPayer = false) if (isTaskReport(report)) { const ownerEmail = report.ownerEmail || ''; const ownerIcon = { - source: UserUtils.getAvatar(lodashGet(personalDetails, [ownerEmail, 'avatar']), ownerEmail), + source: UserUtils.getAvatar(lodashGet(personalDetails, [report.ownerAccountID, 'avatar']), report.ownerAccountID), name: ownerEmail, type: CONST.ICON_TYPE_AVATAR, }; @@ -766,7 +766,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, }; @@ -783,33 +783,40 @@ 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 {Number} accountID * @returns {Object} */ -function getPersonalDetailsForLogin(login) { - if (!login) { +function getPersonalDetailsForAccountID(accountID) { + if (!accountID) { return {}; } + if (Number(accountID) === CONST.ACCOUNT_ID.CONCIERGE) { + return { + accountID, + displayName: 'Concierge', + login: CONST.EMAIL.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), } ); } @@ -827,15 +834,15 @@ function getAccountIDForLogin(login) { /** * Get the displayName for a single report participant. * - * @param {String} login + * @param {Number} 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; const shortName = personalDetails.firstName || longName; return shouldUseShortForm ? shortName : longName; @@ -848,9 +855,9 @@ function getDisplayNameForParticipant(login, shouldUseShortForm = false) { */ function getDisplayNamesWithTooltips(participants, isMultipleParticipantReport) { return _.map(participants, (participant) => { - const displayName = getDisplayNameForParticipant(participant.login, isMultipleParticipantReport); - const avatar = UserUtils.getDefaultAvatar(participant.login); - const accountID = participant.accountID; + const accountID = Number(participant.accountID); + const displayName = getDisplayNameForParticipant(accountID, isMultipleParticipantReport) || participant.login; + const avatar = UserUtils.getDefaultAvatar(accountID); let pronouns = participant.pronouns; if (pronouns && pronouns.startsWith(CONST.PRONOUNS.PREFIX)) { @@ -922,7 +929,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) { @@ -953,7 +960,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}); } @@ -1013,11 +1020,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(', '); } /** @@ -1162,12 +1169,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.getDefaultAvatarURL(currentUserAccountID)), created: DateUtils.getDBTime(), message: [ { @@ -1190,11 +1197,12 @@ function buildOptimisticAddCommentReportAction(text, file) { * @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 {Number} 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, taskAssignee, taskAssigneeAccountID, text, parentReportID) { const reportAction = buildOptimisticAddCommentReportAction(text); reportAction.reportAction.message[0].taskReportID = taskReportID; @@ -1209,6 +1217,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; @@ -1219,7 +1228,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. @@ -1227,8 +1237,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); + const personalDetails = getPersonalDetailsForAccountID(payerAccountID); + const payerEmail = personalDetails.login; return { // If we're sending money, hasOutstandingIOU should be false hasOutstandingIOU: !isSendingMoney, @@ -1236,8 +1248,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, @@ -1254,12 +1267,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}`]); @@ -1274,6 +1288,7 @@ function buildOptimisticExpenseReport(chatReportID, policyID, payeeEmail, total, policyID, type: CONST.REPORT.TYPE.EXPENSE, ownerEmail: payeeEmail, + ownerAccountID: payeeAccountID, hasOutstandingIOU: true, currency: outputCurrency, @@ -1376,6 +1391,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 { @@ -1383,7 +1399,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), @@ -1421,6 +1437,7 @@ function buildOptimisticReportPreview(reportID, iouReportID, payeeAccountID) { linkedReportID: iouReportID, }, actorEmail: currentUserEmail, + actorAccountID: currentUserAccountID, }; } @@ -1436,7 +1453,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: [ @@ -1449,7 +1466,7 @@ function buildOptimisticTaskReportAction(taskReportID, actionName, message = '') person: [ { style: 'strong', - text: lodashGet(currentUserPersonalDetails, 'displayName', currentUserEmail), + text: lodashGet(currentUserPersonalDetails, 'displayName', currentUserAccountID), type: 'TEXT', }, ], @@ -1464,11 +1481,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 @@ -1483,6 +1501,7 @@ function buildOptimisticChatReport( chatType = '', policyID = CONST.POLICY.OWNER_EMAIL_FAKE, ownerEmail = CONST.REPORT.OWNER_EMAIL_FAKE, + ownerAccountID = CONST.REPORT.OWNER_ACCOUNT_ID_FAKE, isOwnPolicyExpenseChat = false, oldPolicyName = '', visibility = undefined, @@ -1498,6 +1517,7 @@ function buildOptimisticChatReport( isOwnPolicyExpenseChat, isPinned: reportName === CONST.REPORT.WORKSPACE_CHAT_ROOMS.ADMINS, lastActorEmail: '', + lastActorAccountID: 0, lastMessageHtml: '', lastMessageText: null, lastReadTime: currentTime, @@ -1505,9 +1525,10 @@ function buildOptimisticChatReport( notificationPreference, oldPolicyName, ownerEmail: ownerEmail || CONST.REPORT.OWNER_EMAIL_FAKE, + ownerAccountID: ownerAccountID || CONST.REPORT.OWNER_ACCOUNT_ID_FAKE, parentReportActionID, parentReportID, - participants: participantList, + participantAccountIDs: participantList, policyID, reportID: generateReportID(), reportName, @@ -1546,11 +1567,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, }; @@ -1586,11 +1607,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, }; @@ -1609,7 +1630,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: [ { @@ -1632,7 +1653,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(), @@ -1647,11 +1668,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, @@ -1665,14 +1687,32 @@ 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 = { @@ -1699,7 +1739,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. @@ -1707,13 +1748,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, @@ -1756,21 +1799,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; } @@ -1787,7 +1829,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; @@ -1814,12 +1856,12 @@ 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 - if (isDomainRoom(report) && doesDomainHaveApprovedAccountant && hasExpensifyEmails(lodashGet(report, ['participants'], []))) { + if (isDomainRoom(report) && doesDomainHaveApprovedAccountant && hasExpensifyEmails(lodashGet(report, ['participantAccountIDs'], []))) { return true; } // If the room has an assigned guide, it can be seen. - if (hasExpensifyGuidesEmails(lodashGet(report, ['participants'], []))) { + if (hasExpensifyGuidesEmails(lodashGet(report, ['participantAccountIDs'], []))) { return true; } @@ -1842,13 +1884,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. @@ -1857,7 +1898,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; } @@ -1875,7 +1916,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; } @@ -1912,12 +1953,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)); }); } @@ -1931,12 +1972,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)); }); } @@ -1954,7 +1995,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); } /** @@ -2056,11 +2097,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 []; } @@ -2188,7 +2230,6 @@ function getParentReport(report) { export { getAccountIDForLogin, getReportParticipantsTitle, - getPersonalDetailsForLogin, isReportMessageAttachment, findLastAccessedReport, canEditReportAction, @@ -2210,7 +2251,7 @@ export { isPublicAnnounceRoom, isConciergeChatReport, isCurrentUserTheOnlyParticipant, - hasAutomatedExpensifyEmails, + hasAutomatedExpensifyAccountIDs, hasExpensifyGuidesEmails, hasOutstandingIOU, isIOUOwnedByCurrentUser, diff --git a/src/libs/SidebarUtils.js b/src/libs/SidebarUtils.js index eace44054613..31155a11232e 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; @@ -119,7 +125,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); @@ -229,9 +235,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, @@ -252,7 +260,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); @@ -266,6 +274,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); @@ -273,7 +282,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 hasMultipleParticipants = participantPersonalDetailList.length > 1 || result.isChatRoom || result.isPolicyExpenseChat; @@ -295,17 +304,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 && Number(lastActorDetails.accountID) !== currentUserAccountID ? `${lastActorDetails.displayName}: ` : ''; lastMessageText += report ? lastMessageTextFromReport : ''; if (result.isArchivedRoom) { @@ -354,6 +364,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; @@ -365,7 +376,7 @@ function getOptionData(reportID) { result.subtitle = subtitle; result.participantsList = participantPersonalDetailList; - result.icons = ReportUtils.getIcons(report, personalDetails, UserUtils.getAvatar(personalDetail.avatar, personalDetail.login), true); + result.icons = ReportUtils.getIcons(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 e2d9f1c7d82c..4202856c3e75 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 (Number(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 {Number} [accountID] * @param {Boolean} [isNewDot] * @returns {String} */ -function getDefaultAvatarURL(login = '', isNewDot = false) { - if (login === CONST.EMAIL.CONCIERGE) { +function getDefaultAvatarURL(accountID = '', isNewDot = false) { + if (Number(accountID) === CONST.ACCOUNT_ID.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(String(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`; } /** @@ -144,11 +144,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; } /** @@ -156,11 +156,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; } /** @@ -168,11 +168,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; } @@ -184,11 +184,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; } @@ -206,8 +206,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 {Number} + */ +function generateAccountID() { + return Math.floor(Math.random() * 2 ** 21) * 2 ** 32 + Math.floor(Math.random() * 2 ** 32); +} + export { - hashLogin, + hashText, hasLoginListError, hasLoginListInfo, getLoginListBrickRoadIndicator, @@ -218,4 +228,5 @@ export { getAvatarUrl, getSmallSizeAvatar, getFullSizeAvatar, + generateAccountID, }; diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js index eccf2623f7f5..38638e22a464 100644 --- a/src/libs/actions/App.js +++ b/src/libs/actions/App.js @@ -17,12 +17,10 @@ import getCurrentUrl from '../Navigation/currentUrl'; import * as Session from './Session'; let currentUserAccountID; -let currentUserEmail; Onyx.connect({ key: ONYXKEYS.SESSION, callback: (val) => { currentUserAccountID = lodashGet(val, 'accountID', ''); - currentUserEmail = lodashGet(val, 'email', ''); }, }); @@ -35,13 +33,13 @@ Onyx.connect({ let myPersonalDetails; Onyx.connect({ - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, callback: (val) => { - if (!val || !currentUserEmail) { + if (!val || !currentUserAccountID) { return; } - myPersonalDetails = val[currentUserEmail]; + myPersonalDetails = val[currentUserAccountID]; }, }); @@ -289,9 +287,9 @@ function openProfile() { optimisticData: [ { onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, value: { - [currentUserEmail]: { + [currentUserAccountID]: { timezone: newTimezoneData, }, }, @@ -300,9 +298,9 @@ function openProfile() { failureData: [ { onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, value: { - [currentUserEmail]: { + [currentUserAccountID]: { timezone: oldTimezoneData, }, }, diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js index 4a5d77dfba77..e0e4f46ca486 100644 --- a/src/libs/actions/IOU.js +++ b/src/libs/actions/IOU.js @@ -15,6 +15,7 @@ import * as OptionsListUtils from '../OptionsListUtils'; import DateUtils from '../DateUtils'; import TransactionUtils from '../TransactionUtils'; import * as ErrorUtils from '../ErrorUtils'; +import * as UserUtils from '../UserUtils'; const chatReports = {}; const iouReports = {}; @@ -51,6 +52,7 @@ function buildOnyxDataForMoneyRequest( chatCreatedAction, iouCreatedAction, iouAction, + optimisticPersonalDetailListAction, reportPreviewAction, isNewChatReport, isNewIOUReport, @@ -104,6 +106,14 @@ function buildOnyxDataForMoneyRequest( }, ]; + if (isNewChatReport && !_.isEmpty(optimisticPersonalDetailListAction)) { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + value: optimisticPersonalDetailListAction, + }); + } + const successData = [ ...(isNewChatReport ? [ @@ -257,11 +267,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 = Number(participant.accountID); const isPolicyExpenseChat = participant.isPolicyExpenseChat || participant.isOwnPolicyExpenseChat; // STEP 1: Get existing chat report OR build a new optimistic one @@ -275,13 +287,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 +307,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 @@ -325,6 +337,16 @@ function requestMoney(report, amount, currency, payeeEmail, participant, comment iouReport.reportID, ); + // Add optimistic personal details for participant + const optimisticPersonalDetailListAction = { + [payerAccountID]: { + accountID: payerAccountID, + avatar: UserUtils.getDefaultAvatarURL(payerAccountID), + displayName: participant.displayName || payerEmail, + login: participant.login, + }, + }; + let isNewReportPreviewAction = false; let reportPreviewAction = isNewIOUReport ? null : ReportActionsUtils.getReportPreviewAction(chatReport.reportID, iouReport.reportID); if (!reportPreviewAction) { @@ -340,6 +362,7 @@ function requestMoney(report, amount, currency, payeeEmail, participant, comment optimisticCreatedActionForChat, optimisticCreatedActionForIOU, optimisticIOUAction, + optimisticPersonalDetailListAction, reportPreviewAction, isNewChatReport, isNewIOUReport, @@ -380,6 +403,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 +411,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) => Number(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 +526,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 = Number(participant.accountID); if (email === currentUserEmail) { return; } @@ -515,20 +540,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 @@ -560,6 +585,16 @@ function createSplitsAndOnyxData(participants, currentUserLogin, amount, comment oneOnOneIOUReport.reportID, ); + // Add optimistic personal details for new participants + const oneOnOnePersonalDetailListAction = { + [accountID]: { + accountID, + avatar: UserUtils.getDefaultAvatarURL(accountID), + displayName: participant.displayName || email, + login: participant.login, + }, + }; + let isNewOneOnOneReportPreviewAction = false; let oneOnOneReportPreviewAction = ReportActionsUtils.getReportPreviewAction(oneOnOneChatReport.reportID, oneOnOneIOUReport.reportID); if (!oneOnOneReportPreviewAction) { @@ -575,6 +610,7 @@ function createSplitsAndOnyxData(participants, currentUserLogin, amount, comment oneOnOneCreatedActionForChat, oneOnOneCreatedActionForIOU, oneOnOneIOUAction, + oneOnOnePersonalDetailListAction, oneOnOneReportPreviewAction, isNewOneOnOneChatReport, isNewOneOnOneIOUReport, @@ -583,6 +619,7 @@ function createSplitsAndOnyxData(participants, currentUserLogin, amount, comment const splitData = { email, + accountID, amount: splitAmount, iouReportID: oneOnOneIOUReport.reportID, chatReportID: oneOnOneChatReport.reportID, @@ -619,13 +656,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 +686,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 +735,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 +743,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 +854,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 = Number(recipient.accountID); const newIOUReportDetails = JSON.stringify({ amount, currency, requestorEmail: recipientEmail, + requestorAccountID: recipientAccountID, comment, idempotencyKey: Str.guid(), }); @@ -832,13 +873,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 = { @@ -928,6 +969,8 @@ function getSendMoneyParams(report, amount, currency, comment, paymentMethodType }, ]; + let optimisticPersonalDetailListData = {}; + // Now, let's add the data we need just when we are creating a new chat report if (isNewChat) { // Change the method to set for new reports because it doesn't exist yet, is faster, @@ -953,6 +996,20 @@ function getSendMoneyParams(report, amount, currency, comment, paymentMethodType }, }); + // Add optimistic personal details for recipient + optimisticPersonalDetailListData = { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + value: { + [recipientAccountID]: { + accountID: recipientAccountID, + avatar: UserUtils.getDefaultAvatarURL(recipient.accountID), + displayName: recipient.displayName || recipient.login, + login: recipient.login, + }, + }, + }; + // Add an optimistic created action to the optimistic reportActions data optimisticReportActionsData.value[optimisticCreatedAction.reportActionID] = optimisticCreatedAction; @@ -961,6 +1018,9 @@ function getSendMoneyParams(report, amount, currency, comment, paymentMethodType } const optimisticData = [optimisticChatReportData, optimisticIOUReportData, optimisticReportActionsData, optimisticTransactionData]; + if (!_.isEmpty(optimisticPersonalDetailListData)) { + optimisticData.push(optimisticPersonalDetailListData); + } return { params: { @@ -998,6 +1058,12 @@ function getPayMoneyRequestParams(chatReport, iouReport, recipient, paymentMetho iouReport.reportID, true, ); + const optimisticPersonalDetailsListAction = { + accountID: Number(recipient.accountID), + avatar: UserUtils.getDefaultAvatarURL(Number(recipient.accountID)), + displayName: recipient.displayName || recipient.login, + login: recipient.login, + }; const optimisticData = [ { @@ -1044,6 +1110,11 @@ function getPayMoneyRequestParams(chatReport, iouReport, recipient, paymentMetho key: ONYXKEYS.NVP_LAST_PAYMENT_METHOD, value: {[iouReport.policyID]: paymentMethodType}, }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + value: optimisticPersonalDetailsListAction, + }, ]; const successData = [ @@ -1102,11 +1173,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 +1189,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 +1205,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 +1227,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 9c7dde0c7aa4..53680f65a1ec 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,9 +287,9 @@ function updateSelectedTimezone(selectedTimezone) { optimisticData: [ { onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, value: { - [currentUserEmail]: { + [currentUserAccountID]: { 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, }, @@ -408,48 +412,6 @@ function updateAvatar(file) { }, ]; - const accountID = lodashGet(personalDetails, [currentUserEmail, 'accountID'], ''); - if (accountID) { - optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - value: { - [accountID]: { - avatar: file.uri, - errorFields: { - avatar: null, - }, - pendingFields: { - avatar: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - }, - }, - }, - }); - successData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - value: { - [accountID]: { - pendingFields: { - avatar: null, - }, - }, - }, - }); - failureData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - value: { - [accountID]: { - avatar: personalDetails[currentUserEmail].avatar, - pendingFields: { - avatar: null, - }, - }, - }, - }); - } - API.write('UpdateUserAvatar', {file}, {optimisticData, successData, failureData}); } @@ -458,14 +420,14 @@ 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); const optimisticData = [ { onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, value: { - [currentUserEmail]: { + [currentUserAccountID]: { avatar: defaultAvatar, }, }, @@ -474,37 +436,15 @@ function deleteAvatar() { const 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, }, }, }, ]; - const accountID = lodashGet(personalDetails, [currentUserEmail, 'accountID'], ''); - if (accountID) { - optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - value: { - [personalDetails[currentUserEmail].accountID]: { - avatar: defaultAvatar, - }, - }, - }); - failureData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - value: { - [personalDetails[currentUserEmail].accountID]: { - avatar: personalDetails[currentUserEmail].avatar, - }, - }, - }); - } - API.write('DeleteUserAvatar', {}, {optimisticData, failureData}); } @@ -512,8 +452,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..f9092f87c48b 100644 --- a/src/libs/actions/Policy.js +++ b/src/libs/actions/Policy.js @@ -51,13 +51,21 @@ Onyx.connect({ }); let sessionEmail = ''; +let sessionAccountID = 0; Onyx.connect({ key: ONYXKEYS.SESSION, callback: (val) => { sessionEmail = lodashGet(val, 'email', ''); + sessionAccountID = lodashGet(val, 'accountID', 0); }, }); +let personalDetails; +Onyx.connect({ + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + callback: (val) => (personalDetails = val), +}); + /** * Stores in Onyx the policy ID of the last workspace that was accessed by the user * @param {String|null} policyID @@ -185,41 +193,41 @@ 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(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) { + // array passed will be empty, prevent the function from proceeding in that case as there is no one to remove + if (accountIDs.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(accountIDs.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(accountIDs.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(accountIDs.length).fill({errors: ErrorUtils.getMicroSecondOnyxError('workspace.people.error.genericRemove')})), }, ]; API.write( 'DeleteMembersFromWorkspace', { - emailList: members.join(','), + emailList: _.map(accountIDs, (accountID) => personalDetails[accountID].login).join(','), policyID, }, {optimisticData, successData, failureData}, @@ -230,11 +238,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,8 +255,11 @@ function createPolicyExpenseChats(policyID, members, betas) { return workspaceMembersChats; } - _.each(members, (login) => { - const oldChat = ReportUtils.getChatByParticipantsAndPolicy([sessionEmail, login], policyID); + _.each(invitedEmailsToAccountIDs, (accountID, email) => { + const cleanAccountID = Number(accountID); + const login = OptionsListUtils.addSMSDomainIfPhoneNumber(email); + + const oldChat = ReportUtils.getChatByParticipantsAndPolicy([sessionAccountID, cleanAccountID], policyID); // If the chat already exists, we don't want to create a new one - just make sure it's not archived if (oldChat) { @@ -265,7 +276,14 @@ 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, cleanAccountID], + undefined, + CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT, + policyID, + login, + cleanAccountID, + ); const optimisticCreatedAction = ReportUtils.buildOptimisticCreatedReportAction(optimisticReport.ownerEmail); workspaceMembersChats.reportCreationData[login] = { @@ -323,17 +341,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) { - const membersListKey = `${ONYXKEYS.COLLECTION.POLICY_MEMBER_LIST}${policyID}`; - const logins = _.map(memberLogins, (memberLogin) => OptionsListUtils.addSMSDomainIfPhoneNumber(memberLogin)); +function addMembersToWorkspace(invitedEmailsToAccountIDs, welcomeNote, policyID, betas) { + const membersListKey = `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`; + 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 +359,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 +371,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 +384,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 +393,7 @@ function addMembersToWorkspace(memberLogins, welcomeNote, policyID, betas) { ...membersChats.onyxFailureData, ]; + const logins = _.map(_.keys(invitedEmailsToAccountIDs), (memberLogin) => OptionsListUtils.addSMSDomainIfPhoneNumber(memberLogin)); API.write( 'AddMembersToWorkspace', { @@ -699,11 +718,11 @@ function updateWorkspaceCustomUnitAndRate(policyID, currentCustomUnit, newCustom * Removes an error after trying to delete a member * * @param {String} policyID - * @param {String} memberEmail + * @param {Number} accountID */ -function clearDeleteMemberError(policyID, memberEmail) { - Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY_MEMBER_LIST}${policyID}`, { - [memberEmail]: { +function clearDeleteMemberError(policyID, accountID) { + Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`, { + [accountID]: { pendingAction: null, errors: null, }, @@ -714,11 +733,11 @@ function clearDeleteMemberError(policyID, memberEmail) { * Removes an error after trying to add a member * * @param {String} policyID - * @param {String} memberEmail + * @param {Number} accountID */ -function clearAddMemberError(policyID, memberEmail) { - Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY_MEMBER_LIST}${policyID}`, { - [memberEmail]: null, +function clearAddMemberError(policyID, accountID) { + Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`, { + [accountID]: null, }); } @@ -851,9 +870,9 @@ function createWorkspace(ownerEmail = '', makeMeAdmin = false, policyName = '', }, { onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.POLICY_MEMBER_LIST}${policyID}`, + key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`, value: { - [sessionEmail]: { + [sessionAccountID]: { role: CONST.POLICY.ROLE.ADMIN, errors: {}, }, @@ -972,7 +991,7 @@ function createWorkspace(ownerEmail = '', makeMeAdmin = false, policyName = '', failureData: [ { onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.POLICY_MEMBER_LIST}${policyID}`, + key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`, value: null, }, { @@ -1079,8 +1098,12 @@ 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 b481eff7d13f..5551816ee961 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -18,11 +18,12 @@ import Log from '../Log'; import * as ReportUtils from '../ReportUtils'; import DateUtils from '../DateUtils'; import * as ReportActionsUtils from '../ReportActionsUtils'; -import * as OptionsListUtils from '../OptionsListUtils'; import * as CollectionUtils from '../CollectionUtils'; import * as EmojiUtils from '../EmojiUtils'; import * as ErrorUtils from '../ErrorUtils'; +import * as UserUtils from '../UserUtils'; import * as Welcome from './Welcome'; +import * as PersonalDetailsUtils from '../PersonalDetailsUtils'; import SidebarUtils from '../SidebarUtils'; let currentUserEmail; @@ -222,7 +223,9 @@ function addActions(reportID, text = '', file) { const optimisticReport = { lastVisibleActionCreated: currentTime, lastMessageText: lastCommentText, + lastMessageHtml: lastCommentText, lastActorEmail: currentUserEmail, + lastActorAccountID: currentUserAccountID, lastReadTime: currentTime, }; @@ -281,8 +284,8 @@ function addActions(reportID, text = '', file) { parameters.timezone = JSON.stringify(timezone); optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS, - value: {[currentUserEmail]: {timezone}}, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + value: {[currentUserAccountID]: {timezone}}, }); DateUtils.setTimezoneUpdated(); } @@ -327,12 +330,12 @@ function addComment(reportID, text) { * If a chat with the passed reportID is not found, we will create a chat based on the passed participantList * * @param {String} reportID - * @param {Array} participantList The list of users that are included in a new chat, not including the user creating it + * @param {Array} participantLoginList The list of users that are included in a new chat, not including the user creating it * @param {Object} newReportObject The optimistic report object created when making a new chat, saved as optimistic data * @param {String} parentReportActionID The parent report action that a thread was created from (only passed for new threads) * @param {Boolean} isFromDeepLink Whether or not this report is being opened from a deep link */ -function openReport(reportID, participantList = [], newReportObject = {}, parentReportActionID = '0', isFromDeepLink = false) { +function openReport(reportID, participantLoginList = [], newReportObject = {}, parentReportActionID = '0', isFromDeepLink = false) { const optimisticReportData = { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, @@ -372,7 +375,7 @@ function openReport(reportID, participantList = [], newReportObject = {}, parent const params = { reportID, - emailList: participantList ? participantList.join(',') : '', + emailList: participantLoginList ? participantLoginList.join(',') : '', parentReportActionID, }; @@ -413,6 +416,23 @@ function openReport(reportID, participantList = [], newReportObject = {}, parent value: {[optimisticCreatedAction.reportActionID]: {pendingAction: null}}, }); + // Add optimistic personal details for new participants + const optimisticPersonalDetails = {}; + _.map(participantLoginList, (login, index) => { + const accountID = newReportObject.participantAccountIDs[index]; + optimisticPersonalDetails[accountID] = { + login, + accountID, + avatar: UserUtils.getDefaultAvatarURL(accountID), + displayName: login, + }; + }); + onyxData.optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + value: optimisticPersonalDetails, + }); + // Add the createdReportActionID parameter to the API call params.createdReportActionID = optimisticCreatedAction.reportActionID; @@ -445,19 +465,19 @@ 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} userLogins list of user logins to start a chat report with. */ function navigateToAndOpenReport(userLogins) { - const formattedUserLogins = _.map(userLogins, (login) => OptionsListUtils.addSMSDomainIfPhoneNumber(login).toLowerCase()); + const participantAccountIDs = PersonalDetailsUtils.getAccountIDsByLogins(userLogins); let newChat = {}; - const chat = ReportUtils.getChatByParticipants(formattedUserLogins); + const chat = ReportUtils.getChatByParticipants(participantAccountIDs); if (!chat) { - newChat = ReportUtils.buildOptimisticChatReport(formattedUserLogins); + newChat = ReportUtils.buildOptimisticChatReport(participantAccountIDs); } const reportID = chat ? chat.reportID : newChat.reportID; // We want to pass newChat here because if anything is passed in that param (even an existing chat), we will try to create a chat on the server - openReport(reportID, newChat.participants, newChat); + openReport(reportID, userLogins, newChat); Navigation.dismissModal(reportID); } @@ -474,15 +494,15 @@ function navigateToAndOpenChildReport(childReportID = '0', parentReportAction = openReport(childReportID); Navigation.navigate(ROUTES.getReportRoute(childReportID)); } else { - const participants = _.uniq([currentUserEmail, parentReportAction.actorEmail]); - const formattedUserLogins = _.map(participants, (login) => OptionsListUtils.addSMSDomainIfPhoneNumber(login).toLowerCase()); + const participantAccountIDs = _.uniq([currentUserAccountID, Number(parentReportAction.actorAccountID)]); const parentReport = allReports[parentReportID]; const newChat = ReportUtils.buildOptimisticChatReport( - formattedUserLogins, + participantAccountIDs, lodashGet(parentReportAction, ['message', 0, 'text']), lodashGet(parentReport, 'chatType', ''), lodashGet(parentReport, 'policyID', CONST.POLICY.OWNER_EMAIL_FAKE), CONST.POLICY.OWNER_EMAIL_FAKE, + CONST.POLICY.OWNER_ACCOUNT_ID_FAKE, false, '', undefined, @@ -491,7 +511,8 @@ function navigateToAndOpenChildReport(childReportID = '0', parentReportAction = parentReportID, ); - openReport(newChat.reportID, newChat.participants, newChat, parentReportAction.reportActionID); + const participantLogins = PersonalDetailsUtils.getLoginsByAccountIDs(newChat.participantAccountIDs); + openReport(newChat.reportID, participantLogins, newChat, parentReportAction.reportActionID); Navigation.navigate(ROUTES.getReportRoute(newChat.reportID)); } } @@ -1131,13 +1152,14 @@ function navigateToConciergeChat() { */ function addPolicyReport(policy, reportName, visibility) { // The participants include the current user (admin) and the employees. Participants must not be empty. - const participants = _.unique([currentUserEmail, ..._.pluck(policy.employeeList, 'email')]); + const participants = _.unique([currentUserAccountID, ..._.pluck(policy.employeeList, 'accountID')]); const policyReport = ReportUtils.buildOptimisticChatReport( participants, reportName, CONST.REPORT.CHAT_TYPE.POLICY_ROOM, policy.id, CONST.REPORT.OWNER_EMAIL_FAKE, + CONST.REPORT.OWNER_ACCOUNT_ID_FAKE, false, '', visibility, @@ -1430,8 +1452,8 @@ function getOptimisticDataForReportActionUpdate(originalReportAction, message, r /** * Returns true if the accountID has reacted to the report action (with the given skin tone). - * @param {String} accountID - * @param {Array} users + * @param {Number} accountID + * @param {Array} users * @param {Number} [skinTone] * @returns {boolean} */ @@ -1440,12 +1462,12 @@ function hasAccountIDReacted(accountID, users, skinTone) { _.find(users, (user) => { let userAccountID; if (typeof user === 'object') { - userAccountID = `${user.accountID}`; + userAccountID = user.accountID; } else { - userAccountID = `${user}`; + userAccountID = user; } - return userAccountID === `${accountID}` && (skinTone == null ? true : user.skinTone === skinTone); + return userAccountID === accountID && (skinTone == null ? true : user.skinTone === skinTone); }) !== undefined ); } diff --git a/src/libs/actions/Task.js b/src/libs/actions/Task.js index a5a76b59afb3..f60aa051035d 100644 --- a/src/libs/actions/Task.js +++ b/src/libs/actions/Task.js @@ -23,29 +23,38 @@ 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, assignee, 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, + assignee, + 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, assignee, assigneeAccountID, `Created a task: ${title}`, parentReportID); const currentTime = DateUtils.getDBTime(); @@ -55,6 +64,7 @@ function createTaskAndNavigate(currentUserEmail, parentReportID, title, descript lastVisibleActionCreated: currentTime, lastMessageText: lastCommentText, lastActorEmail: currentUserEmail, + lastActorAccountID: currentUserAccountID, lastReadTime: currentTime, }; @@ -125,6 +135,7 @@ function createTaskAndNavigate(currentUserEmail, parentReportID, title, descript lastVisibleActionCreated: currentTime, lastMessageText: lastAssigneeCommentText, lastActorEmail: currentUserEmail, + lastActorAccountID: currentUserAccountID, lastReadTime: currentTime, }; @@ -159,6 +170,7 @@ function createTaskAndNavigate(currentUserEmail, parentReportID, title, descript title: optimisticTaskReport.reportName, description: optimisticTaskReport.description, assignee, + assigneeAccountID, assigneeChatReportID, assigneeChatReportActionID: optimisticAssigneeAddComment ? optimisticAssigneeAddComment.reportAction.reportActionID : 0, }, @@ -237,6 +249,7 @@ function reopenTask(taskReportID, taskTitle) { lastVisibleActionCreated: reopenedTaskReportAction.created, lastMessageText: message, lastActorEmail: reopenedTaskReportAction.actorEmail, + lastActorAccountID: reopenedTaskReportAction.actorAccountID, lastReadTime: reopenedTaskReportAction.created, }, }, @@ -275,15 +288,16 @@ function reopenTask(taskReportID, taskTitle) { } /** - * @function editTask * @param {object} report * @param {string} ownerEmail - * @param {{title?: string, description?: string, assignee?:string}} editedTask - * @returns {object} action - * + * @param {Number} ownerAccountID + * @param {Object} editedTask + * @param {String} editedTask.title + * @param {String} editedTask.description + * @param {String} editedTask.assignee + * @param {Number} editedTask.assigneeAccountID */ - -function editTaskAndNavigate(report, ownerEmail, {title, description, assignee}) { +function editTaskAndNavigate(report, ownerEmail, ownerAccountID, {title, description, assignee, assigneeAccountID = 0}) { // Create the EditedReportAction on the task const editTaskReportAction = ReportUtils.buildOptimisticEditedTaskReportAction(ownerEmail); @@ -294,10 +308,16 @@ 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; + if (assigneeAccountID && assigneeAccountID !== report.managerID) { + assigneeChatReportID = ReportUtils.getChatByParticipants([assigneeAccountID]).reportID; if (assigneeChatReportID !== report.parentReportID.toString()) { - optimisticAssigneeAddComment = ReportUtils.buildOptimisticTaskCommentReportAction(report.reportID, reportName, assignee, `Assigned a task to you: ${reportName}`); + optimisticAssigneeAddComment = ReportUtils.buildOptimisticTaskCommentReportAction( + report.reportID, + reportName, + assignee, + assigneeAccountID, + `Assigned a task to you: ${reportName}`, + ); } } @@ -313,7 +333,7 @@ function editTaskAndNavigate(report, ownerEmail, {title, description, assignee}) value: { reportName, description: reportDescription, - managerEmail: assignee || report.managerEmail, + managerID: assigneeAccountID || report.managerID, }, }, ]; @@ -327,7 +347,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}, }, ]; @@ -338,7 +358,7 @@ function editTaskAndNavigate(report, ownerEmail, {title, description, assignee}) const optimisticAssigneeReport = { lastVisibleActionCreated: currentTime, lastMessageText: lastAssigneeCommentText, - lastActorEmail: ownerEmail, + lastActorAccountID: ownerAccountID, lastReadTime: currentTime, }; @@ -369,6 +389,7 @@ function editTaskAndNavigate(report, ownerEmail, {title, description, assignee}) title: reportName, description: reportDescription, assignee: assignee || report.managerEmail, + assigneeAccountID: assigneeAccountID || report.managerID, editedTaskReportActionID: editTaskReportAction.reportActionID, assigneeChatReportActionID: optimisticAssigneeAddComment ? optimisticAssigneeAddComment.reportAction.reportActionID : 0, }, @@ -392,7 +413,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()}); @@ -445,16 +465,23 @@ function setAssigneeValueWithParentReportID(reportID) { * If there is no existing chat, it creates an optimistic chat report * It also sets the shareDestination as that chat report if a share destination isn't already set * @param {string} assignee + * @param {Number} assigneeAccountID * @param {string} shareDestination * @param {boolean} isCurrentUser */ -function setAssigneeValue(assignee, shareDestination, isCurrentUser = false) { +function setAssigneeValue(assignee, assigneeAccountID, shareDestination, isCurrentUser = false) { + let newAssigneeAccountID = Number(assigneeAccountID); + + // Generate optimistic accountID if this is a brand new user account that hasn't been created yet + if (!newAssigneeAccountID) { + newAssigneeAccountID = UserUtils.generateAccountID(); + } if (!isCurrentUser) { let newChat = {}; - const chat = ReportUtils.getChatByParticipants([assignee]); + const chat = ReportUtils.getChatByParticipants([newAssigneeAccountID]); if (!chat) { - newChat = ReportUtils.buildOptimisticChatReport([assignee]); + newChat = ReportUtils.buildOptimisticChatReport([newAssigneeAccountID]); } const reportID = chat ? chat.reportID : newChat.reportID; @@ -466,14 +493,13 @@ function setAssigneeValue(assignee, shareDestination, isCurrentUser = false) { } // This is only needed for creation of a new task and so it should only be stored locally - Onyx.merge(ONYXKEYS.TASK, {assignee}); + Onyx.merge(ONYXKEYS.TASK, {assignee, newAssigneeAccountID}); } /** * 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}); @@ -503,7 +529,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, @@ -555,6 +581,7 @@ function cancelTask(taskReportID, taskTitle, originalStateNum, originalStatusNum lastVisibleActionCreated: optimisticCancelReportAction.created, lastMessageText: message, lastActorEmail: optimisticCancelReportAction.actorEmail, + lastActorAccountID: optimisticCancelReportAction.actorAccountID, }, }, { diff --git a/src/libs/actions/User.js b/src/libs/actions/User.js index 12d98c664e7e..524444e08820 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,14 +785,12 @@ function setContactMethodAsDefault(newDefaultContactMethod) { }, { onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, value: { - [newDefaultContactMethod]: { - ...myPersonalDetails, + [currentUserAccountID]: { login: newDefaultContactMethod, displayName: PersonalDetails.getDisplayName(newDefaultContactMethod, myPersonalDetails), }, - [oldDefaultContactMethod]: null, }, }, ]; @@ -833,10 +831,9 @@ function setContactMethodAsDefault(newDefaultContactMethod) { }, { onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, value: { - [newDefaultContactMethod]: null, - [oldDefaultContactMethod]: {...myPersonalDetails}, + [currentUserAccountID]: {...myPersonalDetails}, }, }, ]; diff --git a/src/pages/DetailsPage.js b/src/pages/DetailsPage.js index 1cb2c0ef76f0..4f78e9c3b869 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)) { @@ -131,7 +143,7 @@ class DetailsPage extends React.PureComponent { @@ -144,7 +156,7 @@ class DetailsPage extends React.PureComponent { @@ -191,7 +203,7 @@ class DetailsPage extends React.PureComponent { Report.navigateToAndOpenReport([details.login])} + onPress={() => Report.navigateToAndOpenReport([login])} 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..a0244f72fae7 100755 --- a/src/pages/NewChatPage.js +++ b/src/pages/NewChatPage.js @@ -205,12 +205,11 @@ class NewChatPage extends Component { if (!this.props.isGroupChat) { return; } - - const userLogins = _.pluck(this.state.selectedOptions, 'login'); - if (userLogins.length < 1) { + const logins = _.pluck(this.state.selectedOptions, 'login'); + if (logins.length < 1) { return; } - Report.navigateToAndOpenReport(userLogins); + Report.navigateToAndOpenReport(logins); } render() { @@ -270,7 +269,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 31d6aeef422e..ac0d1db4d14b 100755 --- a/src/pages/ProfilePage.js +++ b/src/pages/ProfilePage.js @@ -108,7 +108,7 @@ function ProfilePage(props) { // If we have a reportID param this means that we // arrived here via the ParticipantsPage and should be allowed to navigate back to it - const shouldShowLocalTime = !ReportUtils.hasAutomatedExpensifyEmails([login]) && !_.isEmpty(timezone); + const shouldShowLocalTime = !ReportUtils.hasAutomatedExpensifyAccountIDs([accountID]) && !_.isEmpty(timezone); let pronouns = lodashGet(details, 'pronouns', ''); if (pronouns && pronouns.startsWith(CONST.PRONOUNS.PREFIX)) { @@ -142,7 +142,7 @@ function ProfilePage(props) { @@ -155,7 +155,7 @@ function ProfilePage(props) { diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js index 28449426d922..19f78e08c604 100644 --- a/src/pages/ReportDetailsPage.js +++ b/src/pages/ReportDetailsPage.js @@ -70,7 +70,7 @@ function 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) { @@ -123,7 +123,7 @@ function 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 ? ( @@ -200,7 +200,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 5994e3830705..da065dadcb77 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, index) => { + const userPersonalDetail = lodashGet(personalDetails, accountID, {displayName: personalDetails.displayName || 'Hidden', avatar: ''}); + const userLogin = Str.removeSMSDomain(userPersonalDetail.login || '') || 'Hidden'; 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: `${index}-${userLogin}`, + 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..17ca3a0c0be4 100755 --- a/src/pages/SearchPage.js +++ b/src/pages/SearchPage.js @@ -206,7 +206,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/ShareCodePage.js b/src/pages/ShareCodePage.js index 456fbe78d6db..232e225fb187 100644 --- a/src/pages/ShareCodePage.js +++ b/src/pages/ShareCodePage.js @@ -60,7 +60,7 @@ class ShareCodePage extends React.Component { url={url} title={isReport ? this.props.report.reportName : this.props.currentUserPersonalDetails.displayName} subtitle={isReport ? subtitle : this.props.session.email} - logo={isReport ? expensifyLogo : UserUtils.getAvatarUrl(this.props.currentUserPersonalDetails.avatar, this.props.currentUserPersonalDetails.login)} + logo={isReport ? expensifyLogo : UserUtils.getAvatarUrl(this.props.currentUserPersonalDetails.avatar, this.props.currentUserPersonalDetails.accountID)} logoRatio={isReport ? CONST.QR.EXPENSIFY_LOGO_SIZE_RATIO : CONST.QR.DEFAULT_LOGO_SIZE_RATIO} logoMarginRatio={isReport ? CONST.QR.EXPENSIFY_LOGO_MARGIN_RATIO : CONST.QR.DEFAULT_LOGO_MARGIN_RATIO} /> diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index 79278452b8a1..b12733f64ef8 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -71,8 +71,8 @@ const defaultProps = { }; function 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); @@ -82,8 +82,8 @@ function HeaderView(props) { const reportHeaderData = !isTaskReport && !isThread && props.report.parentReportID ? props.parentReport : props.report; const title = ReportUtils.getReportName(reportHeaderData); const subtitle = ReportUtils.getChatRoomSubtitle(reportHeaderData); - const isConcierge = participants.length === 1 && _.contains(participants, CONST.EMAIL.CONCIERGE); - const isAutomatedExpensifyAccount = participants.length === 1 && ReportUtils.hasAutomatedExpensifyEmails(participants); + const isConcierge = participants.length === 1 && _.contains(participants, CONST.ACCOUNT_ID.CONCIERGE); + const isAutomatedExpensifyAccount = participants.length === 1 && ReportUtils.hasAutomatedExpensifyAccountIDs(participants); const guideCalendarLink = lodashGet(props.account, 'guideCalendarLink'); // We hide the button when we are chatting with an automated Expensify account since it's not possible to contact diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index c18116ce5635..d3fb665ecb48 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -392,7 +392,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/ReactionList/PopoverReactionList.js b/src/pages/home/report/ReactionList/PopoverReactionList.js index f0dd786dff76..3cff316561b0 100644 --- a/src/pages/home/report/ReactionList/PopoverReactionList.js +++ b/src/pages/home/report/ReactionList/PopoverReactionList.js @@ -184,7 +184,7 @@ class PopoverReactionList extends React.Component { }; } const emojiCount = selectedReaction.users.length; - const reactionUsers = _.map(selectedReaction.users, (sender) => sender.accountID.toString()); + const reactionUsers = _.map(selectedReaction.users, (sender) => sender.accountID); const emoji = _.find(emojis, (e) => e.name === selectedReaction.emoji); const emojiCodes = EmojiUtils.getUniqueEmojiCodes(emoji, selectedReaction.users); const hasUserReacted = Report.hasAccountIDReacted(this.props.currentUserPersonalDetails.accountID, reactionUsers); diff --git a/src/pages/home/report/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose.js index ec031fe02e32..344b14289c0d 100644 --- a/src/pages/home/report/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose.js @@ -29,7 +29,7 @@ import ReportActionComposeFocusManager from '../../../libs/ReportActionComposeFo import participantPropTypes from '../../../components/participantPropTypes'; import ParticipantLocalTime from './ParticipantLocalTime'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes, withCurrentUserPersonalDetailsDefaultProps} from '../../../components/withCurrentUserPersonalDetails'; -import {withNetwork, withPersonalDetails} from '../../../components/OnyxProvider'; +import {withNetwork} from '../../../components/OnyxProvider'; import * as User from '../../../libs/actions/User'; import Tooltip from '../../../components/Tooltip'; import EmojiPickerButton from '../../../components/EmojiPicker/EmojiPickerButton'; @@ -415,7 +415,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 []; } @@ -893,11 +893,11 @@ 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; + ReportUtils.canShowReportRecipientLocalTime(this.props.personalDetails, this.props.report, this.props.currentUserPersonalDetails.accountID) && !this.props.isComposerFullSize; // Prevents focusing and showing the keyboard while the drawer is covering the chat. const isBlockedFromConcierge = ReportUtils.chatIncludesConcierge(this.props.report) && User.isBlockedFromConcierge(this.props.blockedFromConcierge); @@ -1220,7 +1220,6 @@ export default compose( withNavigationFocus, withLocalize, withNetwork(), - withPersonalDetails(), withCurrentUserPersonalDetails, withKeyboardState, withOnyx({ @@ -1244,7 +1243,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 c1aaf88b14c4..802d0d9aa164 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -323,7 +323,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 ( @@ -360,7 +360,7 @@ function ReportActionItem(props) { numberOfReplies={numberOfThreadReplies} mostRecentReply={`${props.action.childLastVisibleActionCreated}`} isHovered={hovered} - icons={ReportUtils.getIconsForParticipants(oldestFourEmails, props.personalDetails)} + icons={ReportUtils.getIconsForParticipants(oldestFourAccountIDs, props.personalDetailsList)} /> )} diff --git a/src/pages/home/report/ReportActionItemCreated.js b/src/pages/home/report/ReportActionItemCreated.js index 2d09636090bf..680ef678a53d 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/ReportActionItemFragment.js b/src/pages/home/report/ReportActionItemFragment.js index edcf0167f711..65558110ba45 100644 --- a/src/pages/home/report/ReportActionItemFragment.js +++ b/src/pages/home/report/ReportActionItemFragment.js @@ -22,7 +22,7 @@ import UserDetailsTooltip from '../../../components/UserDetailsTooltip'; const propTypes = { /** Users accountID */ - accountID: PropTypes.string.isRequired, + accountID: PropTypes.number.isRequired, /** The message fragment needing to be displayed */ fragment: reportActionFragmentPropTypes.isRequired, diff --git a/src/pages/home/report/ReportActionItemMessage.js b/src/pages/home/report/ReportActionItemMessage.js index e6bcdffe1e8d..14a5eea2676e 100644 --- a/src/pages/home/report/ReportActionItemMessage.js +++ b/src/pages/home/report/ReportActionItemMessage.js @@ -41,7 +41,7 @@ function ReportActionItemMessage(props) { attachmentInfo={props.action.attachmentInfo} pendingAction={props.action.pendingAction} source={lodashGet(props.action, 'originalMessage.source')} - accountID={String(props.action.actorAccountID)} + accountID={props.action.actorAccountID} loading={props.action.isLoading} style={props.style} /> diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js index e6f7cbc95410..fa299858c338 100644 --- a/src/pages/home/report/ReportActionItemSingle.js +++ b/src/pages/home/report/ReportActionItemSingle.js @@ -28,7 +28,7 @@ const propTypes = { action: PropTypes.shape(reportActionPropTypes).isRequired, /** All of the personalDetails */ - personalDetails: PropTypes.objectOf(personalDetailsPropType), + personalDetailsList: PropTypes.objectOf(personalDetailsPropType), /** Styles for the outermost View */ // eslint-disable-next-line react/forbid-prop-types @@ -53,7 +53,7 @@ const propTypes = { }; const defaultProps = { - personalDetails: {}, + personalDetailsList: {}, wrapperStyles: [styles.chatItem], showHeader: true, shouldShowSubscriptAvatar: false, @@ -66,9 +66,10 @@ const showUserDetails = (accountID) => { }; function 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 actorEmail = lodashGet(props.action, 'actorEmail', '').replace(CONST.REGEX.MERGED_ACCOUNT_PREFIX, ''); + const actorAccountID = props.action.actorAccountID; + const {avatar, displayName, pendingFields} = props.personalDetailsList[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 @@ function ReportActionItemSingle(props) { style={[styles.alignSelfStart, styles.mr3]} onPressIn={ControlSelection.block} onPressOut={ControlSelection.unblock} - onPress={() => showUserDetails(accountID)} + onPress={() => showUserDetails(actorAccountID)} > {props.shouldShowSubscriptAvatar ? ( @@ -100,7 +101,7 @@ function ReportActionItemSingle(props) { noMargin /> ) : ( - + showUserDetails(accountID)} + onPress={() => showUserDetails(actorAccountID)} > {_.map(personArray, (fragment, index) => ( @@ -267,14 +270,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; }, @@ -320,7 +324,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 8b08c36359b5..6d3e5e3d19b0 100644 --- a/src/pages/iou/MoneyRequestModal.js +++ b/src/pages/iou/MoneyRequestModal.js @@ -249,20 +249,20 @@ function 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,19 +276,51 @@ function 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], + [ + amount, + props.iou.comment, + props.currentUserPersonalDetails.login, + props.currentUserPersonalDetails.accountID, + props.hasMultipleParticipants, + props.iou.selectedCurrencyCode, + props.report, + props.route, + ], ); const currentStep = steps[currentStepIndex]; @@ -372,7 +404,7 @@ function 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 +443,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 f892e57c3e06..7ea455111d66 100644 --- a/src/pages/iou/SplitBillDetailsPage.js +++ b/src/pages/iou/SplitBillDetailsPage.js @@ -64,10 +64,10 @@ function getReportID(route) { function 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..49f569925692 100755 --- a/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js +++ b/src/pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsSelector.js @@ -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..638cb1250f64 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), @@ -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]; } @@ -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..cf02b34f3fbd 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.number, + // 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 01fa80dfd468..b7364ce623fa 100755 --- a/src/pages/settings/InitialSettingsPage.js +++ b/src/pages/settings/InitialSettingsPage.js @@ -100,8 +100,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, @@ -119,7 +119,7 @@ const defaultProps = { bankAccountList: {}, cardList: {}, loginList: {}, - policyMembers: {}, + allPolicyMembers: {}, ...withCurrentUserPersonalDetailsDefaultProps, }; @@ -171,7 +171,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; @@ -332,7 +332,7 @@ class InitialSettingsPage extends React.Component { @@ -398,8 +398,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/settings/Profile/ProfilePage.js b/src/pages/settings/Profile/ProfilePage.js index 562b862a7fc6..e59b98785ec8 100755 --- a/src/pages/settings/Profile/ProfilePage.js +++ b/src/pages/settings/Profile/ProfilePage.js @@ -89,7 +89,7 @@ function ProfilePage(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, {description: values.description}); + TaskUtils.editTaskAndNavigate(props.task.report, props.session.email, props.session.accountID, {description: values.description}); }, [props], ); diff --git a/src/pages/tasks/TaskShareDestinationSelectorModal.js b/src/pages/tasks/TaskShareDestinationSelectorModal.js index 2073ca92a233..cb2f500195a0 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 c8ac41c87c88..2a13033886de 100644 --- a/src/pages/tasks/TaskTitlePage.js +++ b/src/pages/tasks/TaskTitlePage.js @@ -55,8 +55,7 @@ function TaskTitlePage(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, {title: values.title}); + TaskUtils.editTaskAndNavigate(props.task.report, props.session.email, props.session.accountID, {title: values.title}); }, [props], ); diff --git a/src/pages/workspace/WorkspaceInitialPage.js b/src/pages/workspace/WorkspaceInitialPage.js index 7ced21b0e833..0ceb52066cfe 100644 --- a/src/pages/workspace/WorkspaceInitialPage.js +++ b/src/pages/workspace/WorkspaceInitialPage.js @@ -92,7 +92,7 @@ function 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 4debb088a43a..c3b53e98a09a 100644 --- a/src/pages/workspace/WorkspaceInviteMessagePage.js +++ b/src/pages/workspace/WorkspaceInviteMessagePage.js @@ -32,11 +32,11 @@ const personalDetailsPropTypes = PropTypes.shape({ login: PropTypes.string.isRequired, /** The URL of the person's avatar (there should already be a default avatar if - the person doesn't have their own avatar uploaded yet) */ - avatar: PropTypes.string.isRequired, + the person doesn't have their own avatar uploaded yet, except for anon users) */ + avatar: PropTypes.string, /** This is either the user's full name, or their login if full name is an empty string */ - displayName: PropTypes.string.isRequired, + displayName: PropTypes.string, }); const propTypes = { @@ -46,7 +46,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({ @@ -65,7 +65,7 @@ const defaultProps = { ...policyDefaultProps, personalDetails: {}, betas: [], - invitedMembersDraft: [], + invitedEmailsToAccountIDsDraft: {}, }; class WorkspaceInviteMessagePage extends React.Component { @@ -114,7 +114,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)); } @@ -141,7 +141,7 @@ class WorkspaceInviteMessagePage extends React.Component { validate() { const errorFields = {}; - if (_.isEmpty(this.props.invitedMembersDraft)) { + if (_.isEmpty(this.props.invitedEmailsToAccountIDsDraft)) { errorFields.welcomeMessage = 'workspace.inviteMessage.inviteNoMembersError'; } return errorFields; @@ -189,7 +189,7 @@ class WorkspaceInviteMessagePage extends React.Component { @@ -227,12 +227,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 4eadf6c5898d..5d8c1f36621c 100644 --- a/src/pages/workspace/WorkspaceInvitePage.js +++ b/src/pages/workspace/WorkspaceInvitePage.js @@ -24,17 +24,18 @@ 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) */ login: PropTypes.string.isRequired, /** The URL of the person's avatar (there should already be a default avatar if - the person doesn't have their own avatar uploaded yet) */ - avatar: PropTypes.string.isRequired, + the person doesn't have their own avatar uploaded yet, except for anon users) */ + avatar: PropTypes.string, /** This is either the user's full name, or their login if full name is an empty string */ - displayName: PropTypes.string.isRequired, + displayName: PropTypes.string, }); const propTypes = { @@ -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,25 @@ 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]; + // Exclude any expensify emails or valid policy members from the invite options + const memberEmailsToExclude = [...CONST.EXPENSIFY_EMAILS]; + _.each(this.props.policyMembers, (policyMember, accountID) => { + // Policy members that are pending delete or have errors are not valid and we should show them in the invite options (don't exclude them). + if (policyMember.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || !_.isEmpty(policyMember.errors)) { + return; + } + const memberEmail = lodashGet(this.props.personalDetails, `[${accountID}].login`); + if (!memberEmail) { + return; + } + memberEmailsToExclude.push(memberEmail); + }); + return memberEmailsToExclude; } /** @@ -224,13 +229,16 @@ class WorkspaceInvitePage extends React.Component { return; } - const logins = _.map(this.state.selectedOptions, (option) => option.login); - const filteredLogins = _.chain(logins) - .map((login) => login.toLowerCase().trim()) - .compact() - .uniq() - .value(); - Policy.setWorkspaceInviteMembersDraft(this.props.route.params.policyID, filteredLogins); + const invitedEmailsToAccountIDs = {}; + _.each(this.state.selectedOptions, (option) => { + const login = option.login || ''; + const accountID = lodashGet(option, 'participantsList[0].accountID'); + if (!login.toLowerCase().trim() || !accountID) { + return; + } + invitedEmailsToAccountIDs[login] = Number(accountID); + }); + Policy.setWorkspaceInviteMembersDraft(this.props.route.params.policyID, invitedEmailsToAccountIDs); Navigation.navigate(ROUTES.getWorkspaceInviteMessageRoute(this.props.route.params.policyID)); } @@ -320,7 +328,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 0c1116db2ee4..7274dc3ec662 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,12 @@ 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 +120,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))); } /** @@ -183,8 +182,9 @@ 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); + const accountIDsToRemove = _.without(this.state.selectedEmployees, this.props.session.accountID); + + Policy.removeMembers(accountIDsToRemove, this.props.route.params.policyID); this.setState({ selectedEmployees: [], isRemoveMembersConfirmModalVisible: false, @@ -226,32 +226,32 @@ class WorkspaceMembersPage extends React.Component { /** * Toggle user from the selectedEmployees list * - * @param {String} login + * @param {String} accountID * @param {String} pendingAction * */ - toggleUser(login, pendingAction) { + toggleUser(accountID, pendingAction) { if (pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) { return; } // Add or remove the user if the checkbox is enabled - if (_.contains(this.state.selectedEmployees, login)) { - this.removeUser(login); + if (_.contains(this.state.selectedEmployees, accountID)) { + this.removeUser(accountID); } else { - this.addUser(login); + this.addUser(accountID); } } /** * Add user from the selectedEmployees list * - * @param {String} login + * @param {String} accountID */ - addUser(login) { + addUser(accountID) { this.setState( (prevState) => ({ - selectedEmployees: [...prevState.selectedEmployees, login], + selectedEmployees: [...prevState.selectedEmployees, accountID], }), () => this.validate(), ); @@ -260,12 +260,12 @@ class WorkspaceMembersPage extends React.Component { /** * Remove user from the selectedEmployees list * - * @param {String} login + * @param {String} accountID */ - removeUser(login) { + removeUser(accountID) { this.setState( (prevState) => ({ - selectedEmployees: _.without(prevState.selectedEmployees, login), + selectedEmployees: _.without(prevState.selectedEmployees, accountID), }), () => this.validate(), ); @@ -278,9 +278,9 @@ class WorkspaceMembersPage extends React.Component { */ dismissError(item) { if (item.pendingAction === 'delete') { - Policy.clearDeleteMemberError(this.props.route.params.policyID, item.login); + Policy.clearDeleteMemberError(this.props.route.params.policyID, item.accountID); } else { - Policy.clearAddMemberError(this.props.route.params.policyID, item.login); + Policy.clearAddMemberError(this.props.route.params.policyID, item.accountID); } } @@ -320,7 +320,7 @@ class WorkspaceMembersPage extends React.Component { */ renderItem({item}) { const hasError = !_.isEmpty(item.errors) || this.state.errors[item.login]; - const isChecked = _.contains(this.state.selectedEmployees, item.login); + const isChecked = _.contains(this.state.selectedEmployees, item.accountID); return ( this.dismissError(item)} @@ -329,7 +329,7 @@ class WorkspaceMembersPage extends React.Component { > this.toggleUser(item.login, item.pendingAction)} + onPress={() => this.toggleUser(item.accountID, item.pendingAction)} accessibilityRole="checkbox" accessibilityState={{ checked: isChecked, @@ -341,7 +341,7 @@ class WorkspaceMembersPage extends React.Component { > this.toggleUser(item.login, item.pendingAction)} + onPress={() => this.toggleUser(item.accountID, item.pendingAction)} /> this.toggleUser(item.login, item.pendingAction)} + onSelectRow={() => this.toggleUser(item.accountID, item.pendingAction)} /> {(this.props.session.email === item.login || item.role === 'admin') && ( @@ -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, @@ -408,7 +411,7 @@ class WorkspaceMembersPage extends React.Component { if (member.login === this.props.session.email || member.login === this.props.policy.owner || member.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) { return; } - removableMembers[member.login] = member; + removableMembers[member.accountID] = member; }); const policyID = lodashGet(this.props.route, 'params.policyID'); const policyName = lodashGet(this.props.policy, 'name'); @@ -471,9 +474,7 @@ class WorkspaceMembersPage extends React.Component { _.contains(this.state.selectedEmployees, memberEmail)) - } + isChecked={!_.isEmpty(removableMembers) && _.every(_.keys(removableMembers), (accountID) => _.contains(this.state.selectedEmployees, accountID))} onPress={() => this.toggleAllUsers(removableMembers)} /> @@ -513,7 +514,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 ee57b95b6f51..f88ced6fc9fb 100755 --- a/src/pages/workspace/WorkspacesListPage.js +++ b/src/pages/workspace/WorkspacesListPage.js @@ -55,8 +55,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({ @@ -72,7 +72,7 @@ const propTypes = { const defaultProps = { policies: {}, - policyMembers: {}, + allPolicyMembers: {}, reimbursementAccount: {}, userWallet: { currentBalance: 0, @@ -131,7 +131,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), @@ -218,8 +218,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 17a7f0efc72d..b1659ea2b7a6 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..112377f373d5 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,17 @@ 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, @@ -907,7 +926,7 @@ describe('actions/IOU', () => { 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 +942,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 +1168,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/DateUtilsTest.js b/tests/unit/DateUtilsTest.js index cb1e8d839fa1..da2c9456e205 100644 --- a/tests/unit/DateUtilsTest.js +++ b/tests/unit/DateUtilsTest.js @@ -12,8 +12,8 @@ describe('DateUtils', () => { Onyx.init({ keys: ONYXKEYS, initialKeyStates: { - [ONYXKEYS.SESSION]: {email: 'current@user.com'}, - [ONYXKEYS.PERSONAL_DETAILS]: {'current@user.com': {timezone: {selected: 'Etc/UTC'}}}, + [ONYXKEYS.SESSION]: {accountID: 999}, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: {999: {timezone: {selected: 'Etc/UTC'}}}, }, }); return waitForPromisesToResolve(); 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 849d16b97ea2..f33fdc86ef77 100644 --- a/tests/unit/ReportUtilsTest.js +++ b/tests/unit/ReportUtilsTest.js @@ -10,29 +10,30 @@ 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', - accountID: 1, }, - 'floki@vikings.net': { + 2: { + accountID: 2, login: 'floki@vikings.net', displayName: 'floki@vikings.net', - accountID: 2, }, - 'lagertha@vikings.net': { + 3: { + accountID: 3, displayName: 'Lagertha Lothbrok', firstName: 'Lagertha', login: 'lagertha@vikings.net', pronouns: 'She/her', - accountID: 3, }, - '+18332403627@expensify.sms': { + 4: { + accountID: 4, login: '+18332403627@expensify.sms', displayName: '(833) 240-3627', - accountID: 4, }, }; const policy = { @@ -45,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, }); @@ -61,7 +62,7 @@ describe('ReportUtils', () => { displayName: 'Ragnar Lothbrok', login: 'ragnar@vikings.net', avatar: { - testUri: '../../../assets/images/avatars/user/default-avatar_16.svg', + testUri: '../../../assets/images/avatars/user/default-avatar_2.svg', }, accountID: 1, pronouns: undefined, @@ -69,7 +70,7 @@ describe('ReportUtils', () => { { displayName: 'floki@vikings.net', avatar: { - testUri: '../../../assets/images/avatars/user/default-avatar_24.svg', + testUri: '../../../assets/images/avatars/user/default-avatar_3.svg', }, login: 'floki@vikings.net', accountID: 2, @@ -78,7 +79,7 @@ describe('ReportUtils', () => { { displayName: 'Lagertha Lothbrok', avatar: { - testUri: '../../../assets/images/avatars/user/default-avatar_11.svg', + testUri: '../../../assets/images/avatars/user/default-avatar_4.svg', }, login: 'lagertha@vikings.net', accountID: 3, @@ -87,7 +88,7 @@ describe('ReportUtils', () => { { displayName: '(833) 240-3627', avatar: { - testUri: '../../../assets/images/avatars/user/default-avatar_15.svg', + testUri: '../../../assets/images/avatars/user/default-avatar_5.svg', }, login: '+18332403627@expensify.sms', accountID: 4, @@ -102,7 +103,7 @@ describe('ReportUtils', () => { displayName: 'Ragnar', login: 'ragnar@vikings.net', avatar: { - testUri: '../../../assets/images/avatars/user/default-avatar_16.svg', + testUri: '../../../assets/images/avatars/user/default-avatar_2.svg', }, accountID: 1, pronouns: undefined, @@ -110,7 +111,7 @@ describe('ReportUtils', () => { { displayName: 'floki@vikings.net', avatar: { - testUri: '../../../assets/images/avatars/user/default-avatar_24.svg', + testUri: '../../../assets/images/avatars/user/default-avatar_3.svg', }, login: 'floki@vikings.net', accountID: 2, @@ -119,7 +120,7 @@ describe('ReportUtils', () => { { displayName: 'Lagertha', avatar: { - testUri: '../../../assets/images/avatars/user/default-avatar_11.svg', + testUri: '../../../assets/images/avatars/user/default-avatar_4.svg', }, login: 'lagertha@vikings.net', accountID: 3, @@ -128,7 +129,7 @@ describe('ReportUtils', () => { { displayName: '(833) 240-3627', avatar: { - testUri: '../../../assets/images/avatars/user/default-avatar_15.svg', + testUri: '../../../assets/images/avatars/user/default-avatar_5.svg', }, login: '+18332403627@expensify.sms', accountID: 4, @@ -144,6 +145,7 @@ describe('ReportUtils', () => { expect( ReportUtils.getReportName({ participants: [currentUserEmail, 'ragnar@vikings.net'], + participantAccountIDs: [currentUserAccountID, 1], }), ).toBe('Ragnar Lothbrok'); }); @@ -152,6 +154,7 @@ describe('ReportUtils', () => { expect( ReportUtils.getReportName({ participants: [currentUserEmail, 'floki@vikings.net'], + participantAccountIDs: [currentUserAccountID, 2], }), ).toBe('floki@vikings.net'); }); @@ -160,6 +163,7 @@ describe('ReportUtils', () => { expect( ReportUtils.getReportName({ participants: [currentUserEmail, '+18332403627@expensify.sms'], + participantAccountIDs: [currentUserAccountID, 4], }), ).toBe('(833) 240-3627'); }); @@ -169,6 +173,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'); }); @@ -228,6 +233,7 @@ describe('ReportUtils', () => { policyID: policy.policyID, isOwnPolicyExpenseChat: true, ownerEmail: 'ragnar@vikings.net', + ownerAccountID: 1, }), ).toBe('Vikings Policy'); }); @@ -239,6 +245,7 @@ describe('ReportUtils', () => { policyID: policy.policyID, isOwnPolicyExpenseChat: false, ownerEmail: 'ragnar@vikings.net', + ownerAccountID: 1, }), ).toBe('Ragnar Lothbrok'); }); @@ -248,6 +255,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, @@ -304,7 +312,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 = { @@ -316,7 +324,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 = { @@ -326,10 +334,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 = { @@ -340,10 +348,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 = { @@ -354,10 +362,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); }); }); @@ -365,8 +373,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, }, }); @@ -376,20 +385,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); }); }); @@ -403,7 +412,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); }, ); @@ -411,33 +420,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(); }