From 0b6c83cfc212965700b3760fcee7707eee947705 Mon Sep 17 00:00:00 2001 From: Pavlo Tsimura Date: Mon, 26 Feb 2024 23:23:25 +0100 Subject: [PATCH 1/4] Set childReportID of the optimistic IOU report action --- src/libs/ReportUtils.ts | 1 + src/libs/actions/IOU.ts | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 8813501e2b3f..a84d0088a199 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -175,6 +175,7 @@ type OptimisticIOUReportAction = Pick< | 'pendingAction' | 'receipt' | 'whisperedToAccountIDs' + | 'childReportID' >; type ReportRouteParams = { diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index ca43d92bde83..933b7e9b070d 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -892,6 +892,7 @@ function getMoneyRequestInformation( currentTime, ); const optimisticTransactionThread = ReportUtils.buildTransactionThread(iouAction, iouReport.reportID); + iouAction.childReportID = optimisticTransactionThread.reportID; const optimisticCreatedActionForTransactionThread = ReportUtils.buildOptimisticCreatedReportAction(payeeEmail); let reportPreviewAction = shouldCreateNewMoneyRequestReport ? null : ReportActionsUtils.getReportPreviewAction(chatReport.reportID, iouReport.reportID); @@ -1890,6 +1891,7 @@ function createSplitsAndOnyxData( // Create optimistic transactionThread const optimisticTransactionThread = ReportUtils.buildTransactionThread(oneOnOneIOUAction, oneOnOneIOUReport.reportID); + oneOnOneIOUAction.childReportID = optimisticTransactionThread.reportID; const optimisticCreatedActionForTransactionThread = ReportUtils.buildOptimisticCreatedReportAction(currentUserEmailForIOUSplit); // STEP 5: Build Onyx Data @@ -2494,6 +2496,7 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA } const optimisticTransactionThread = ReportUtils.buildTransactionThread(oneOnOneIOUAction, oneOnOneIOUReport.reportID); + oneOnOneIOUAction.childReportID = optimisticTransactionThread.reportID; const optimisticCreatedActionForTransactionThread = ReportUtils.buildOptimisticCreatedReportAction(currentUserEmailForIOUSplit); const [oneOnOneOptimisticData, oneOnOneSuccessData, oneOnOneFailureData] = buildOnyxDataForMoneyRequest( @@ -3213,6 +3216,7 @@ function getSendMoneyParams( const reportPreviewAction = ReportUtils.buildOptimisticReportPreview(chatReport, optimisticIOUReport); const optimisticTransactionThread = ReportUtils.buildTransactionThread(optimisticIOUReportAction, optimisticIOUReport.reportID); + optimisticIOUReportAction.childReportID = optimisticTransactionThread.reportID; const optimisticCreatedActionForTransactionThread = ReportUtils.buildOptimisticCreatedReportAction(recipientEmail); // Change the method to set for new reports because it doesn't exist yet, is faster, From ab668704000a4f0f2d456963f1a3cb1da3e3a1e4 Mon Sep 17 00:00:00 2001 From: Pavlo Tsimura Date: Sun, 3 Mar 2024 21:59:33 +0100 Subject: [PATCH 2/4] Fix unavailable workspace for optimistic thread reports --- .../ReportActionItem/MoneyRequestAction.tsx | 12 +----------- src/libs/ReportUtils.ts | 9 ++++----- src/libs/actions/IOU.ts | 10 +++++----- .../home/report/ContextMenu/ContextMenuActions.tsx | 7 ------- 4 files changed, 10 insertions(+), 28 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestAction.tsx b/src/components/ReportActionItem/MoneyRequestAction.tsx index 9e169b23391a..05891311ba6d 100644 --- a/src/components/ReportActionItem/MoneyRequestAction.tsx +++ b/src/components/ReportActionItem/MoneyRequestAction.tsx @@ -8,9 +8,7 @@ import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; import * as IOUUtils from '@libs/IOUUtils'; import Navigation from '@libs/Navigation/Navigation'; -import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; -import * as ReportUtils from '@libs/ReportUtils'; import type {ContextMenuAnchor} from '@pages/home/report/ContextMenu/ReportActionContextMenu'; import * as Report from '@userActions/Report'; import CONST from '@src/CONST'; @@ -91,15 +89,7 @@ function MoneyRequestAction({ return; } - // If the childReportID is not present, we need to create a new thread - const childReportID = action?.childReportID; - if (!childReportID) { - const thread = ReportUtils.buildTransactionThread(action, requestReportID); - const userLogins = PersonalDetailsUtils.getLoginsByAccountIDs(thread.participantAccountIDs ?? []); - Report.openReport(thread.reportID, userLogins, thread, action.reportActionID); - Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(thread.reportID)); - return; - } + const childReportID = action?.childReportID ?? '0'; Report.openReport(childReportID); Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(childReportID)); }; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index ead850ddeef5..4b4a359fabdc 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3800,16 +3800,15 @@ function buildOptimisticTaskReport( * A helper method to create transaction thread * * @param reportAction - the parent IOU report action from which to create the thread - * - * @param moneyRequestReportID - the reportID which the report action belong to + * @param moneyRequestReport - the report which the report action belongs to */ -function buildTransactionThread(reportAction: OnyxEntry, moneyRequestReportID: string): OptimisticChatReport { +function buildTransactionThread(reportAction: OnyxEntry, moneyRequestReport: Report): OptimisticChatReport { const participantAccountIDs = [...new Set([currentUserAccountID, Number(reportAction?.actorAccountID)])].filter(Boolean) as number[]; return buildOptimisticChatReport( participantAccountIDs, getTransactionReportName(reportAction), undefined, - getReport(moneyRequestReportID)?.policyID ?? CONST.POLICY.OWNER_EMAIL_FAKE, + moneyRequestReport.policyID, CONST.POLICY.OWNER_ACCOUNT_ID_FAKE, false, '', @@ -3817,7 +3816,7 @@ function buildTransactionThread(reportAction: OnyxEntry Date: Sun, 3 Mar 2024 22:12:33 +0100 Subject: [PATCH 3/4] Fix unavailable workspace for optimistic thread reports --- .../home/report/ContextMenu/ContextMenuActions.tsx | 1 - tests/actions/IOUTest.js | 14 +++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx index 00880ae6256e..066b4cdaae1f 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx @@ -16,7 +16,6 @@ import getAttachmentDetails from '@libs/fileDownload/getAttachmentDetails'; import ModifiedExpenseMessage from '@libs/ModifiedExpenseMessage'; import Navigation from '@libs/Navigation/Navigation'; import Permissions from '@libs/Permissions'; -import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; diff --git a/tests/actions/IOUTest.js b/tests/actions/IOUTest.js index ec32be4d241c..64c8edb134b1 100644 --- a/tests/actions/IOUTest.js +++ b/tests/actions/IOUTest.js @@ -1509,7 +1509,7 @@ describe('actions/IOU', () => { }), ) .then(() => { - thread = ReportUtils.buildTransactionThread(iouAction, iouReport.reportID); + thread = ReportUtils.buildTransactionThread(iouAction, iouReport); Onyx.set(`report_${thread.reportID}`, thread); return waitForBatchedUpdates(); }) @@ -1643,7 +1643,7 @@ describe('actions/IOU', () => { }), ) .then(() => { - thread = ReportUtils.buildTransactionThread(iouAction, iouReport.reportID); + thread = ReportUtils.buildTransactionThread(iouAction, iouReport); Onyx.set(`report_${thread.reportID}`, thread); return waitForBatchedUpdates(); }) @@ -2214,7 +2214,7 @@ describe('actions/IOU', () => { jest.advanceTimersByTime(10); // Given a transaction thread - thread = ReportUtils.buildTransactionThread(createIOUAction, IOU_REPORT_ID); + thread = ReportUtils.buildTransactionThread(createIOUAction, {reportID: IOU_REPORT_ID}); expect(thread.notificationPreference).toBe(CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN); @@ -2295,7 +2295,7 @@ describe('actions/IOU', () => { jest.advanceTimersByTime(10); // Given a transaction thread - thread = ReportUtils.buildTransactionThread(createIOUAction, IOU_REPORT_ID); + thread = ReportUtils.buildTransactionThread(createIOUAction, {reportID: IOU_REPORT_ID}); Onyx.connect({ key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${thread.reportID}`, @@ -2374,7 +2374,7 @@ describe('actions/IOU', () => { await waitForBatchedUpdates(); // Given a transaction thread - thread = ReportUtils.buildTransactionThread(createIOUAction); + thread = ReportUtils.buildTransactionThread(createIOUAction, {reportID: IOU_REPORT_ID}); expect(thread.notificationPreference).toBe(CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN); @@ -2460,7 +2460,7 @@ describe('actions/IOU', () => { // Given a thread report jest.advanceTimersByTime(10); - thread = ReportUtils.buildTransactionThread(createIOUAction, IOU_REPORT_ID); + thread = ReportUtils.buildTransactionThread(createIOUAction, {reportID: IOU_REPORT_ID}); expect(thread.notificationPreference).toBe(CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN); @@ -2686,7 +2686,7 @@ describe('actions/IOU', () => { // Given a thread report jest.advanceTimersByTime(10); - thread = ReportUtils.buildTransactionThread(createIOUAction, IOU_REPORT_ID); + thread = ReportUtils.buildTransactionThread(createIOUAction, {reportID: IOU_REPORT_ID}); expect(thread.notificationPreference).toBe(CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN); From fd7af5969cdfc3f163f9c299fc0529822796b118 Mon Sep 17 00:00:00 2001 From: Pavlo Tsimura Date: Sun, 10 Mar 2024 12:31:13 +0100 Subject: [PATCH 4/4] Unify the logic for building optimistic Money Request entities --- src/libs/ReportUtils.ts | 55 ++++++++++++++++++++++++++- src/libs/actions/IOU.ts | 83 ++++++++++++++--------------------------- 2 files changed, 83 insertions(+), 55 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 922dcbacab8e..814490da97cd 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3097,7 +3097,6 @@ function getIOUReportActionMessage(iouReportID: string, type: string, total: num * @param [receipt] * @param [isOwnPolicyExpenseChat] - Whether this is an expense report create from the current user's policy expense chat */ - function buildOptimisticIOUReportAction( type: ValueOf, amount: number, @@ -3846,6 +3845,59 @@ function buildTransactionThread(reportAction: OnyxEntry, + amount: number, + currency: string, + comment: string, + payeeEmail: string, + participants: Participant[], + transactionID: string, + paymentType?: PaymentMethodType, + isSettlingUp = false, + isSendMoneyFlow = false, + receipt: Receipt = {}, + isOwnPolicyExpenseChat = false, +): [OptimisticCreatedReportAction, OptimisticIOUReportAction, OptimisticChatReport, OptimisticCreatedReportAction] { + const iouActionCreationTime = DateUtils.getDBTime(); + + // The `CREATED` action must be optimistically generated before the IOU action so that it won't appear after the IOU action in the chat. + const createdActionForIOUReport = buildOptimisticCreatedReportAction(payeeEmail, DateUtils.subtractMillisecondsFromDateTime(iouActionCreationTime, 1)); + const iouAction = buildOptimisticIOUReportAction( + type, + amount, + currency, + comment, + participants, + transactionID, + paymentType, + iouReport.reportID, + isSettlingUp, + isSendMoneyFlow, + receipt, + isOwnPolicyExpenseChat, + iouActionCreationTime, + ); + + // Create optimistic transactionThread and the `CREATED` action for it + const transactionThread = buildTransactionThread(iouAction, iouReport); + const createdActionForTransactionThread = buildOptimisticCreatedReportAction(payeeEmail); + + // The IOU action and the transactionThread are co-dependent as parent-child, so we need to link them together + iouAction.childReportID = transactionThread.reportID; + + return [createdActionForIOUReport, iouAction, transactionThread, createdActionForTransactionThread]; +} + function isUnread(report: OnyxEntry): boolean { if (!report) { return false; @@ -5166,6 +5218,7 @@ export { buildOptimisticSubmittedReportAction, buildOptimisticExpenseReport, buildOptimisticIOUReportAction, + buildOptimisticMoneyRequestEntities, buildOptimisticReportPreview, buildOptimisticModifiedExpenseReportAction, buildOptimisticCancelPaymentReportAction, diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 9b4a2ff2a087..fc181da50bd8 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -908,27 +908,22 @@ function getMoneyRequestInformation( // 4. The transaction thread, which requires the iouAction, and CREATED action for the transaction thread // 5. REPORTPREVIEW action for the chatReport // Note: The CREATED action for the IOU report must be optimistically generated before the IOU action so there's no chance that it appears after the IOU action in the chat - const currentTime = DateUtils.getDBTime(); const optimisticCreatedActionForChat = ReportUtils.buildOptimisticCreatedReportAction(payeeEmail); - const optimisticCreatedActionForIOU = ReportUtils.buildOptimisticCreatedReportAction(payeeEmail, DateUtils.subtractMillisecondsFromDateTime(currentTime, 1)); - const iouAction = ReportUtils.buildOptimisticIOUReportAction( + const [optimisticCreatedActionForIOUReport, iouAction, optimisticTransactionThread, optimisticCreatedActionForTransactionThread] = ReportUtils.buildOptimisticMoneyRequestEntities( + iouReport, CONST.IOU.REPORT_ACTION_TYPE.CREATE, amount, currency, comment, + payeeEmail, [participant], optimisticTransaction.transactionID, undefined, - iouReport.reportID, false, false, receiptObject, false, - currentTime, ); - const optimisticTransactionThread = ReportUtils.buildTransactionThread(iouAction, iouReport); - iouAction.childReportID = optimisticTransactionThread.reportID; - const optimisticCreatedActionForTransactionThread = ReportUtils.buildOptimisticCreatedReportAction(payeeEmail); let reportPreviewAction = shouldCreateNewMoneyRequestReport ? null : ReportActionsUtils.getReportPreviewAction(chatReport.reportID, iouReport.reportID); if (reportPreviewAction) { @@ -965,7 +960,7 @@ function getMoneyRequestInformation( iouReport, optimisticTransaction, optimisticCreatedActionForChat, - optimisticCreatedActionForIOU, + optimisticCreatedActionForIOUReport, iouAction, optimisticPersonalDetailListAction, reportPreviewAction, @@ -989,7 +984,7 @@ function getMoneyRequestInformation( transaction: optimisticTransaction, iouAction, createdChatReportActionID: isNewChatReport ? optimisticCreatedActionForChat.reportActionID : '0', - createdIOUReportActionID: shouldCreateNewMoneyRequestReport ? optimisticCreatedActionForIOU.reportActionID : '0', + createdIOUReportActionID: shouldCreateNewMoneyRequestReport ? optimisticCreatedActionForIOUReport.reportActionID : '0', reportPreviewAction, transactionThreadReportID: optimisticTransactionThread.reportID, createdReportActionIDForThread: optimisticCreatedActionForTransactionThread.reportActionID, @@ -1884,25 +1879,18 @@ function createSplitsAndOnyxData( // 1. CREATED action for the chatReport // 2. CREATED action for the iouReport // 3. IOU action for the iouReport - // 4. REPORTPREVIEW action for the chatReport - // Note: The CREATED action for the IOU report must be optimistically generated before the IOU action so there's no chance that it appears after the IOU action in the chat - const currentTime = DateUtils.getDBTime(); + // 4. Transaction Thread and the CREATED action for it + // 5. REPORTPREVIEW action for the chatReport const oneOnOneCreatedActionForChat = ReportUtils.buildOptimisticCreatedReportAction(currentUserEmailForIOUSplit); - const oneOnOneCreatedActionForIOU = ReportUtils.buildOptimisticCreatedReportAction(currentUserEmailForIOUSplit, DateUtils.subtractMillisecondsFromDateTime(currentTime, 1)); - const oneOnOneIOUAction = ReportUtils.buildOptimisticIOUReportAction( + const [oneOnOneCreatedActionForIOU, oneOnOneIOUAction, optimisticTransactionThread, optimisticCreatedActionForTransactionThread] = ReportUtils.buildOptimisticMoneyRequestEntities( + oneOnOneIOUReport, CONST.IOU.REPORT_ACTION_TYPE.CREATE, splitAmount, currency, comment, + currentUserEmailForIOUSplit, [participant], oneOnOneTransaction.transactionID, - undefined, - oneOnOneIOUReport.reportID, - undefined, - undefined, - undefined, - undefined, - currentTime, ); // Add optimistic personal details for new participants @@ -1933,11 +1921,6 @@ function createSplitsAndOnyxData( // Add tag to optimistic policy recently used tags when a participant is a workspace const optimisticPolicyRecentlyUsedTags = isPolicyExpenseChat ? Policy.buildOptimisticPolicyRecentlyUsedTags(participant.policyID, tag) : {}; - // Create optimistic transactionThread - const optimisticTransactionThread = ReportUtils.buildTransactionThread(oneOnOneIOUAction, oneOnOneIOUReport); - oneOnOneIOUAction.childReportID = optimisticTransactionThread.reportID; - const optimisticCreatedActionForTransactionThread = ReportUtils.buildOptimisticCreatedReportAction(currentUserEmailForIOUSplit); - // STEP 5: Build Onyx Data const [oneOnOneOptimisticData, oneOnOneSuccessData, oneOnOneFailureData] = buildOnyxDataForMoneyRequest( oneOnOneChatReport, @@ -2520,16 +2503,16 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA ); const oneOnOneCreatedActionForChat = ReportUtils.buildOptimisticCreatedReportAction(currentUserEmailForIOUSplit); - const oneOnOneCreatedActionForIOU = ReportUtils.buildOptimisticCreatedReportAction(currentUserEmailForIOUSplit); - const oneOnOneIOUAction = ReportUtils.buildOptimisticIOUReportAction( + const [oneOnOneCreatedActionForIOU, oneOnOneIOUAction, optimisticTransactionThread, optimisticCreatedActionForTransactionThread] = ReportUtils.buildOptimisticMoneyRequestEntities( + oneOnOneIOUReport, CONST.IOU.REPORT_ACTION_TYPE.CREATE, splitAmount, currency ?? '', updatedTransaction.comment.comment ?? '', + currentUserEmailForIOUSplit, [participant], oneOnOneTransaction.transactionID, undefined, - oneOnOneIOUReport?.reportID, ); let oneOnOneReportPreviewAction = ReportActionsUtils.getReportPreviewAction(oneOnOneChatReport?.reportID ?? '', oneOnOneIOUReport?.reportID ?? ''); @@ -2539,10 +2522,6 @@ function completeSplitBill(chatReportID: string, reportAction: OnyxTypes.ReportA oneOnOneReportPreviewAction = ReportUtils.buildOptimisticReportPreview(oneOnOneChatReport, oneOnOneIOUReport, '', oneOnOneTransaction); } - const optimisticTransactionThread = ReportUtils.buildTransactionThread(oneOnOneIOUAction, oneOnOneIOUReport); - oneOnOneIOUAction.childReportID = optimisticTransactionThread.reportID; - const optimisticCreatedActionForTransactionThread = ReportUtils.buildOptimisticCreatedReportAction(currentUserEmailForIOUSplit); - const [oneOnOneOptimisticData, oneOnOneSuccessData, oneOnOneFailureData] = buildOnyxDataForMoneyRequest( oneOnOneChatReport, oneOnOneIOUReport, @@ -3250,27 +3229,23 @@ function getSendMoneyParams( value: optimisticTransaction, }; - // Note: The created action must be optimistically generated before the IOU action so there's no chance that the created action appears after the IOU action in the chat - const optimisticCreatedAction = ReportUtils.buildOptimisticCreatedReportAction(recipientEmail); - const optimisticIOUReportAction = ReportUtils.buildOptimisticIOUReportAction( - CONST.IOU.REPORT_ACTION_TYPE.PAY, - amount, - currency, - comment, - [recipient], - optimisticTransaction.transactionID, - paymentMethodType, - optimisticIOUReport.reportID, - false, - true, - ); + const [optimisticCreatedActionForIOUReport, optimisticIOUReportAction, optimisticTransactionThread, optimisticCreatedActionForTransactionThread] = + ReportUtils.buildOptimisticMoneyRequestEntities( + optimisticIOUReport, + CONST.IOU.REPORT_ACTION_TYPE.PAY, + amount, + currency, + comment, + recipientEmail, + [recipient], + optimisticTransaction.transactionID, + paymentMethodType, + false, + true, + ); const reportPreviewAction = ReportUtils.buildOptimisticReportPreview(chatReport, optimisticIOUReport); - const optimisticTransactionThread = ReportUtils.buildTransactionThread(optimisticIOUReportAction, optimisticIOUReport); - optimisticIOUReportAction.childReportID = optimisticTransactionThread.reportID; - const optimisticCreatedActionForTransactionThread = ReportUtils.buildOptimisticCreatedReportAction(recipientEmail); - // Change the method to set for new reports because it doesn't exist yet, is faster, // and we need the data to be available when we navigate to the chat page const optimisticChatReportData: OnyxUpdate = isNewChat @@ -3444,7 +3419,7 @@ function getSendMoneyParams( if (optimisticChatReportActionsData.value) { // Add an optimistic created action to the optimistic chat reportActions data - optimisticChatReportActionsData.value[optimisticCreatedAction.reportActionID] = optimisticCreatedAction; + optimisticChatReportActionsData.value[optimisticCreatedActionForIOUReport.reportActionID] = optimisticCreatedActionForIOUReport; } } else { failureData.push({ @@ -3480,7 +3455,7 @@ function getSendMoneyParams( paymentMethodType, transactionID: optimisticTransaction.transactionID, newIOUReportDetails, - createdReportActionID: isNewChat ? optimisticCreatedAction.reportActionID : '0', + createdReportActionID: isNewChat ? optimisticCreatedActionForIOUReport.reportActionID : '0', reportPreviewReportActionID: reportPreviewAction.reportActionID, transactionThreadReportID: optimisticTransactionThread.reportID, createdReportActionIDForThread: optimisticCreatedActionForTransactionThread.reportActionID,