From d4666513581137739ec7c2cf02cdd23fc3da3004 Mon Sep 17 00:00:00 2001 From: bartlomiej obudzinski Date: Mon, 9 Mar 2026 15:33:02 +0100 Subject: [PATCH 01/13] perf: remove draftTransactions collection subscriptions from expense creation flow --- .../BaseFloatingCameraButton.tsx | 3 +-- .../Modal/EmployeeTestDriveModal.tsx | 2 -- src/hooks/useReceiptScanDrop.tsx | 3 --- src/libs/actions/IOU/index.ts | 11 ++++------ src/libs/actions/QuickActionNavigation.ts | 10 +++++----- src/pages/Share/SubmitDetailsPage.tsx | 4 ---- .../useAttachmentUploadValidation.ts | 2 -- .../FloatingActionButtonAndPopover.tsx | 18 ++++++++--------- .../iou/request/DistanceRequestStartPage.tsx | 3 --- src/pages/iou/request/IOURequestStartPage.tsx | 3 --- tests/actions/IOUTest.ts | 20 ------------------- 11 files changed, 18 insertions(+), 61 deletions(-) diff --git a/src/components/FloatingCameraButton/BaseFloatingCameraButton.tsx b/src/components/FloatingCameraButton/BaseFloatingCameraButton.tsx index 61aec9e812fb3..736161b21e7bb 100644 --- a/src/components/FloatingCameraButton/BaseFloatingCameraButton.tsx +++ b/src/components/FloatingCameraButton/BaseFloatingCameraButton.tsx @@ -36,7 +36,6 @@ function BaseFloatingCameraButton({icon}: BaseFloatingCameraButtonProps) { const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID); const [activePolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID}`); const [session] = useOnyx(ONYXKEYS.SESSION, {selector: sessionSelector}); - const [allTransactionDrafts] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT); const [userBillingGraceEndPeriods] = useOnyx(ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_USER_BILLING_GRACE_PERIOD_END); const reportID = useMemo(() => generateReportID(), []); @@ -61,7 +60,7 @@ function BaseFloatingCameraButton({icon}: BaseFloatingCameraButtonProps) { const quickActionReportID = policyChatForActivePolicy?.reportID ?? reportID; Tab.setSelectedTab(CONST.TAB.IOU_REQUEST_TYPE, CONST.IOU.REQUEST_TYPE.SCAN); - startMoneyRequest(CONST.IOU.TYPE.CREATE, quickActionReportID, CONST.IOU.REQUEST_TYPE.SCAN, !!policyChatForActivePolicy?.reportID, undefined, allTransactionDrafts, true); + startMoneyRequest(CONST.IOU.TYPE.CREATE, quickActionReportID, CONST.IOU.REQUEST_TYPE.SCAN, !!policyChatForActivePolicy?.reportID, undefined, true); }); }; diff --git a/src/components/TestDrive/Modal/EmployeeTestDriveModal.tsx b/src/components/TestDrive/Modal/EmployeeTestDriveModal.tsx index d12f46d459d0b..58d8e12d509ac 100644 --- a/src/components/TestDrive/Modal/EmployeeTestDriveModal.tsx +++ b/src/components/TestDrive/Modal/EmployeeTestDriveModal.tsx @@ -49,7 +49,6 @@ function EmployeeTestDriveModal() { const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const personalPolicy = usePersonalPolicy(); const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); - const [draftTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT); const hasOnlyPersonalPolicies = useMemo(() => hasOnlyPersonalPoliciesUtil(allPolicies), [allPolicies]); const onBossEmailChange = useCallback((value: string) => { @@ -86,7 +85,6 @@ function EmployeeTestDriveModal() { currentDate, currentUserPersonalDetails, hasOnlyPersonalPolicies, - draftTransactions, }); setMoneyRequestReceipt(transactionID, source, filename, true, CONST.TEST_RECEIPT.FILE_TYPE, false, true); diff --git a/src/hooks/useReceiptScanDrop.tsx b/src/hooks/useReceiptScanDrop.tsx index bdb496b10c271..782b4a21d16c1 100644 --- a/src/hooks/useReceiptScanDrop.tsx +++ b/src/hooks/useReceiptScanDrop.tsx @@ -33,8 +33,6 @@ function useReceiptScanDrop() { const [activePolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID}`); const [personalPolicyID] = useOnyx(ONYXKEYS.PERSONAL_POLICY_ID); const [personalPolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${personalPolicyID}`); - const [draftTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT); - const newReportID = generateReportID(); const [newReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${newReportID}`); const [newParentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${newReport?.parentReportID}`); @@ -51,7 +49,6 @@ function useReceiptScanDrop() { currentDate, currentUserPersonalDetails, hasOnlyPersonalPolicies, - draftTransactions, }); const newReceiptFiles: ReceiptFile[] = []; diff --git a/src/libs/actions/IOU/index.ts b/src/libs/actions/IOU/index.ts index 388901c16f77b..c23d7a2cc2206 100644 --- a/src/libs/actions/IOU/index.ts +++ b/src/libs/actions/IOU/index.ts @@ -299,7 +299,6 @@ type InitMoneyRequestParams = { lastSelectedDistanceRates?: OnyxEntry; currentUserPersonalDetails: CurrentUserPersonalDetails; hasOnlyPersonalPolicies: boolean; - draftTransactions: OnyxCollection; }; type MoneyRequestInformation = { @@ -1234,7 +1233,6 @@ function initMoneyRequest({ lastSelectedDistanceRates, currentUserPersonalDetails, hasOnlyPersonalPolicies, - draftTransactions, }: InitMoneyRequestParams) { // Generate a brand new transactionID const newTransactionID = CONST.IOU.OPTIMISTIC_TRANSACTION_ID; @@ -1245,7 +1243,7 @@ function initMoneyRequest({ const created = currentDate || format(new Date(), 'yyyy-MM-dd'); // We remove draft transactions created during multi scanning if there are some - removeDraftTransactions(true, draftTransactions); + removeDraftTransactions(true); // in case we have to re-init money request, but the IOU request type is the same with the old draft transaction, // we should keep most of the existing data by using the ONYX MERGE operation @@ -1353,8 +1351,8 @@ function createDraftTransaction(transaction: OnyxTypes.Transaction) { Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transaction.transactionID}`, newTransaction); } -function clearMoneyRequest(transactionID: string, skipConfirmation = false, draftTransactions?: OnyxCollection) { - removeDraftTransactions(undefined, draftTransactions); +function clearMoneyRequest(transactionID: string, skipConfirmation = false) { + removeDraftTransactions(); Onyx.set(`${ONYXKEYS.COLLECTION.SKIP_CONFIRMATION}${transactionID}`, skipConfirmation); } @@ -1364,7 +1362,6 @@ function startMoneyRequest( requestType?: IOURequestType, skipConfirmation = false, backToReport?: string, - draftTransactions?: OnyxCollection, isFromFloatingActionButton?: boolean, ) { const sourceRoute = Navigation.getActiveRoute(); @@ -1378,7 +1375,7 @@ function startMoneyRequest( [CONST.TELEMETRY.ATTRIBUTE_ROUTE_FROM]: sourceRoute || 'unknown', }, }); - clearMoneyRequest(CONST.IOU.OPTIMISTIC_TRANSACTION_ID, skipConfirmation, draftTransactions); + clearMoneyRequest(CONST.IOU.OPTIMISTIC_TRANSACTION_ID, skipConfirmation); if (isFromFloatingActionButton) { Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${CONST.IOU.OPTIMISTIC_TRANSACTION_ID}`, {isFromFloatingActionButton}); } diff --git a/src/libs/actions/QuickActionNavigation.ts b/src/libs/actions/QuickActionNavigation.ts index d662b5a12f046..84ea68a3684c3 100644 --- a/src/libs/actions/QuickActionNavigation.ts +++ b/src/libs/actions/QuickActionNavigation.ts @@ -48,15 +48,15 @@ function navigateToQuickAction(params: NavigateToQuickActionParams) { case CONST.QUICK_ACTIONS.REQUEST_MANUAL: case CONST.QUICK_ACTIONS.REQUEST_SCAN: case CONST.QUICK_ACTIONS.PER_DIEM: - selectOption(() => startMoneyRequest(CONST.IOU.TYPE.SUBMIT, reportID, requestType, true, undefined, undefined, isFromFloatingActionButton), true); + selectOption(() => startMoneyRequest(CONST.IOU.TYPE.SUBMIT, reportID, requestType, true, undefined, isFromFloatingActionButton), true); break; case CONST.QUICK_ACTIONS.SPLIT_MANUAL: case CONST.QUICK_ACTIONS.SPLIT_SCAN: case CONST.QUICK_ACTIONS.SPLIT_DISTANCE: - selectOption(() => startMoneyRequest(CONST.IOU.TYPE.SPLIT, reportID, requestType, true, undefined, undefined, isFromFloatingActionButton), true); + selectOption(() => startMoneyRequest(CONST.IOU.TYPE.SPLIT, reportID, requestType, true, undefined, isFromFloatingActionButton), true); break; case CONST.QUICK_ACTIONS.SEND_MONEY: - selectOption(() => startMoneyRequest(CONST.IOU.TYPE.PAY, reportID, undefined, true, undefined, undefined, isFromFloatingActionButton), false); + selectOption(() => startMoneyRequest(CONST.IOU.TYPE.PAY, reportID, undefined, true, undefined, isFromFloatingActionButton), false); break; case CONST.QUICK_ACTIONS.ASSIGN_TASK: selectOption(() => startOutCreateTaskQuickAction(currentUserAccountID, isValidReport ? reportID : '', targetAccountPersonalDetails), false); @@ -64,7 +64,7 @@ function navigateToQuickAction(params: NavigateToQuickActionParams) { case CONST.QUICK_ACTIONS.TRACK_MANUAL: case CONST.QUICK_ACTIONS.TRACK_SCAN: case CONST.QUICK_ACTIONS.TRACK_PER_DIEM: - selectOption(() => startMoneyRequest(CONST.IOU.TYPE.TRACK, reportID, requestType, true, undefined, undefined, isFromFloatingActionButton), false); + selectOption(() => startMoneyRequest(CONST.IOU.TYPE.TRACK, reportID, requestType, true, undefined, isFromFloatingActionButton), false); break; case CONST.QUICK_ACTIONS.REQUEST_DISTANCE: selectOption(() => startDistanceRequest(CONST.IOU.TYPE.SUBMIT, reportID, requestType, true, undefined, isFromFloatingActionButton), false); @@ -73,7 +73,7 @@ function navigateToQuickAction(params: NavigateToQuickActionParams) { selectOption(() => startDistanceRequest(CONST.IOU.TYPE.TRACK, reportID, requestType, true, undefined, isFromFloatingActionButton), false); break; case CONST.QUICK_ACTIONS.REQUEST_TIME: - selectOption(() => startMoneyRequest(CONST.IOU.TYPE.SUBMIT, reportID, requestType, false, undefined, undefined, isFromFloatingActionButton), true); + selectOption(() => startMoneyRequest(CONST.IOU.TYPE.SUBMIT, reportID, requestType, false, undefined, isFromFloatingActionButton), true); break; default: } diff --git a/src/pages/Share/SubmitDetailsPage.tsx b/src/pages/Share/SubmitDetailsPage.tsx index 47d353e6cb442..9d4781e126d77 100644 --- a/src/pages/Share/SubmitDetailsPage.tsx +++ b/src/pages/Share/SubmitDetailsPage.tsx @@ -91,8 +91,6 @@ function SubmitDetailsPage({ const fileName = shouldUsePreValidatedFile ? getFileName(validFilesToUpload?.uri ?? CONST.ATTACHMENT_IMAGE_DEFAULT_NAME) : getFileName(currentAttachment?.content ?? ''); const fileType = shouldUsePreValidatedFile ? (validFilesToUpload?.type ?? CONST.RECEIPT_ALLOWED_FILE_TYPES.JPEG) : (currentAttachment?.mimeType ?? ''); const [hasOnlyPersonalPolicies = false] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {selector: hasOnlyPersonalPoliciesUtil}); - const [draftTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT); - useEffect(() => { if (!errorTitle || !errorMessage) { return; @@ -113,9 +111,7 @@ function SubmitDetailsPage({ currentDate, currentUserPersonalDetails, hasOnlyPersonalPolicies, - draftTransactions, }); - // The draftTransactions can be changed if users update the expense, so we don't want to re-init the money request // eslint-disable-next-line react-hooks/exhaustive-deps }, [reportOrAccountID, policy, personalPolicy, report, parentReport, currentDate, currentUserPersonalDetails, hasOnlyPersonalPolicies]); diff --git a/src/pages/inbox/report/ReportActionCompose/useAttachmentUploadValidation.ts b/src/pages/inbox/report/ReportActionCompose/useAttachmentUploadValidation.ts index e51b4e873dc21..9f3cc5f663639 100644 --- a/src/pages/inbox/report/ReportActionCompose/useAttachmentUploadValidation.ts +++ b/src/pages/inbox/report/ReportActionCompose/useAttachmentUploadValidation.ts @@ -55,7 +55,6 @@ function useAttachmentUploadValidation({ const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policy?.id}`); const personalPolicy = usePersonalPolicy(); const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); - const [draftTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT); const hasOnlyPersonalPolicies = useMemo(() => hasOnlyPersonalPoliciesUtil(allPolicies), [allPolicies]); const reportAttachmentsContext = useContext(AttachmentModalContext); @@ -102,7 +101,6 @@ function useAttachmentUploadValidation({ currentDate, currentUserPersonalDetails, hasOnlyPersonalPolicies, - draftTransactions, }); for (const [index, file] of files.entries()) { diff --git a/src/pages/inbox/sidebar/FloatingActionButtonAndPopover.tsx b/src/pages/inbox/sidebar/FloatingActionButtonAndPopover.tsx index e91245da4ed14..37534cd275904 100644 --- a/src/pages/inbox/sidebar/FloatingActionButtonAndPopover.tsx +++ b/src/pages/inbox/sidebar/FloatingActionButtonAndPopover.tsx @@ -139,7 +139,6 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, ref const [quickAction] = useOnyx(ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE); const [quickActionReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${quickAction?.chatReportID}`); const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID); - const [allTransactionDrafts] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT); const [activePolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID}`); const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS); const {isRestrictedToPreferredPolicy, isRestrictedPolicyCreation} = usePreferredPolicy(); @@ -353,9 +352,9 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, ref } // Start the scan flow directly - startMoneyRequest(CONST.IOU.TYPE.CREATE, reportID, CONST.IOU.REQUEST_TYPE.SCAN, false, undefined, allTransactionDrafts, true); + startMoneyRequest(CONST.IOU.TYPE.CREATE, reportID, CONST.IOU.REQUEST_TYPE.SCAN, false, undefined, true); }); - }, [shouldRedirectToExpensifyClassic, allTransactionDrafts, reportID, showRedirectToExpensifyClassicModal]); + }, [shouldRedirectToExpensifyClassic, reportID, showRedirectToExpensifyClassicModal]); const startQuickScan = useCallback(() => { interceptAnonymousUser(() => { @@ -366,9 +365,9 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, ref const quickActionReportID = policyChatForActivePolicy?.reportID ?? reportID; Tab.setSelectedTab(CONST.TAB.IOU_REQUEST_TYPE, CONST.IOU.REQUEST_TYPE.SCAN); - startMoneyRequest(CONST.IOU.TYPE.CREATE, quickActionReportID, CONST.IOU.REQUEST_TYPE.SCAN, !!policyChatForActivePolicy?.reportID, undefined, allTransactionDrafts, true); + startMoneyRequest(CONST.IOU.TYPE.CREATE, quickActionReportID, CONST.IOU.REQUEST_TYPE.SCAN, !!policyChatForActivePolicy?.reportID, undefined, true); }); - }, [policyChatForActivePolicy?.policyID, policyChatForActivePolicy?.reportID, reportID, allTransactionDrafts]); + }, [policyChatForActivePolicy?.policyID, policyChatForActivePolicy?.reportID, reportID]); /** * Check if LHN status changed from active to inactive. @@ -449,12 +448,12 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, ref showRedirectToExpensifyClassicModal(); return; } - startMoneyRequest(CONST.IOU.TYPE.CREATE, reportID, undefined, undefined, undefined, allTransactionDrafts, true); + startMoneyRequest(CONST.IOU.TYPE.CREATE, reportID, undefined, undefined, undefined, true); }), sentryLabel: CONST.SENTRY_LABEL.FAB_MENU.CREATE_EXPENSE, }, ]; - }, [translate, shouldRedirectToExpensifyClassic, shouldUseNarrowLayout, allTransactionDrafts, reportID, icons, showRedirectToExpensifyClassicModal]); + }, [translate, shouldRedirectToExpensifyClassic, shouldUseNarrowLayout, reportID, icons, showRedirectToExpensifyClassicModal]); const quickActionMenuItems = useMemo(() => { // Define common properties in baseQuickAction @@ -519,7 +518,7 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, ref } const quickActionReportID = policyChatForActivePolicy?.reportID || reportID; - startMoneyRequest(CONST.IOU.TYPE.SUBMIT, quickActionReportID, CONST.IOU.REQUEST_TYPE.SCAN, true, undefined, allTransactionDrafts, true); + startMoneyRequest(CONST.IOU.TYPE.SUBMIT, quickActionReportID, CONST.IOU.REQUEST_TYPE.SCAN, true, undefined, true); }); }; @@ -562,7 +561,6 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, ref currentUserPersonalDetails.accountID, showDelegateNoAccessModal, reportID, - allTransactionDrafts, allBetas, ]); @@ -660,7 +658,7 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, ref return; } - startMoneyRequest(CONST.IOU.TYPE.INVOICE, reportID, undefined, undefined, undefined, allTransactionDrafts, true); + startMoneyRequest(CONST.IOU.TYPE.INVOICE, reportID, undefined, undefined, undefined, true); }), sentryLabel: CONST.SENTRY_LABEL.FAB_MENU.SEND_INVOICE, }, diff --git a/src/pages/iou/request/DistanceRequestStartPage.tsx b/src/pages/iou/request/DistanceRequestStartPage.tsx index 47c07fa7dac7a..5ac376b0fc866 100644 --- a/src/pages/iou/request/DistanceRequestStartPage.tsx +++ b/src/pages/iou/request/DistanceRequestStartPage.tsx @@ -57,7 +57,6 @@ function DistanceRequestStartPage({ const [lastDistanceExpenseType] = useOnyx(ONYXKEYS.NVP_LAST_DISTANCE_EXPENSE_TYPE); const isLoadingSelectedTab = isLoadingOnyxValue(selectedTabResult); const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${getNonEmptyStringOnyxID(route?.params.transactionID)}`); - const [draftTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT); const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); const [lastSelectedDistanceRates] = useOnyx(ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES); const [currentDate] = useOnyx(ONYXKEYS.CURRENT_DATE); @@ -121,7 +120,6 @@ function DistanceRequestStartPage({ lastSelectedDistanceRates, currentUserPersonalDetails, hasOnlyPersonalPolicies, - draftTransactions, }); }, [ @@ -138,7 +136,6 @@ function DistanceRequestStartPage({ lastSelectedDistanceRates, currentUserPersonalDetails, hasOnlyPersonalPolicies, - draftTransactions, ], ); diff --git a/src/pages/iou/request/IOURequestStartPage.tsx b/src/pages/iou/request/IOURequestStartPage.tsx index b642e8d2bd0e4..171bbac6043ed 100644 --- a/src/pages/iou/request/IOURequestStartPage.tsx +++ b/src/pages/iou/request/IOURequestStartPage.tsx @@ -84,7 +84,6 @@ function IOURequestStartPage({ const isLoadingTransaction = isLoadingOnyxValue(transactionResult); const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); const [lastSelectedDistanceRates] = useOnyx(ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES); - const [draftTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT); const [isMultiScanEnabled, setIsMultiScanEnabled] = useState(false); const [currentDate] = useOnyx(ONYXKEYS.CURRENT_DATE); const {isOffline} = useNetwork(); @@ -196,7 +195,6 @@ function IOURequestStartPage({ lastSelectedDistanceRates, currentUserPersonalDetails, hasOnlyPersonalPolicies, - draftTransactions, }); }, [ @@ -213,7 +211,6 @@ function IOURequestStartPage({ lastSelectedDistanceRates, currentUserPersonalDetails, hasOnlyPersonalPolicies, - draftTransactions, ], ); diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index 4b865745ecd25..f51a46a19676b 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -10971,7 +10971,6 @@ describe('actions/IOU', () => { currentDate, currentUserPersonalDetails, hasOnlyPersonalPolicies: false, - draftTransactions: undefined, }); }) .then(async () => { @@ -10994,7 +10993,6 @@ describe('actions/IOU', () => { currentDate, currentUserPersonalDetails, hasOnlyPersonalPolicies: false, - draftTransactions: undefined, }); }) .then(async () => { @@ -11018,7 +11016,6 @@ describe('actions/IOU', () => { currentDate, currentUserPersonalDetails, hasOnlyPersonalPolicies: false, - draftTransactions: undefined, }); }) .then(async () => { @@ -11039,10 +11036,6 @@ describe('actions/IOU', () => { // Set up an additional draft transaction await Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${otherDraftTransactionID}`, otherDraftTransaction); - const draftTransactions = { - [`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${otherDraftTransactionID}`]: otherDraftTransaction, - }; - await waitForBatchedUpdates() .then(() => { initMoneyRequest({ @@ -11056,7 +11049,6 @@ describe('actions/IOU', () => { currentDate, currentUserPersonalDetails, hasOnlyPersonalPolicies: false, - draftTransactions, }); }) .then(async () => { @@ -11082,11 +11074,6 @@ describe('actions/IOU', () => { await Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${otherDraftTransactionID}`, otherDraftTransaction); await Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${CONST.IOU.OPTIMISTIC_TRANSACTION_ID}`, existingOptimisticTransaction); - const draftTransactions = { - [`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${otherDraftTransactionID}`]: otherDraftTransaction, - [`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${CONST.IOU.OPTIMISTIC_TRANSACTION_ID}`]: existingOptimisticTransaction, - }; - await waitForBatchedUpdates() .then(() => { initMoneyRequest({ @@ -11100,7 +11087,6 @@ describe('actions/IOU', () => { currentDate, currentUserPersonalDetails, hasOnlyPersonalPolicies: false, - draftTransactions, }); }) .then(async () => { @@ -11127,11 +11113,6 @@ describe('actions/IOU', () => { await Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${draftTransactionID1}`, draftTransaction1); await Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${draftTransactionID2}`, draftTransaction2); - const draftTransactions = { - [`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${draftTransactionID1}`]: draftTransaction1, - [`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${draftTransactionID2}`]: draftTransaction2, - }; - await waitForBatchedUpdates() .then(() => { initMoneyRequest({ @@ -11145,7 +11126,6 @@ describe('actions/IOU', () => { currentDate, currentUserPersonalDetails, hasOnlyPersonalPolicies: false, - draftTransactions, }); }) .then(async () => { From 4340f8d4b60732ec88a5dac47930e357e4edc8d7 Mon Sep 17 00:00:00 2001 From: bartlomiej obudzinski Date: Mon, 9 Mar 2026 15:46:37 +0100 Subject: [PATCH 02/13] fix: remove unused imports from setupSentry --- src/setup/telemetry/setupSentry.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/setup/telemetry/setupSentry.ts b/src/setup/telemetry/setupSentry.ts index ba9b3582d8c6a..19d649d6882bd 100644 --- a/src/setup/telemetry/setupSentry.ts +++ b/src/setup/telemetry/setupSentry.ts @@ -1,25 +1,23 @@ import * as Sentry from '@sentry/react-native'; import {Platform} from 'react-native'; -import {isDevelopment} from '@libs/Environment/Environment'; import {breadcrumbsIntegration, browserProfilingIntegration, consoleIntegration, navigationIntegration, tracingIntegration} from '@libs/telemetry/integrations'; import {processBeforeSendLogs, processBeforeSendTransactions} from '@libs/telemetry/middlewares'; import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; import pkg from '../../../package.json'; -import makeDebugTransport from './debugTransport'; function setupSentry(): void { // With Sentry enabled in dev mode, profiling on iOS and Android does not work // If you want to enable Sentry in dev, set ENABLE_SENTRY_ON_DEV=true in .env - if (isDevelopment() && !CONFIG.ENABLE_SENTRY_ON_DEV) { - return; - } + // if (isDevelopment() && !CONFIG.ENABLE_SENTRY_ON_DEV) { + // return; + // } const integrations = [navigationIntegration, tracingIntegration, browserProfilingIntegration, breadcrumbsIntegration, consoleIntegration].filter((integration) => !!integration); Sentry.init({ dsn: CONFIG.SENTRY_DSN, - transport: isDevelopment() ? makeDebugTransport : undefined, + // transport: isDevelopment() ? makeDebugTransport : undefined, tracesSampleRate: 1.0, // 1. Profiling for Android is currently disabled because it causes crashes sometimes. // 2. When updating the profile sample rate, make sure it will not blow up our current limit in Sentry. From 36a691cec49bcab0deffbada6b5fc35243d159c7 Mon Sep 17 00:00:00 2001 From: bartlomiej obudzinski Date: Mon, 9 Mar 2026 15:50:48 +0100 Subject: [PATCH 03/13] fix: add eslint-disable for pre-existing Onyx.connect and ref pattern warnings --- src/libs/actions/IOU/index.ts | 11 +++++++++++ src/pages/inbox/sidebar/SidebarLinksData.tsx | 4 +++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/IOU/index.ts b/src/libs/actions/IOU/index.ts index c23d7a2cc2206..f288c2c7cc851 100644 --- a/src/libs/actions/IOU/index.ts +++ b/src/libs/actions/IOU/index.ts @@ -813,6 +813,7 @@ type GetTrackExpenseInformationParams = { }; let allPersonalDetails: OnyxTypes.PersonalDetailsList = {}; +// eslint-disable-next-line rulesdir/no-onyx-connect Onyx.connect({ key: ONYXKEYS.PERSONAL_DETAILS_LIST, callback: (value) => { @@ -911,6 +912,7 @@ type PayMoneyRequestFunctionParams = { }; let allTransactions: NonNullable> = {}; +// eslint-disable-next-line rulesdir/no-onyx-connect Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION, waitForCollectionCallback: true, @@ -925,6 +927,7 @@ Onyx.connect({ }); let allTransactionDrafts: NonNullable> = {}; +// eslint-disable-next-line rulesdir/no-onyx-connect Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, waitForCollectionCallback: true, @@ -934,6 +937,7 @@ Onyx.connect({ }); let allTransactionViolations: NonNullable> = {}; +// eslint-disable-next-line rulesdir/no-onyx-connect Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, waitForCollectionCallback: true, @@ -948,6 +952,7 @@ Onyx.connect({ }); let allPolicyTags: OnyxCollection = {}; +// eslint-disable-next-line rulesdir/no-onyx-connect Onyx.connect({ key: ONYXKEYS.COLLECTION.POLICY_TAGS, waitForCollectionCallback: true, @@ -961,6 +966,7 @@ Onyx.connect({ }); let allReports: OnyxCollection; +// eslint-disable-next-line rulesdir/no-onyx-connect Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT, waitForCollectionCallback: true, @@ -970,6 +976,7 @@ Onyx.connect({ }); let allReportNameValuePairs: OnyxCollection; +// eslint-disable-next-line rulesdir/no-onyx-connect Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS, waitForCollectionCallback: true, @@ -980,6 +987,7 @@ Onyx.connect({ let userAccountID = -1; let currentUserEmail = ''; +// eslint-disable-next-line rulesdir/no-onyx-connect Onyx.connect({ key: ONYXKEYS.SESSION, callback: (value) => { @@ -989,6 +997,7 @@ Onyx.connect({ }); let deprecatedCurrentUserPersonalDetails: OnyxEntry; +// eslint-disable-next-line rulesdir/no-onyx-connect Onyx.connect({ key: ONYXKEYS.PERSONAL_DETAILS_LIST, callback: (value) => { @@ -997,6 +1006,7 @@ Onyx.connect({ }); let allReportActions: OnyxCollection; +// eslint-disable-next-line rulesdir/no-onyx-connect Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, waitForCollectionCallback: true, @@ -1009,6 +1019,7 @@ Onyx.connect({ }); let personalDetailsList: OnyxEntry; +// eslint-disable-next-line rulesdir/no-onyx-connect Onyx.connect({ key: ONYXKEYS.PERSONAL_DETAILS_LIST, callback: (value) => (personalDetailsList = value), diff --git a/src/pages/inbox/sidebar/SidebarLinksData.tsx b/src/pages/inbox/sidebar/SidebarLinksData.tsx index c47562b8b2772..e1da272f8ec76 100644 --- a/src/pages/inbox/sidebar/SidebarLinksData.tsx +++ b/src/pages/inbox/sidebar/SidebarLinksData.tsx @@ -23,9 +23,10 @@ function SidebarLinksData({insets}: SidebarLinksDataProps) { const {translate} = useLocalize(); const [priorityMode = CONST.PRIORITY_MODE.DEFAULT] = useOnyx(ONYXKEYS.NVP_PRIORITY_MODE); - const {orderedReports, currentReportID} = useSidebarOrderedReportsState('SidebarLinksData'); + const {orderedReports, currentReportID, firstReportIDWithGBRorRBR} = useSidebarOrderedReportsState('SidebarLinksData'); const currentReportIDRef = useRef(currentReportID); + // eslint-disable-next-line react-hooks/refs -- intentional ref sync pattern to avoid stale closures currentReportIDRef.current = currentReportID; const isActiveReport = useCallback((reportID: string): boolean => currentReportIDRef.current === reportID, []); @@ -62,6 +63,7 @@ function SidebarLinksData({insets}: SidebarLinksDataProps) { // Data props: isActiveReport={isActiveReport} optionListItems={orderedReports} + firstReportIDWithGBRorRBR={firstReportIDWithGBRorRBR} /> ); From 9eda1f7e95aacb015accb60eb8b70e41f16f50ed Mon Sep 17 00:00:00 2001 From: bartlomiej obudzinski Date: Mon, 9 Mar 2026 15:57:53 +0100 Subject: [PATCH 04/13] Revert "fix: add eslint-disable for pre-existing Onyx.connect and ref pattern warnings" This reverts commit 36a691cec49bcab0deffbada6b5fc35243d159c7. --- src/libs/actions/IOU/index.ts | 11 ----------- src/pages/inbox/sidebar/SidebarLinksData.tsx | 4 +--- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/src/libs/actions/IOU/index.ts b/src/libs/actions/IOU/index.ts index f288c2c7cc851..c23d7a2cc2206 100644 --- a/src/libs/actions/IOU/index.ts +++ b/src/libs/actions/IOU/index.ts @@ -813,7 +813,6 @@ type GetTrackExpenseInformationParams = { }; let allPersonalDetails: OnyxTypes.PersonalDetailsList = {}; -// eslint-disable-next-line rulesdir/no-onyx-connect Onyx.connect({ key: ONYXKEYS.PERSONAL_DETAILS_LIST, callback: (value) => { @@ -912,7 +911,6 @@ type PayMoneyRequestFunctionParams = { }; let allTransactions: NonNullable> = {}; -// eslint-disable-next-line rulesdir/no-onyx-connect Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION, waitForCollectionCallback: true, @@ -927,7 +925,6 @@ Onyx.connect({ }); let allTransactionDrafts: NonNullable> = {}; -// eslint-disable-next-line rulesdir/no-onyx-connect Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, waitForCollectionCallback: true, @@ -937,7 +934,6 @@ Onyx.connect({ }); let allTransactionViolations: NonNullable> = {}; -// eslint-disable-next-line rulesdir/no-onyx-connect Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, waitForCollectionCallback: true, @@ -952,7 +948,6 @@ Onyx.connect({ }); let allPolicyTags: OnyxCollection = {}; -// eslint-disable-next-line rulesdir/no-onyx-connect Onyx.connect({ key: ONYXKEYS.COLLECTION.POLICY_TAGS, waitForCollectionCallback: true, @@ -966,7 +961,6 @@ Onyx.connect({ }); let allReports: OnyxCollection; -// eslint-disable-next-line rulesdir/no-onyx-connect Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT, waitForCollectionCallback: true, @@ -976,7 +970,6 @@ Onyx.connect({ }); let allReportNameValuePairs: OnyxCollection; -// eslint-disable-next-line rulesdir/no-onyx-connect Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS, waitForCollectionCallback: true, @@ -987,7 +980,6 @@ Onyx.connect({ let userAccountID = -1; let currentUserEmail = ''; -// eslint-disable-next-line rulesdir/no-onyx-connect Onyx.connect({ key: ONYXKEYS.SESSION, callback: (value) => { @@ -997,7 +989,6 @@ Onyx.connect({ }); let deprecatedCurrentUserPersonalDetails: OnyxEntry; -// eslint-disable-next-line rulesdir/no-onyx-connect Onyx.connect({ key: ONYXKEYS.PERSONAL_DETAILS_LIST, callback: (value) => { @@ -1006,7 +997,6 @@ Onyx.connect({ }); let allReportActions: OnyxCollection; -// eslint-disable-next-line rulesdir/no-onyx-connect Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, waitForCollectionCallback: true, @@ -1019,7 +1009,6 @@ Onyx.connect({ }); let personalDetailsList: OnyxEntry; -// eslint-disable-next-line rulesdir/no-onyx-connect Onyx.connect({ key: ONYXKEYS.PERSONAL_DETAILS_LIST, callback: (value) => (personalDetailsList = value), diff --git a/src/pages/inbox/sidebar/SidebarLinksData.tsx b/src/pages/inbox/sidebar/SidebarLinksData.tsx index e1da272f8ec76..c47562b8b2772 100644 --- a/src/pages/inbox/sidebar/SidebarLinksData.tsx +++ b/src/pages/inbox/sidebar/SidebarLinksData.tsx @@ -23,10 +23,9 @@ function SidebarLinksData({insets}: SidebarLinksDataProps) { const {translate} = useLocalize(); const [priorityMode = CONST.PRIORITY_MODE.DEFAULT] = useOnyx(ONYXKEYS.NVP_PRIORITY_MODE); - const {orderedReports, currentReportID, firstReportIDWithGBRorRBR} = useSidebarOrderedReportsState('SidebarLinksData'); + const {orderedReports, currentReportID} = useSidebarOrderedReportsState('SidebarLinksData'); const currentReportIDRef = useRef(currentReportID); - // eslint-disable-next-line react-hooks/refs -- intentional ref sync pattern to avoid stale closures currentReportIDRef.current = currentReportID; const isActiveReport = useCallback((reportID: string): boolean => currentReportIDRef.current === reportID, []); @@ -63,7 +62,6 @@ function SidebarLinksData({insets}: SidebarLinksDataProps) { // Data props: isActiveReport={isActiveReport} optionListItems={orderedReports} - firstReportIDWithGBRorRBR={firstReportIDWithGBRorRBR} /> ); From 880cd54eef2a4386fdb2ca9aa81907c147a4658a Mon Sep 17 00:00:00 2001 From: bartlomiej obudzinski Date: Mon, 9 Mar 2026 15:57:53 +0100 Subject: [PATCH 05/13] Revert "fix: remove unused imports from setupSentry" This reverts commit 4340f8d4b60732ec88a5dac47930e357e4edc8d7. --- src/setup/telemetry/setupSentry.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/setup/telemetry/setupSentry.ts b/src/setup/telemetry/setupSentry.ts index 19d649d6882bd..ba9b3582d8c6a 100644 --- a/src/setup/telemetry/setupSentry.ts +++ b/src/setup/telemetry/setupSentry.ts @@ -1,23 +1,25 @@ import * as Sentry from '@sentry/react-native'; import {Platform} from 'react-native'; +import {isDevelopment} from '@libs/Environment/Environment'; import {breadcrumbsIntegration, browserProfilingIntegration, consoleIntegration, navigationIntegration, tracingIntegration} from '@libs/telemetry/integrations'; import {processBeforeSendLogs, processBeforeSendTransactions} from '@libs/telemetry/middlewares'; import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; import pkg from '../../../package.json'; +import makeDebugTransport from './debugTransport'; function setupSentry(): void { // With Sentry enabled in dev mode, profiling on iOS and Android does not work // If you want to enable Sentry in dev, set ENABLE_SENTRY_ON_DEV=true in .env - // if (isDevelopment() && !CONFIG.ENABLE_SENTRY_ON_DEV) { - // return; - // } + if (isDevelopment() && !CONFIG.ENABLE_SENTRY_ON_DEV) { + return; + } const integrations = [navigationIntegration, tracingIntegration, browserProfilingIntegration, breadcrumbsIntegration, consoleIntegration].filter((integration) => !!integration); Sentry.init({ dsn: CONFIG.SENTRY_DSN, - // transport: isDevelopment() ? makeDebugTransport : undefined, + transport: isDevelopment() ? makeDebugTransport : undefined, tracesSampleRate: 1.0, // 1. Profiling for Android is currently disabled because it causes crashes sometimes. // 2. When updating the profile sample rate, make sure it will not blow up our current limit in Sentry. From fa5e3b5491d713128c1454751fbdb6f84efb480d Mon Sep 17 00:00:00 2001 From: bartlomiej obudzinski Date: Tue, 10 Mar 2026 13:45:35 +0100 Subject: [PATCH 06/13] perf: reduce Onyx subscriptions during expense creation mount --- src/hooks/useShowNotFoundPageInIOUStep.ts | 4 ++-- src/libs/actions/IOU/index.ts | 2 +- src/libs/actions/Transaction.ts | 5 ++++ src/pages/Share/SubmitDetailsPage.tsx | 1 + src/pages/iou/request/IOURequestStartPage.tsx | 14 +++++++---- .../iou/request/step/IOURequestStepAmount.tsx | 24 ++++++++++--------- .../workspace/AccessOrNotFoundWrapper.tsx | 7 +++++- src/selectors/TransactionDraft.ts | 13 ++++++++-- 8 files changed, 49 insertions(+), 21 deletions(-) diff --git a/src/hooks/useShowNotFoundPageInIOUStep.ts b/src/hooks/useShowNotFoundPageInIOUStep.ts index e1023c01af883..0e1a8579d3468 100644 --- a/src/hooks/useShowNotFoundPageInIOUStep.ts +++ b/src/hooks/useShowNotFoundPageInIOUStep.ts @@ -23,8 +23,8 @@ const useShowNotFoundPageInIOUStep = (action: IOUAction, iouType: IOUType, repor const isSplitExpense = iouType === CONST.IOU.TYPE.SPLIT_EXPENSE; const [session] = useOnyx(ONYXKEYS.SESSION); - const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`); - const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(transaction?.reportID)}`); + const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${isEditing ? report?.policyID : undefined}`); + const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${isEditing ? getNonEmptyStringOnyxID(transaction?.reportID) : undefined}`); const reportActionsReportID = useMemo(() => { let actionsReportID; diff --git a/src/libs/actions/IOU/index.ts b/src/libs/actions/IOU/index.ts index 16a5fb636cb12..97c80cd092ba7 100644 --- a/src/libs/actions/IOU/index.ts +++ b/src/libs/actions/IOU/index.ts @@ -1409,7 +1409,7 @@ function createDraftTransaction(transaction: OnyxTypes.Transaction) { } function clearMoneyRequest(transactionID: string, skipConfirmation = false) { - removeDraftTransactions(); + requestAnimationFrame(() => removeDraftTransactions()); Onyx.set(`${ONYXKEYS.COLLECTION.SKIP_CONFIRMATION}${transactionID}`, skipConfirmation); } diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index d575d9c8c1160..883993564fc2c 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -1653,6 +1653,10 @@ function getDraftTransactions(draftTransactions?: OnyxCollection): return Object.values(draftTransactions ?? allTransactionDrafts ?? {}).filter((transaction): transaction is Transaction => !!transaction); } +function getDraftTransactionByID(transactionID: string): OnyxEntry { + return allTransactionDrafts?.[`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`]; +} + function mergeTransactionIdsHighlightOnSearchRoute(type: SearchDataTypes, data: Record | null) { return Onyx.merge(ONYXKEYS.TRANSACTION_IDS_HIGHLIGHT_ON_SEARCH_ROUTE, {[type]: data}); } @@ -1678,6 +1682,7 @@ export { markAsCash, dismissDuplicateTransactionViolation, getDraftTransactions, + getDraftTransactionByID, generateTransactionID, setReviewDuplicatesKey, abandonReviewDuplicateTransactions, diff --git a/src/pages/Share/SubmitDetailsPage.tsx b/src/pages/Share/SubmitDetailsPage.tsx index 9d4781e126d77..a81c5e338a32c 100644 --- a/src/pages/Share/SubmitDetailsPage.tsx +++ b/src/pages/Share/SubmitDetailsPage.tsx @@ -112,6 +112,7 @@ function SubmitDetailsPage({ currentUserPersonalDetails, hasOnlyPersonalPolicies, }); + // initMoneyRequest is an imported action, intentionally excluded to avoid re-initializing on every render // eslint-disable-next-line react-hooks/exhaustive-deps }, [reportOrAccountID, policy, personalPolicy, report, parentReport, currentDate, currentUserPersonalDetails, hasOnlyPersonalPolicies]); diff --git a/src/pages/iou/request/IOURequestStartPage.tsx b/src/pages/iou/request/IOURequestStartPage.tsx index 171bbac6043ed..a8626ed3edf3a 100644 --- a/src/pages/iou/request/IOURequestStartPage.tsx +++ b/src/pages/iou/request/IOURequestStartPage.tsx @@ -44,7 +44,7 @@ import type SCREENS from '@src/SCREENS'; import type {SelectedTabRequest} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; -import IOURequestStepAmount from './step/IOURequestStepAmount'; +import {IOURequestStepAmountWithTransactionOnly} from './step/IOURequestStepAmount'; import IOURequestStepDestination from './step/IOURequestStepDestination'; import IOURequestStepDistance from './step/IOURequestStepDistance'; import IOURequestStepHours from './step/IOURequestStepHours'; @@ -65,6 +65,8 @@ function IOURequestStartPage({ route: { params: {iouType, reportID}, }, + report, + reportDraft, navigation, // This is currently only being used for testing defaultSelectedTab = CONST.TAB_REQUEST.SCAN, @@ -73,7 +75,6 @@ function IOURequestStartPage({ const {translate} = useLocalize(); const shouldUseTab = iouType !== CONST.IOU.TYPE.SEND && iouType !== CONST.IOU.TYPE.PAY && iouType !== CONST.IOU.TYPE.INVOICE; const personalPolicy = usePersonalPolicy(); - const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`); const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`); const policy = usePolicy(report?.policyID); const [lastSelectedTab, selectedTabResult] = useOnyx(`${ONYXKEYS.COLLECTION.SELECTED_TAB}${CONST.TAB.IOU_REQUEST_TYPE}`); @@ -282,6 +283,7 @@ function IOURequestStartPage({ return ( {() => ( - )} @@ -407,10 +411,12 @@ function IOURequestStartPage({ onContainerElementChanged={setActiveTabContainerElement} style={[styles.flexColumn, styles.flex1]} > - )} diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index e5030f82db299..b9c32bb49a350 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -1,6 +1,6 @@ import {useFocusEffect} from '@react-navigation/native'; import {hasSeenTourSelector} from '@selectors/Onboarding'; -import {validTransactionDraftsSelector} from '@selectors/TransactionDraft'; +import {validTransactionDraftIDsSelector} from '@selectors/TransactionDraft'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import isTextInputFocused from '@components/TextInput/BaseTextInput/isTextInputFocused'; @@ -19,7 +19,7 @@ import useReportAttributes from '@hooks/useReportAttributes'; import useReportIsArchived from '@hooks/useReportIsArchived'; import useSelfDMReport from '@hooks/useSelfDMReport'; import useShowNotFoundPageInIOUStep from '@hooks/useShowNotFoundPageInIOUStep'; -import {setTransactionReport} from '@libs/actions/Transaction'; +import {getDraftTransactionByID, setTransactionReport} from '@libs/actions/Transaction'; import {convertToBackendAmount} from '@libs/CurrencyUtils'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import {calculateDefaultReimbursable, getExistingTransactionID, isMovingTransactionFromTrackExpense, navigateToConfirmationPage, navigateToParticipantPage} from '@libs/IOUUtils'; @@ -30,6 +30,7 @@ import shouldUseDefaultExpensePolicy from '@libs/shouldUseDefaultExpensePolicy'; import {calculateTaxAmount, getAmount, getCurrency, getDefaultTaxCode, getRequestType, getTaxValue, isDistanceRequest, isExpenseUnreported} from '@libs/TransactionUtils'; import MoneyRequestAmountForm from '@pages/iou/MoneyRequestAmountForm'; import { + getAllTransactionViolations, getMoneyRequestParticipantsFromReport, requestMoney, setMoneyRequestAmount, @@ -108,10 +109,10 @@ function IOURequestStepAmount({ const defaultExpensePolicy = useDefaultExpensePolicy(); const personalPolicy = usePersonalPolicy(); const [amountOwed] = useOnyx(ONYXKEYS.NVP_PRIVATE_AMOUNT_OWED); - const {duplicateTransactions, duplicateTransactionViolations} = useDuplicateTransactionsAndViolations(transactionID ? [transactionID] : []); + const isEditing = action === CONST.IOU.ACTION.EDIT; + const {duplicateTransactions, duplicateTransactionViolations} = useDuplicateTransactionsAndViolations(isEditing && transactionID ? [transactionID] : []); const reportAttributesDerived = useReportAttributes(); const privateIsArchivedMap = usePrivateIsArchivedMap(); - const isEditing = action === CONST.IOU.ACTION.EDIT; const isSplitBill = iouType === CONST.IOU.TYPE.SPLIT; const isCreateAction = action === CONST.IOU.ACTION.CREATE; const isSubmitAction = action === CONST.IOU.ACTION.SUBMIT; @@ -130,9 +131,7 @@ function IOURequestStepAmount({ const isUnreportedDistanceExpense = isEditing && isDistanceRequest(transaction) && isExpenseUnreported(transaction); const isASAPSubmitBetaEnabled = isBetaEnabled(CONST.BETAS.ASAP_SUBMIT); - const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS); - const [transactionDrafts] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {selector: validTransactionDraftsSelector}); - const draftTransactionIDs = Object.keys(transactionDrafts ?? {}); + const [draftTransactionIDs] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {selector: validTransactionDraftIDsSelector}); const currentUserAccountIDParam = currentUserPersonalDetails.accountID; const currentUserEmailParam = currentUserPersonalDetails.login ?? ''; @@ -251,7 +250,7 @@ function IOURequestStepAmount({ } if (iouType === CONST.IOU.TYPE.SUBMIT || iouType === CONST.IOU.TYPE.REQUEST) { const existingTransactionID = getExistingTransactionID(transaction?.linkedTrackedExpenseReportAction); - const existingTransactionDraft = existingTransactionID ? transactionDrafts?.[existingTransactionID] : undefined; + const existingTransactionDraft = existingTransactionID ? getDraftTransactionByID(existingTransactionID) : undefined; requestMoney({ report, @@ -274,11 +273,11 @@ function IOURequestStepAmount({ isASAPSubmitBetaEnabled, currentUserAccountIDParam, currentUserEmailParam, - transactionViolations, + transactionViolations: getAllTransactionViolations(), quickAction, policyRecentlyUsedCurrencies: policyRecentlyUsedCurrencies ?? [], existingTransactionDraft, - draftTransactionIDs, + draftTransactionIDs: draftTransactionIDs ?? [], isSelfTourViewed, personalDetails, }); @@ -489,5 +488,8 @@ const IOURequestStepAmountWithWritableReportOrNotFound = withWritableReportOrNot // eslint-disable-next-line rulesdir/no-negated-variables const IOURequestStepAmountWithFullTransactionOrNotFound = withFullTransactionOrNotFound(IOURequestStepAmountWithWritableReportOrNotFound, true); +// Version without withWritableReportOrNotFound, for use when parent already provides report prop +const IOURequestStepAmountWithTransactionOnly = withFullTransactionOrNotFound(IOURequestStepAmount, true); + export default IOURequestStepAmountWithFullTransactionOrNotFound; -export {isParticipantP2P}; +export {isParticipantP2P, IOURequestStepAmountWithTransactionOnly}; diff --git a/src/pages/workspace/AccessOrNotFoundWrapper.tsx b/src/pages/workspace/AccessOrNotFoundWrapper.tsx index 185323a47cae5..3a57de9961699 100644 --- a/src/pages/workspace/AccessOrNotFoundWrapper.tsx +++ b/src/pages/workspace/AccessOrNotFoundWrapper.tsx @@ -81,6 +81,9 @@ type AccessOrNotFoundWrapperProps = { /** The id of the report that holds the transaction */ reportID?: string; + /** Pre-fetched report, avoids duplicate Onyx subscription when already available from parent */ + report?: OnyxEntry; + /** The report currently being looked at */ policyID?: string; @@ -136,12 +139,14 @@ function AccessOrNotFoundWrapper({ shouldBeBlocked, policyID, reportID, + report: reportProp, iouType, allPolicies, featureName, ...props }: AccessOrNotFoundWrapperProps) { - const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`); + const [reportFromOnyx] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportProp ? undefined : reportID}`); + const report = reportProp ?? reportFromOnyx; const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); const [isLoadingReportData = true] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA); const {login = ''} = useCurrentUserPersonalDetails(); diff --git a/src/selectors/TransactionDraft.ts b/src/selectors/TransactionDraft.ts index 92a274165eb09..d56146101a46b 100644 --- a/src/selectors/TransactionDraft.ts +++ b/src/selectors/TransactionDraft.ts @@ -9,5 +9,14 @@ const validTransactionDraftsSelector = (drafts: OnyxCollection): Re return acc; }, {}); -// eslint-disable-next-line import/prefer-default-export -export {validTransactionDraftsSelector}; +const validTransactionDraftIDsSelector = (drafts: OnyxCollection): string[] => { + const ids: string[] = []; + for (const draft of Object.values(drafts ?? {})) { + if (draft) { + ids.push(draft.transactionID); + } + } + return ids; +}; + +export {validTransactionDraftsSelector, validTransactionDraftIDsSelector}; From 9a936b74c6464a64181c70192750abc4aa08bdc7 Mon Sep 17 00:00:00 2001 From: bartlomiej obudzinski Date: Tue, 10 Mar 2026 14:57:01 +0100 Subject: [PATCH 07/13] fix: update QuickActionNavigationTest for removed draftTransactions param --- tests/unit/QuickActionNavigationTest.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit/QuickActionNavigationTest.ts b/tests/unit/QuickActionNavigationTest.ts index 7ae0d1c991fec..eae9de615c2f4 100644 --- a/tests/unit/QuickActionNavigationTest.ts +++ b/tests/unit/QuickActionNavigationTest.ts @@ -34,7 +34,7 @@ describe('IOU Utils', () => { }); // Then we should start manual submit request flow - expect(startMoneyRequest).toHaveBeenCalledWith(CONST.IOU.TYPE.SUBMIT, reportID, CONST.IOU.REQUEST_TYPE.MANUAL, true, undefined, undefined, undefined); + expect(startMoneyRequest).toHaveBeenCalledWith(CONST.IOU.TYPE.SUBMIT, reportID, CONST.IOU.REQUEST_TYPE.MANUAL, true, undefined, undefined); }); it('should be navigated to Scan receipt Split Expense', () => { @@ -50,7 +50,7 @@ describe('IOU Utils', () => { }); // Then we should start scan split request flow - expect(startMoneyRequest).toHaveBeenCalledWith(CONST.IOU.TYPE.SPLIT, reportID, CONST.IOU.REQUEST_TYPE.SCAN, true, undefined, undefined, undefined); + expect(startMoneyRequest).toHaveBeenCalledWith(CONST.IOU.TYPE.SPLIT, reportID, CONST.IOU.REQUEST_TYPE.SCAN, true, undefined, undefined); }); it('should be navigated to Track distance Expense', () => { @@ -115,7 +115,7 @@ describe('IOU Utils', () => { }); // Then we should start per diem request flow - expect(startMoneyRequest).toHaveBeenCalledWith(CONST.IOU.TYPE.SUBMIT, reportID, CONST.IOU.REQUEST_TYPE.PER_DIEM, true, undefined, undefined, undefined); + expect(startMoneyRequest).toHaveBeenCalledWith(CONST.IOU.TYPE.SUBMIT, reportID, CONST.IOU.REQUEST_TYPE.PER_DIEM, true, undefined, undefined); }); it('should be navigated to Time Expense', () => { @@ -131,7 +131,7 @@ describe('IOU Utils', () => { }); // Then we should start time request flow - expect(startMoneyRequest).toHaveBeenCalledWith(CONST.IOU.TYPE.SUBMIT, reportID, CONST.IOU.REQUEST_TYPE.TIME, false, undefined, undefined, undefined); + expect(startMoneyRequest).toHaveBeenCalledWith(CONST.IOU.TYPE.SUBMIT, reportID, CONST.IOU.REQUEST_TYPE.TIME, false, undefined, undefined); }); }); }); From f9fe4f49048c28f959806ec92951c63ac65fc03b Mon Sep 17 00:00:00 2001 From: bartlomiej obudzinski Date: Tue, 10 Mar 2026 15:34:56 +0100 Subject: [PATCH 08/13] fix: revert requestAnimationFrame on removeDraftTransactions --- src/libs/actions/IOU/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/IOU/index.ts b/src/libs/actions/IOU/index.ts index 97c80cd092ba7..16a5fb636cb12 100644 --- a/src/libs/actions/IOU/index.ts +++ b/src/libs/actions/IOU/index.ts @@ -1409,7 +1409,7 @@ function createDraftTransaction(transaction: OnyxTypes.Transaction) { } function clearMoneyRequest(transactionID: string, skipConfirmation = false) { - requestAnimationFrame(() => removeDraftTransactions()); + removeDraftTransactions(); Onyx.set(`${ONYXKEYS.COLLECTION.SKIP_CONFIRMATION}${transactionID}`, skipConfirmation); } From 78187ab0bf49e638e53be78c0920cf2cebc82b6f Mon Sep 17 00:00:00 2001 From: bartlomiej obudzinski Date: Wed, 11 Mar 2026 08:29:30 +0100 Subject: [PATCH 09/13] refactor: pass draftTransactionIDs instead of full collection to preserve pure function pattern --- .../BaseFloatingCameraButton.tsx | 4 +++- .../Modal/EmployeeTestDriveModal.tsx | 3 +++ src/hooks/useReceiptScanDrop.tsx | 3 +++ src/libs/actions/IOU/index.ts | 20 +++++++++++++++---- src/libs/actions/QuickActionNavigation.ts | 13 ++++++------ src/pages/Share/SubmitDetailsPage.tsx | 1 + .../useAttachmentUploadValidation.ts | 3 +++ .../FloatingActionButtonAndPopover.tsx | 20 +++++++++++-------- .../iou/request/DistanceRequestStartPage.tsx | 4 ++++ src/pages/iou/request/IOURequestStartPage.tsx | 4 ++++ tests/unit/QuickActionNavigationTest.ts | 8 ++++---- 11 files changed, 60 insertions(+), 23 deletions(-) diff --git a/src/components/FloatingCameraButton/BaseFloatingCameraButton.tsx b/src/components/FloatingCameraButton/BaseFloatingCameraButton.tsx index 0de447361af7c..9c91ac04eded1 100644 --- a/src/components/FloatingCameraButton/BaseFloatingCameraButton.tsx +++ b/src/components/FloatingCameraButton/BaseFloatingCameraButton.tsx @@ -1,3 +1,4 @@ +import {validTransactionDraftIDsSelector} from '@selectors/TransactionDraft'; import React, {useCallback, useMemo} from 'react'; import {View} from 'react-native'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; @@ -39,6 +40,7 @@ function BaseFloatingCameraButton({icon}: BaseFloatingCameraButtonProps) { const [userBillingGraceEndPeriods] = useOnyx(ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_USER_BILLING_GRACE_PERIOD_END); const [ownerBillingGraceEndPeriod] = useOnyx(ONYXKEYS.NVP_PRIVATE_OWNER_BILLING_GRACE_PERIOD_END); const reportID = useMemo(() => generateReportID(), []); + const [draftTransactionIDs] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {selector: validTransactionDraftIDsSelector}); const policyChatForActivePolicySelector = useCallback( (reports: OnyxCollection) => { @@ -64,7 +66,7 @@ function BaseFloatingCameraButton({icon}: BaseFloatingCameraButtonProps) { const quickActionReportID = policyChatForActivePolicy?.reportID ?? reportID; Tab.setSelectedTab(CONST.TAB.IOU_REQUEST_TYPE, CONST.IOU.REQUEST_TYPE.SCAN); - startMoneyRequest(CONST.IOU.TYPE.CREATE, quickActionReportID, CONST.IOU.REQUEST_TYPE.SCAN, !!policyChatForActivePolicy?.reportID, undefined, true); + startMoneyRequest(CONST.IOU.TYPE.CREATE, quickActionReportID, CONST.IOU.REQUEST_TYPE.SCAN, !!policyChatForActivePolicy?.reportID, undefined, draftTransactionIDs, true); }); }; diff --git a/src/components/TestDrive/Modal/EmployeeTestDriveModal.tsx b/src/components/TestDrive/Modal/EmployeeTestDriveModal.tsx index 58d8e12d509ac..ce29cce99cb73 100644 --- a/src/components/TestDrive/Modal/EmployeeTestDriveModal.tsx +++ b/src/components/TestDrive/Modal/EmployeeTestDriveModal.tsx @@ -1,4 +1,5 @@ import {useRoute} from '@react-navigation/native'; +import {validTransactionDraftIDsSelector} from '@selectors/TransactionDraft'; import {format} from 'date-fns'; import {Str} from 'expensify-common'; import React, {useCallback, useMemo, useState} from 'react'; @@ -50,6 +51,7 @@ function EmployeeTestDriveModal() { const personalPolicy = usePersonalPolicy(); const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); const hasOnlyPersonalPolicies = useMemo(() => hasOnlyPersonalPoliciesUtil(allPolicies), [allPolicies]); + const [draftTransactionIDs] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {selector: validTransactionDraftIDsSelector}); const onBossEmailChange = useCallback((value: string) => { setBossEmail(value); @@ -85,6 +87,7 @@ function EmployeeTestDriveModal() { currentDate, currentUserPersonalDetails, hasOnlyPersonalPolicies, + draftTransactionIDs, }); setMoneyRequestReceipt(transactionID, source, filename, true, CONST.TEST_RECEIPT.FILE_TYPE, false, true); diff --git a/src/hooks/useReceiptScanDrop.tsx b/src/hooks/useReceiptScanDrop.tsx index 782b4a21d16c1..1877108ca366c 100644 --- a/src/hooks/useReceiptScanDrop.tsx +++ b/src/hooks/useReceiptScanDrop.tsx @@ -1,3 +1,4 @@ +import {validTransactionDraftIDsSelector} from '@selectors/TransactionDraft'; import {setTransactionReport} from '@libs/actions/Transaction'; import {navigateToParticipantPage} from '@libs/IOUUtils'; import Navigation from '@libs/Navigation/Navigation'; @@ -36,6 +37,7 @@ function useReceiptScanDrop() { const newReportID = generateReportID(); const [newReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${newReportID}`); const [newParentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${newReport?.parentReportID}`); + const [draftTransactionIDs] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {selector: validTransactionDraftIDsSelector}); const saveFileAndInitMoneyRequest = (files: FileObject[]) => { const initialTransaction = initMoneyRequest({ @@ -49,6 +51,7 @@ function useReceiptScanDrop() { currentDate, currentUserPersonalDetails, hasOnlyPersonalPolicies, + draftTransactionIDs, }); const newReceiptFiles: ReceiptFile[] = []; diff --git a/src/libs/actions/IOU/index.ts b/src/libs/actions/IOU/index.ts index 16a5fb636cb12..480134b3538c2 100644 --- a/src/libs/actions/IOU/index.ts +++ b/src/libs/actions/IOU/index.ts @@ -303,6 +303,7 @@ type InitMoneyRequestParams = { lastSelectedDistanceRates?: OnyxEntry; currentUserPersonalDetails: CurrentUserPersonalDetails; hasOnlyPersonalPolicies: boolean; + draftTransactionIDs?: string[]; }; type MoneyRequestInformation = { @@ -1290,6 +1291,7 @@ function initMoneyRequest({ lastSelectedDistanceRates, currentUserPersonalDetails, hasOnlyPersonalPolicies, + draftTransactionIDs, }: InitMoneyRequestParams) { // Generate a brand new transactionID const newTransactionID = CONST.IOU.OPTIMISTIC_TRANSACTION_ID; @@ -1300,7 +1302,12 @@ function initMoneyRequest({ const created = currentDate || format(new Date(), 'yyyy-MM-dd'); // We remove draft transactions created during multi scanning if there are some - removeDraftTransactions(true); + if (draftTransactionIDs) { + const idsToRemove = draftTransactionIDs.filter((id) => id !== CONST.IOU.OPTIMISTIC_TRANSACTION_ID); + removeDraftTransactionsByIDs(idsToRemove); + } else { + removeDraftTransactions(true); + } // in case we have to re-init money request, but the IOU request type is the same with the old draft transaction, // we should keep most of the existing data by using the ONYX MERGE operation @@ -1408,8 +1415,12 @@ function createDraftTransaction(transaction: OnyxTypes.Transaction) { Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transaction.transactionID}`, newTransaction); } -function clearMoneyRequest(transactionID: string, skipConfirmation = false) { - removeDraftTransactions(); +function clearMoneyRequest(transactionID: string, skipConfirmation = false, draftTransactionIDs?: string[]) { + if (draftTransactionIDs) { + removeDraftTransactionsByIDs(draftTransactionIDs); + } else { + removeDraftTransactions(); + } Onyx.set(`${ONYXKEYS.COLLECTION.SKIP_CONFIRMATION}${transactionID}`, skipConfirmation); } @@ -1419,6 +1430,7 @@ function startMoneyRequest( requestType?: IOURequestType, skipConfirmation = false, backToReport?: string, + draftTransactionIDs?: string[], isFromFloatingActionButton?: boolean, ) { const sourceRoute = Navigation.getActiveRoute(); @@ -1432,7 +1444,7 @@ function startMoneyRequest( [CONST.TELEMETRY.ATTRIBUTE_ROUTE_FROM]: sourceRoute || 'unknown', }, }); - clearMoneyRequest(CONST.IOU.OPTIMISTIC_TRANSACTION_ID, skipConfirmation); + clearMoneyRequest(CONST.IOU.OPTIMISTIC_TRANSACTION_ID, skipConfirmation, draftTransactionIDs); if (isFromFloatingActionButton) { Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${CONST.IOU.OPTIMISTIC_TRANSACTION_ID}`, {isFromFloatingActionButton}); } diff --git a/src/libs/actions/QuickActionNavigation.ts b/src/libs/actions/QuickActionNavigation.ts index 84ea68a3684c3..58bfb99952df1 100644 --- a/src/libs/actions/QuickActionNavigation.ts +++ b/src/libs/actions/QuickActionNavigation.ts @@ -16,6 +16,7 @@ type NavigateToQuickActionParams = { targetAccountPersonalDetails: PersonalDetails; currentUserAccountID: number; isFromFloatingActionButton?: boolean; + draftTransactionIDs?: string[]; }; function getQuickActionRequestType(action: QuickActionName | undefined, lastDistanceExpenseType?: DistanceExpenseType): IOURequestType | undefined { @@ -40,7 +41,7 @@ function getQuickActionRequestType(action: QuickActionName | undefined, lastDist } function navigateToQuickAction(params: NavigateToQuickActionParams) { - const {isValidReport, quickAction, selectOption, lastDistanceExpenseType, targetAccountPersonalDetails, currentUserAccountID, isFromFloatingActionButton} = params; + const {isValidReport, quickAction, selectOption, lastDistanceExpenseType, targetAccountPersonalDetails, currentUserAccountID, isFromFloatingActionButton, draftTransactionIDs} = params; const reportID = isValidReport && quickAction?.chatReportID ? quickAction?.chatReportID : generateReportID(); const requestType = getQuickActionRequestType(quickAction?.action, lastDistanceExpenseType); @@ -48,15 +49,15 @@ function navigateToQuickAction(params: NavigateToQuickActionParams) { case CONST.QUICK_ACTIONS.REQUEST_MANUAL: case CONST.QUICK_ACTIONS.REQUEST_SCAN: case CONST.QUICK_ACTIONS.PER_DIEM: - selectOption(() => startMoneyRequest(CONST.IOU.TYPE.SUBMIT, reportID, requestType, true, undefined, isFromFloatingActionButton), true); + selectOption(() => startMoneyRequest(CONST.IOU.TYPE.SUBMIT, reportID, requestType, true, undefined, draftTransactionIDs, isFromFloatingActionButton), true); break; case CONST.QUICK_ACTIONS.SPLIT_MANUAL: case CONST.QUICK_ACTIONS.SPLIT_SCAN: case CONST.QUICK_ACTIONS.SPLIT_DISTANCE: - selectOption(() => startMoneyRequest(CONST.IOU.TYPE.SPLIT, reportID, requestType, true, undefined, isFromFloatingActionButton), true); + selectOption(() => startMoneyRequest(CONST.IOU.TYPE.SPLIT, reportID, requestType, true, undefined, draftTransactionIDs, isFromFloatingActionButton), true); break; case CONST.QUICK_ACTIONS.SEND_MONEY: - selectOption(() => startMoneyRequest(CONST.IOU.TYPE.PAY, reportID, undefined, true, undefined, isFromFloatingActionButton), false); + selectOption(() => startMoneyRequest(CONST.IOU.TYPE.PAY, reportID, undefined, true, undefined, draftTransactionIDs, isFromFloatingActionButton), false); break; case CONST.QUICK_ACTIONS.ASSIGN_TASK: selectOption(() => startOutCreateTaskQuickAction(currentUserAccountID, isValidReport ? reportID : '', targetAccountPersonalDetails), false); @@ -64,7 +65,7 @@ function navigateToQuickAction(params: NavigateToQuickActionParams) { case CONST.QUICK_ACTIONS.TRACK_MANUAL: case CONST.QUICK_ACTIONS.TRACK_SCAN: case CONST.QUICK_ACTIONS.TRACK_PER_DIEM: - selectOption(() => startMoneyRequest(CONST.IOU.TYPE.TRACK, reportID, requestType, true, undefined, isFromFloatingActionButton), false); + selectOption(() => startMoneyRequest(CONST.IOU.TYPE.TRACK, reportID, requestType, true, undefined, draftTransactionIDs, isFromFloatingActionButton), false); break; case CONST.QUICK_ACTIONS.REQUEST_DISTANCE: selectOption(() => startDistanceRequest(CONST.IOU.TYPE.SUBMIT, reportID, requestType, true, undefined, isFromFloatingActionButton), false); @@ -73,7 +74,7 @@ function navigateToQuickAction(params: NavigateToQuickActionParams) { selectOption(() => startDistanceRequest(CONST.IOU.TYPE.TRACK, reportID, requestType, true, undefined, isFromFloatingActionButton), false); break; case CONST.QUICK_ACTIONS.REQUEST_TIME: - selectOption(() => startMoneyRequest(CONST.IOU.TYPE.SUBMIT, reportID, requestType, false, undefined, isFromFloatingActionButton), true); + selectOption(() => startMoneyRequest(CONST.IOU.TYPE.SUBMIT, reportID, requestType, false, undefined, draftTransactionIDs, isFromFloatingActionButton), true); break; default: } diff --git a/src/pages/Share/SubmitDetailsPage.tsx b/src/pages/Share/SubmitDetailsPage.tsx index a81c5e338a32c..e509c0abf7b9b 100644 --- a/src/pages/Share/SubmitDetailsPage.tsx +++ b/src/pages/Share/SubmitDetailsPage.tsx @@ -111,6 +111,7 @@ function SubmitDetailsPage({ currentDate, currentUserPersonalDetails, hasOnlyPersonalPolicies, + draftTransactionIDs, }); // initMoneyRequest is an imported action, intentionally excluded to avoid re-initializing on every render // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/src/pages/inbox/report/ReportActionCompose/useAttachmentUploadValidation.ts b/src/pages/inbox/report/ReportActionCompose/useAttachmentUploadValidation.ts index 9f3cc5f663639..d830056ca68b2 100644 --- a/src/pages/inbox/report/ReportActionCompose/useAttachmentUploadValidation.ts +++ b/src/pages/inbox/report/ReportActionCompose/useAttachmentUploadValidation.ts @@ -1,3 +1,4 @@ +import {validTransactionDraftIDsSelector} from '@selectors/TransactionDraft'; import {useCallback, useContext, useMemo, useRef} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import useFilesValidation from '@hooks/useFilesValidation'; @@ -56,6 +57,7 @@ function useAttachmentUploadValidation({ const personalPolicy = usePersonalPolicy(); const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); const hasOnlyPersonalPolicies = useMemo(() => hasOnlyPersonalPoliciesUtil(allPolicies), [allPolicies]); + const [draftTransactionIDs] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {selector: validTransactionDraftIDsSelector}); const reportAttachmentsContext = useContext(AttachmentModalContext); const showAttachmentModalScreen = useCallback( @@ -101,6 +103,7 @@ function useAttachmentUploadValidation({ currentDate, currentUserPersonalDetails, hasOnlyPersonalPolicies, + draftTransactionIDs, }); for (const [index, file] of files.entries()) { diff --git a/src/pages/inbox/sidebar/FloatingActionButtonAndPopover.tsx b/src/pages/inbox/sidebar/FloatingActionButtonAndPopover.tsx index 37534cd275904..e7e6f2cb4a79e 100644 --- a/src/pages/inbox/sidebar/FloatingActionButtonAndPopover.tsx +++ b/src/pages/inbox/sidebar/FloatingActionButtonAndPopover.tsx @@ -1,6 +1,7 @@ import {useIsFocused} from '@react-navigation/native'; import {hasSeenTourSelector, tryNewDotOnyxSelector} from '@selectors/Onboarding'; import {groupPaidPoliciesWithExpenseChatEnabledSelector} from '@selectors/Policy'; +import {validTransactionDraftIDsSelector} from '@selectors/TransactionDraft'; import {Str} from 'expensify-common'; import type {ImageContentFit} from 'expo-image'; import type {ForwardedRef} from 'react'; @@ -196,6 +197,7 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, ref const isUserPaidPolicyMember = useIsPaidPolicyAdmin(); const reportID = useMemo(() => generateReportID(), []); + const [draftTransactionIDs] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {selector: validTransactionDraftIDsSelector}); const groupPaidPoliciesWithChatEnabled = useCallback( (policies: OnyxCollection) => groupPaidPoliciesWithExpenseChatEnabledSelector(policies, session?.email), @@ -352,9 +354,9 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, ref } // Start the scan flow directly - startMoneyRequest(CONST.IOU.TYPE.CREATE, reportID, CONST.IOU.REQUEST_TYPE.SCAN, false, undefined, true); + startMoneyRequest(CONST.IOU.TYPE.CREATE, reportID, CONST.IOU.REQUEST_TYPE.SCAN, false, undefined, draftTransactionIDs, true); }); - }, [shouldRedirectToExpensifyClassic, reportID, showRedirectToExpensifyClassicModal]); + }, [shouldRedirectToExpensifyClassic, reportID, showRedirectToExpensifyClassicModal, draftTransactionIDs]); const startQuickScan = useCallback(() => { interceptAnonymousUser(() => { @@ -365,9 +367,9 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, ref const quickActionReportID = policyChatForActivePolicy?.reportID ?? reportID; Tab.setSelectedTab(CONST.TAB.IOU_REQUEST_TYPE, CONST.IOU.REQUEST_TYPE.SCAN); - startMoneyRequest(CONST.IOU.TYPE.CREATE, quickActionReportID, CONST.IOU.REQUEST_TYPE.SCAN, !!policyChatForActivePolicy?.reportID, undefined, true); + startMoneyRequest(CONST.IOU.TYPE.CREATE, quickActionReportID, CONST.IOU.REQUEST_TYPE.SCAN, !!policyChatForActivePolicy?.reportID, undefined, draftTransactionIDs, true); }); - }, [policyChatForActivePolicy?.policyID, policyChatForActivePolicy?.reportID, reportID]); + }, [policyChatForActivePolicy?.policyID, policyChatForActivePolicy?.reportID, reportID, draftTransactionIDs]); /** * Check if LHN status changed from active to inactive. @@ -448,12 +450,12 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, ref showRedirectToExpensifyClassicModal(); return; } - startMoneyRequest(CONST.IOU.TYPE.CREATE, reportID, undefined, undefined, undefined, true); + startMoneyRequest(CONST.IOU.TYPE.CREATE, reportID, undefined, undefined, undefined, draftTransactionIDs, true); }), sentryLabel: CONST.SENTRY_LABEL.FAB_MENU.CREATE_EXPENSE, }, ]; - }, [translate, shouldRedirectToExpensifyClassic, shouldUseNarrowLayout, reportID, icons, showRedirectToExpensifyClassicModal]); + }, [translate, shouldRedirectToExpensifyClassic, shouldUseNarrowLayout, reportID, icons, showRedirectToExpensifyClassicModal, draftTransactionIDs]); const quickActionMenuItems = useMemo(() => { // Define common properties in baseQuickAction @@ -492,6 +494,7 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, ref targetAccountPersonalDetails, currentUserAccountID: currentUserPersonalDetails.accountID, isFromFloatingActionButton: true, + draftTransactionIDs, }); }); }; @@ -518,7 +521,7 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, ref } const quickActionReportID = policyChatForActivePolicy?.reportID || reportID; - startMoneyRequest(CONST.IOU.TYPE.SUBMIT, quickActionReportID, CONST.IOU.REQUEST_TYPE.SCAN, true, undefined, true); + startMoneyRequest(CONST.IOU.TYPE.SUBMIT, quickActionReportID, CONST.IOU.REQUEST_TYPE.SCAN, true, undefined, draftTransactionIDs, true); }); }; @@ -562,6 +565,7 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, ref showDelegateNoAccessModal, reportID, allBetas, + draftTransactionIDs, ]); const isTravelEnabled = useMemo(() => { @@ -658,7 +662,7 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu, ref return; } - startMoneyRequest(CONST.IOU.TYPE.INVOICE, reportID, undefined, undefined, undefined, true); + startMoneyRequest(CONST.IOU.TYPE.INVOICE, reportID, undefined, undefined, undefined, draftTransactionIDs, true); }), sentryLabel: CONST.SENTRY_LABEL.FAB_MENU.SEND_INVOICE, }, diff --git a/src/pages/iou/request/DistanceRequestStartPage.tsx b/src/pages/iou/request/DistanceRequestStartPage.tsx index 5ac376b0fc866..2c91f67a26979 100644 --- a/src/pages/iou/request/DistanceRequestStartPage.tsx +++ b/src/pages/iou/request/DistanceRequestStartPage.tsx @@ -1,4 +1,5 @@ import {useFocusEffect} from '@react-navigation/native'; +import {validTransactionDraftIDsSelector} from '@selectors/TransactionDraft'; import React, {useCallback, useEffect, useMemo, useState} from 'react'; import {Keyboard, View} from 'react-native'; import FocusTrapContainerElement from '@components/FocusTrap/FocusTrapContainerElement'; @@ -67,6 +68,7 @@ function DistanceRequestStartPage({ const hasOnlyPersonalPolicies = useMemo(() => hasOnlyPersonalPoliciesUtil(allPolicies), [allPolicies]); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const personalPolicy = usePersonalPolicy(); + const [draftTransactionIDs] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {selector: validTransactionDraftIDsSelector}); const tabTitles = { [CONST.IOU.TYPE.REQUEST]: translate('iou.trackDistance'), @@ -120,6 +122,7 @@ function DistanceRequestStartPage({ lastSelectedDistanceRates, currentUserPersonalDetails, hasOnlyPersonalPolicies, + draftTransactionIDs, }); }, [ @@ -136,6 +139,7 @@ function DistanceRequestStartPage({ lastSelectedDistanceRates, currentUserPersonalDetails, hasOnlyPersonalPolicies, + draftTransactionIDs, ], ); diff --git a/src/pages/iou/request/IOURequestStartPage.tsx b/src/pages/iou/request/IOURequestStartPage.tsx index a8626ed3edf3a..9884645eea5c6 100644 --- a/src/pages/iou/request/IOURequestStartPage.tsx +++ b/src/pages/iou/request/IOURequestStartPage.tsx @@ -1,4 +1,5 @@ import {useFocusEffect} from '@react-navigation/native'; +import {validTransactionDraftIDsSelector} from '@selectors/TransactionDraft'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {Keyboard, View} from 'react-native'; import DragAndDropProvider from '@components/DragAndDrop/Provider'; @@ -89,6 +90,7 @@ function IOURequestStartPage({ const [currentDate] = useOnyx(ONYXKEYS.CURRENT_DATE); const {isOffline} = useNetwork(); const [nvpDismissedProductTraining] = useOnyx(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING); + const [draftTransactionIDs] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {selector: validTransactionDraftIDsSelector}); const hasOnlyPersonalPolicies = useMemo(() => hasOnlyPersonalPoliciesUtil(allPolicies), [allPolicies]); const perDiemInputRef = useRef(null); @@ -196,6 +198,7 @@ function IOURequestStartPage({ lastSelectedDistanceRates, currentUserPersonalDetails, hasOnlyPersonalPolicies, + draftTransactionIDs, }); }, [ @@ -212,6 +215,7 @@ function IOURequestStartPage({ lastSelectedDistanceRates, currentUserPersonalDetails, hasOnlyPersonalPolicies, + draftTransactionIDs, ], ); diff --git a/tests/unit/QuickActionNavigationTest.ts b/tests/unit/QuickActionNavigationTest.ts index eae9de615c2f4..7ae0d1c991fec 100644 --- a/tests/unit/QuickActionNavigationTest.ts +++ b/tests/unit/QuickActionNavigationTest.ts @@ -34,7 +34,7 @@ describe('IOU Utils', () => { }); // Then we should start manual submit request flow - expect(startMoneyRequest).toHaveBeenCalledWith(CONST.IOU.TYPE.SUBMIT, reportID, CONST.IOU.REQUEST_TYPE.MANUAL, true, undefined, undefined); + expect(startMoneyRequest).toHaveBeenCalledWith(CONST.IOU.TYPE.SUBMIT, reportID, CONST.IOU.REQUEST_TYPE.MANUAL, true, undefined, undefined, undefined); }); it('should be navigated to Scan receipt Split Expense', () => { @@ -50,7 +50,7 @@ describe('IOU Utils', () => { }); // Then we should start scan split request flow - expect(startMoneyRequest).toHaveBeenCalledWith(CONST.IOU.TYPE.SPLIT, reportID, CONST.IOU.REQUEST_TYPE.SCAN, true, undefined, undefined); + expect(startMoneyRequest).toHaveBeenCalledWith(CONST.IOU.TYPE.SPLIT, reportID, CONST.IOU.REQUEST_TYPE.SCAN, true, undefined, undefined, undefined); }); it('should be navigated to Track distance Expense', () => { @@ -115,7 +115,7 @@ describe('IOU Utils', () => { }); // Then we should start per diem request flow - expect(startMoneyRequest).toHaveBeenCalledWith(CONST.IOU.TYPE.SUBMIT, reportID, CONST.IOU.REQUEST_TYPE.PER_DIEM, true, undefined, undefined); + expect(startMoneyRequest).toHaveBeenCalledWith(CONST.IOU.TYPE.SUBMIT, reportID, CONST.IOU.REQUEST_TYPE.PER_DIEM, true, undefined, undefined, undefined); }); it('should be navigated to Time Expense', () => { @@ -131,7 +131,7 @@ describe('IOU Utils', () => { }); // Then we should start time request flow - expect(startMoneyRequest).toHaveBeenCalledWith(CONST.IOU.TYPE.SUBMIT, reportID, CONST.IOU.REQUEST_TYPE.TIME, false, undefined, undefined); + expect(startMoneyRequest).toHaveBeenCalledWith(CONST.IOU.TYPE.SUBMIT, reportID, CONST.IOU.REQUEST_TYPE.TIME, false, undefined, undefined, undefined); }); }); }); From 20399ce4bf538bb839914338f5a81a8f94c2d800 Mon Sep 17 00:00:00 2001 From: bartlomiej obudzinski Date: Wed, 11 Mar 2026 08:48:48 +0100 Subject: [PATCH 10/13] fix: restore pure Onyx subscriptions for violations and transaction drafts in IOURequestStepAmount --- src/libs/actions/Transaction.ts | 5 ----- .../iou/request/step/IOURequestStepAmount.tsx | 15 ++++++++------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index 883993564fc2c..d575d9c8c1160 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -1653,10 +1653,6 @@ function getDraftTransactions(draftTransactions?: OnyxCollection): return Object.values(draftTransactions ?? allTransactionDrafts ?? {}).filter((transaction): transaction is Transaction => !!transaction); } -function getDraftTransactionByID(transactionID: string): OnyxEntry { - return allTransactionDrafts?.[`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`]; -} - function mergeTransactionIdsHighlightOnSearchRoute(type: SearchDataTypes, data: Record | null) { return Onyx.merge(ONYXKEYS.TRANSACTION_IDS_HIGHLIGHT_ON_SEARCH_ROUTE, {[type]: data}); } @@ -1682,7 +1678,6 @@ export { markAsCash, dismissDuplicateTransactionViolation, getDraftTransactions, - getDraftTransactionByID, generateTransactionID, setReviewDuplicatesKey, abandonReviewDuplicateTransactions, diff --git a/src/pages/iou/request/step/IOURequestStepAmount.tsx b/src/pages/iou/request/step/IOURequestStepAmount.tsx index b9c32bb49a350..ec9e623498382 100644 --- a/src/pages/iou/request/step/IOURequestStepAmount.tsx +++ b/src/pages/iou/request/step/IOURequestStepAmount.tsx @@ -1,6 +1,6 @@ import {useFocusEffect} from '@react-navigation/native'; import {hasSeenTourSelector} from '@selectors/Onboarding'; -import {validTransactionDraftIDsSelector} from '@selectors/TransactionDraft'; +import {validTransactionDraftsSelector} from '@selectors/TransactionDraft'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import isTextInputFocused from '@components/TextInput/BaseTextInput/isTextInputFocused'; @@ -19,7 +19,7 @@ import useReportAttributes from '@hooks/useReportAttributes'; import useReportIsArchived from '@hooks/useReportIsArchived'; import useSelfDMReport from '@hooks/useSelfDMReport'; import useShowNotFoundPageInIOUStep from '@hooks/useShowNotFoundPageInIOUStep'; -import {getDraftTransactionByID, setTransactionReport} from '@libs/actions/Transaction'; +import {setTransactionReport} from '@libs/actions/Transaction'; import {convertToBackendAmount} from '@libs/CurrencyUtils'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import {calculateDefaultReimbursable, getExistingTransactionID, isMovingTransactionFromTrackExpense, navigateToConfirmationPage, navigateToParticipantPage} from '@libs/IOUUtils'; @@ -30,7 +30,6 @@ import shouldUseDefaultExpensePolicy from '@libs/shouldUseDefaultExpensePolicy'; import {calculateTaxAmount, getAmount, getCurrency, getDefaultTaxCode, getRequestType, getTaxValue, isDistanceRequest, isExpenseUnreported} from '@libs/TransactionUtils'; import MoneyRequestAmountForm from '@pages/iou/MoneyRequestAmountForm'; import { - getAllTransactionViolations, getMoneyRequestParticipantsFromReport, requestMoney, setMoneyRequestAmount, @@ -131,7 +130,9 @@ function IOURequestStepAmount({ const isUnreportedDistanceExpense = isEditing && isDistanceRequest(transaction) && isExpenseUnreported(transaction); const isASAPSubmitBetaEnabled = isBetaEnabled(CONST.BETAS.ASAP_SUBMIT); - const [draftTransactionIDs] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {selector: validTransactionDraftIDsSelector}); + const [transactionDrafts] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_DRAFT, {selector: validTransactionDraftsSelector}); + const draftTransactionIDs = Object.keys(transactionDrafts ?? {}); + const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS); const currentUserAccountIDParam = currentUserPersonalDetails.accountID; const currentUserEmailParam = currentUserPersonalDetails.login ?? ''; @@ -250,7 +251,7 @@ function IOURequestStepAmount({ } if (iouType === CONST.IOU.TYPE.SUBMIT || iouType === CONST.IOU.TYPE.REQUEST) { const existingTransactionID = getExistingTransactionID(transaction?.linkedTrackedExpenseReportAction); - const existingTransactionDraft = existingTransactionID ? getDraftTransactionByID(existingTransactionID) : undefined; + const existingTransactionDraft = existingTransactionID ? transactionDrafts?.[existingTransactionID] : undefined; requestMoney({ report, @@ -273,11 +274,11 @@ function IOURequestStepAmount({ isASAPSubmitBetaEnabled, currentUserAccountIDParam, currentUserEmailParam, - transactionViolations: getAllTransactionViolations(), + transactionViolations, quickAction, policyRecentlyUsedCurrencies: policyRecentlyUsedCurrencies ?? [], existingTransactionDraft, - draftTransactionIDs: draftTransactionIDs ?? [], + draftTransactionIDs, isSelfTourViewed, personalDetails, }); From 6e33140b58e5de8754de851043a406380db310d7 Mon Sep 17 00:00:00 2001 From: bartlomiej obudzinski Date: Wed, 11 Mar 2026 08:51:41 +0100 Subject: [PATCH 11/13] fix: revert AccessOrNotFoundWrapper report prop and restore report subscription in IOURequestStartPage --- src/pages/iou/request/IOURequestStartPage.tsx | 5 ++--- src/pages/workspace/AccessOrNotFoundWrapper.tsx | 7 +------ 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/pages/iou/request/IOURequestStartPage.tsx b/src/pages/iou/request/IOURequestStartPage.tsx index 26882a2bcbd42..6a5629b15b3cd 100644 --- a/src/pages/iou/request/IOURequestStartPage.tsx +++ b/src/pages/iou/request/IOURequestStartPage.tsx @@ -73,8 +73,6 @@ function IOURequestStartPage({ route: { params: {iouType, reportID}, }, - report, - reportDraft, navigation, // This is currently only being used for testing defaultSelectedTab = CONST.TAB_REQUEST.SCAN, @@ -83,6 +81,8 @@ function IOURequestStartPage({ const {translate} = useLocalize(); const shouldUseTab = iouType !== CONST.IOU.TYPE.SEND && iouType !== CONST.IOU.TYPE.PAY && iouType !== CONST.IOU.TYPE.INVOICE; const personalPolicy = usePersonalPolicy(); + const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`); + const [reportDraft] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_DRAFT}${reportID}`); const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`); const policy = usePolicy(report?.policyID); const [lastSelectedTab, selectedTabResult] = useOnyx(`${ONYXKEYS.COLLECTION.SELECTED_TAB}${CONST.TAB.IOU_REQUEST_TYPE}`); @@ -286,7 +286,6 @@ function IOURequestStartPage({ return ( ; - /** The report currently being looked at */ policyID?: string; @@ -139,14 +136,12 @@ function AccessOrNotFoundWrapper({ shouldBeBlocked, policyID, reportID, - report: reportProp, iouType, allPolicies, featureName, ...props }: AccessOrNotFoundWrapperProps) { - const [reportFromOnyx] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportProp ? undefined : reportID}`); - const report = reportProp ?? reportFromOnyx; + const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`); const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); const [isLoadingReportData = true] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA); const {login = ''} = useCurrentUserPersonalDetails(); From 99bdc4f31571f9e733ad5e7b00414b35fa2cef2a Mon Sep 17 00:00:00 2001 From: bartlomiej obudzinski Date: Thu, 12 Mar 2026 15:27:38 +0100 Subject: [PATCH 12/13] fix: revert conditional useOnyx keys in useShowNotFoundPageInIOUStep --- src/hooks/useShowNotFoundPageInIOUStep.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hooks/useShowNotFoundPageInIOUStep.ts b/src/hooks/useShowNotFoundPageInIOUStep.ts index 0e1a8579d3468..e1023c01af883 100644 --- a/src/hooks/useShowNotFoundPageInIOUStep.ts +++ b/src/hooks/useShowNotFoundPageInIOUStep.ts @@ -23,8 +23,8 @@ const useShowNotFoundPageInIOUStep = (action: IOUAction, iouType: IOUType, repor const isSplitExpense = iouType === CONST.IOU.TYPE.SPLIT_EXPENSE; const [session] = useOnyx(ONYXKEYS.SESSION); - const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${isEditing ? report?.policyID : undefined}`); - const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${isEditing ? getNonEmptyStringOnyxID(transaction?.reportID) : undefined}`); + const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`); + const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(transaction?.reportID)}`); const reportActionsReportID = useMemo(() => { let actionsReportID; From aff7b12f41140583a9fb847a04a1d8ada94973dc Mon Sep 17 00:00:00 2001 From: bartlomiej obudzinski Date: Mon, 16 Mar 2026 09:54:55 +0100 Subject: [PATCH 13/13] refactor: Import React alongside useMemo in useReceiptScanDrop hook --- src/hooks/useReceiptScanDrop.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/useReceiptScanDrop.tsx b/src/hooks/useReceiptScanDrop.tsx index 18857e03563a9..5746bea6407be 100644 --- a/src/hooks/useReceiptScanDrop.tsx +++ b/src/hooks/useReceiptScanDrop.tsx @@ -1,5 +1,5 @@ import {validTransactionDraftIDsSelector} from '@selectors/TransactionDraft'; -import {useMemo} from 'react'; +import React, {useMemo} from 'react'; import {setTransactionReport} from '@libs/actions/Transaction'; import {navigateToParticipantPage} from '@libs/IOUUtils'; import Navigation from '@libs/Navigation/Navigation';