diff --git a/Mobile-Expensify b/Mobile-Expensify index 984335621e0c4..4c7a93ee82200 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit 984335621e0c4713ab1338f3064c51f513b4256b +Subproject commit 4c7a93ee8220007ef4ba6a8bbbec960e840ec07f diff --git a/android/app/build.gradle b/android/app/build.gradle index e917ec1648cc1..b3a0376889614 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -114,8 +114,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1009015904 - versionName "9.1.59-4" + versionCode 1009015906 + versionName "9.1.59-6" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 08c0bd194e515..e124328df974e 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -44,7 +44,7 @@ CFBundleVersion - 9.1.59.4 + 9.1.59.6 FullStory OrgId diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index 7ef6594d98ef8..aab6d712fc26a 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 9.1.59 CFBundleVersion - 9.1.59.4 + 9.1.59.6 NSExtension NSExtensionPointIdentifier diff --git a/ios/ShareViewController/Info.plist b/ios/ShareViewController/Info.plist index cb01562952fa6..4c16fe60a781f 100644 --- a/ios/ShareViewController/Info.plist +++ b/ios/ShareViewController/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 9.1.59 CFBundleVersion - 9.1.59.4 + 9.1.59.6 NSExtension NSExtensionAttributes diff --git a/package-lock.json b/package-lock.json index 215cd96d6a517..8c93a616970fa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "9.1.59-4", + "version": "9.1.59-6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "9.1.59-4", + "version": "9.1.59-6", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index c00b3081daa70..0c82c5cebc804 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "9.1.59-4", + "version": "9.1.59-6", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", diff --git a/src/CONST.ts b/src/CONST.ts index 8ae664811cb73..910fffd5583c9 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1063,7 +1063,6 @@ const CONST = { EMPTY_ARRAY, EMPTY_OBJECT, DEFAULT_NUMBER_ID, - FAKE_REPORT_ID: 'FAKE_REPORT_ID', USE_EXPENSIFY_URL, EXPENSIFY_URL, EXPENSIFY_MOBILE_URL, diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 39cb1bb389d9a..9485b758d8c33 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -84,14 +84,12 @@ const ROUTES = { backTo, moneyRequestReportActionID, transactionID, - iouReportID, }: { reportID: string | undefined; reportActionID?: string; backTo?: string; moneyRequestReportActionID?: string; transactionID?: string; - iouReportID?: string; }) => { if (!reportID) { Log.warn('Invalid reportID is used to build the SEARCH_REPORT route'); @@ -108,10 +106,6 @@ const ROUTES = { queryParams.push(`moneyRequestReportActionID=${moneyRequestReportActionID}`); } - if (iouReportID) { - queryParams.push(`iouReportID=${iouReportID}`); - } - const queryString = queryParams.length > 0 ? (`${baseRoute}?${queryParams.join('&')}` as const) : baseRoute; return getUrlWithBackToParam(queryString, backTo); }, @@ -382,19 +376,10 @@ const ROUTES = { REPORT: 'r', REPORT_WITH_ID: { route: 'r/:reportID?/:reportActionID?', - getRoute: ( - reportID: string | undefined, - reportActionID?: string, - referrer?: string, - moneyRequestReportActionID?: string, - transactionID?: string, - backTo?: string, - iouReportID?: string, - ) => { + getRoute: (reportID: string | undefined, reportActionID?: string, referrer?: string, moneyRequestReportActionID?: string, transactionID?: string, backTo?: string) => { if (!reportID) { Log.warn('Invalid reportID is used to build the REPORT_WITH_ID route'); } - const baseRoute = reportActionID ? (`r/${reportID}/${reportActionID}` as const) : (`r/${reportID}` as const); const queryParams: string[] = []; @@ -408,10 +393,6 @@ const ROUTES = { queryParams.push(`transactionID=${transactionID}`); } - if (iouReportID) { - queryParams.push(`iouReportID=${iouReportID}`); - } - const queryString = queryParams.length > 0 ? `?${queryParams.join('&')}` : ''; return getUrlWithBackToParam(`${baseRoute}${queryString}` as const, backTo); @@ -611,15 +592,10 @@ const ROUTES = { }, MONEY_REQUEST_HOLD_REASON: { route: ':type/edit/reason/:transactionID?/:searchHash?', - getRoute: (type: ValueOf, transactionID: string, reportID: string | undefined, backTo: string, searchHash?: number) => { - let route = searchHash - ? (`${type as string}/edit/reason/${transactionID}/${searchHash}/?backTo=${backTo}` as const) - : (`${type as string}/edit/reason/${transactionID}/?backTo=${backTo}` as const); - - if (reportID) { - route = `${route}&reportID=${reportID}` as const; - } - + getRoute: (type: ValueOf, transactionID: string, reportID: string, backTo: string, searchHash?: number) => { + const route = searchHash + ? (`${type as string}/edit/reason/${transactionID}/${searchHash}/?backTo=${backTo}&reportID=${reportID}` as const) + : (`${type as string}/edit/reason/${transactionID}/?backTo=${backTo}&reportID=${reportID}` as const); return route; }, }, diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx index 4ecac1dac0ecd..30e1c9e62ffa6 100644 --- a/src/components/MoneyReportHeader.tsx +++ b/src/components/MoneyReportHeader.tsx @@ -17,20 +17,18 @@ import useSelectedTransactionsActions from '@hooks/useSelectedTransactionsAction import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode'; -import {deleteAppReport, downloadReportPDF, exportReportToCSV, exportReportToPDF, exportToIntegration, markAsManuallyExported, openReport, openUnreportedExpense} from '@libs/actions/Report'; +import {deleteAppReport, downloadReportPDF, exportReportToCSV, exportReportToPDF, exportToIntegration, markAsManuallyExported, openUnreportedExpense} from '@libs/actions/Report'; import {getThreadReportIDsForTransactions, getTotalAmountForIOUReportPreviewButton} from '@libs/MoneyRequestReportUtils'; import Navigation from '@libs/Navigation/Navigation'; import {buildOptimisticNextStepForPreventSelfApprovalsEnabled} from '@libs/NextStepUtils'; import {isSecondaryActionAPaymentOption, selectPaymentType} from '@libs/PaymentUtils'; import type {KYCFlowEvent, TriggerKYCFlow} from '@libs/PaymentUtils'; import {getValidConnectedIntegration} from '@libs/PolicyUtils'; -import {getIOUActionForReportID, getOriginalMessage, getReportAction, isMoneyRequestAction} from '@libs/ReportActionsUtils'; +import {getOriginalMessage, getReportAction, isMoneyRequestAction} from '@libs/ReportActionsUtils'; import {getAllExpensesToHoldIfApplicable, getReportPrimaryAction} from '@libs/ReportPrimaryActionUtils'; import {getSecondaryReportActions} from '@libs/ReportSecondaryActionUtils'; import { - buildTransactionThread, changeMoneyRequestHoldStatus, - generateReportID, getArchiveReason, getBankAccountRoute, getIntegrationIcon, @@ -605,14 +603,9 @@ function MoneyReportHeader({ success text={translate('iou.reviewDuplicates')} onPress={() => { - let threadID = transactionThreadReportID ?? getFirstDuplicateThreadID(transactions, reportActions); + const threadID = transactionThreadReportID ?? getFirstDuplicateThreadID(transactions, reportActions); if (!threadID) { - threadID = generateReportID(); - const duplicateTransaction = transactions.find((reportTransaction) => isDuplicate(reportTransaction.transactionID)); - const transactionID = duplicateTransaction?.transactionID; - const iouAction = getIOUActionForReportID(moneyRequestReport?.reportID, transactionID); - const optimisticTransactionThread = buildTransactionThread(iouAction, moneyRequestReport, undefined, threadID); - openReport(threadID, undefined, session?.email ? [session?.email] : [], optimisticTransactionThread, iouAction?.reportActionID); + return; } Navigation.navigate(ROUTES.TRANSACTION_DUPLICATE_REVIEW_PAGE.getRoute(threadID)); }} diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx index 7b338fb1d6ca7..8652ffdc7e017 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx @@ -28,16 +28,14 @@ import {canUseTouchScreen} from '@libs/DeviceCapabilities'; import {getThreadReportIDsForTransactions} from '@libs/MoneyRequestReportUtils'; import {navigationRef} from '@libs/Navigation/Navigation'; import {getIOUActionForTransactionID} from '@libs/ReportActionsUtils'; -import {generateReportID, getMoneyRequestSpendBreakdown, isIOUReport} from '@libs/ReportUtils'; +import {getMoneyRequestSpendBreakdown, isIOUReport} from '@libs/ReportUtils'; import {compareValues} from '@libs/SearchUIUtils'; import {getTransactionPendingAction, isTransactionPendingDelete} from '@libs/TransactionUtils'; import shouldShowTransactionYear from '@libs/TransactionUtils/shouldShowTransactionYear'; import Navigation from '@navigation/Navigation'; -import type {ReportsSplitNavigatorParamList} from '@navigation/types'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; -import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; @@ -98,8 +96,6 @@ type SortedTransactions = { sortOrder: SortOrder; }; -type ReportScreenNavigationProps = ReportsSplitNavigatorParamList[typeof SCREENS.REPORT]; - const isSortableColumnName = (key: unknown): key is SortableColumnName => !!sortableColumnNames.find((val) => val === key); const getTransactionKey = (transaction: OnyxTypes.Transaction, key: SortableColumnName) => { @@ -194,27 +190,19 @@ function MoneyRequestReportTransactionList({ const navigateToTransaction = useCallback( (activeTransaction: OnyxTypes.Transaction) => { const iouAction = getIOUActionForTransactionID(reportActions, activeTransaction.transactionID); - const reportIDToNavigate = iouAction?.childReportID ?? generateReportID(); + const reportIDToNavigate = iouAction?.childReportID; + if (!reportIDToNavigate) { + return; + } - const backTo = Navigation.getActiveRoute() as Route; + const backTo = Navigation.getActiveRoute(); // Single transaction report will open in RHP, and we need to find every other report ID for the rest of transactions // to display prev/next arrows in RHP for navigating between transactions const sortedSiblingTransactionReportIDs = getThreadReportIDsForTransactions(reportActions, sortedTransactions); setActiveTransactionThreadIDs(sortedSiblingTransactionReportIDs); - const routeParams = { - reportID: reportIDToNavigate, - backTo, - } as ReportScreenNavigationProps; - - if (!iouAction?.childReportID) { - routeParams.moneyRequestReportActionID = iouAction?.reportActionID; - routeParams.transactionID = activeTransaction.transactionID; - routeParams.iouReportID = activeTransaction.reportID; - } - - Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute(routeParams)); + Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute({reportID: reportIDToNavigate, backTo})); }, [reportActions, sortedTransactions], ); diff --git a/src/components/ReportActionItem/MoneyRequestAction.tsx b/src/components/ReportActionItem/MoneyRequestAction.tsx index 168c640b14b92..d62e6ba3bc095 100644 --- a/src/components/ReportActionItem/MoneyRequestAction.tsx +++ b/src/components/ReportActionItem/MoneyRequestAction.tsx @@ -131,7 +131,7 @@ function MoneyRequestAction({ const transactionID = isMoneyRequestAction(action) ? getOriginalMessage(action)?.IOUTransactionID : CONST.DEFAULT_NUMBER_ID; if (!action?.childReportID && transactionID && action.reportActionID) { const optimisticReportID = generateReportID(); - Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(optimisticReportID, undefined, undefined, action.reportActionID, transactionID, Navigation.getActiveRoute(), requestReportID)); + Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(optimisticReportID, undefined, undefined, action.reportActionID, transactionID, Navigation.getActiveRoute())); return; } diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 104aabc7efaba..376abbd3932ce 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -22,7 +22,7 @@ import {canUseTouchScreen} from '@libs/DeviceCapabilities'; import Log from '@libs/Log'; import isSearchTopmostFullScreenRoute from '@libs/Navigation/helpers/isSearchTopmostFullScreenRoute'; import type {PlatformStackNavigationProp} from '@libs/Navigation/PlatformStackNavigation/types'; -import {getIOUActionForTransactionID, getOriginalMessage, getReportAction, isMoneyRequestAction} from '@libs/ReportActionsUtils'; +import {getIOUActionForTransactionID} from '@libs/ReportActionsUtils'; import {canEditFieldOfMoneyRequest, generateReportID} from '@libs/ReportUtils'; import {buildSearchQueryString} from '@libs/SearchQueryUtils'; import { @@ -400,28 +400,8 @@ function Search({queryJSON, currentSearchResults, lastNonEmptySearchResults, onS const isFromSelfDM = item.reportID === CONST.REPORT.UNREPORTED_REPORT_ID; const isTransactionItem = isTransactionListItemType(item); - const backTo = Navigation.getActiveRoute(); - - // If we're trying to open a legacy transaction without a transaction thread, let's create the thread and navigate the user - if (isTransactionItem && item.transactionThreadReportID === CONST.REPORT.UNREPORTED_REPORT_ID) { - const transactionThreadReportID = generateReportID(); - const reportAction = getReportAction(item.reportID, item.moneyRequestReportActionID); - const iouReportID = isMoneyRequestAction(reportAction) ? getOriginalMessage(reportAction)?.IOUReportID : undefined; - - updateSearchResultsWithTransactionThreadReportID(hash, item.transactionID, transactionThreadReportID); - Navigation.navigate( - ROUTES.SEARCH_REPORT.getRoute({ - reportID: transactionThreadReportID, - backTo, - moneyRequestReportActionID: item.moneyRequestReportActionID, - transactionID: item.transactionID, - iouReportID, - }), - ); - return; - } - const reportID = + let reportID = isTransactionItem && (!item.isFromOneTransactionReport || isFromSelfDM) && item.transactionThreadReportID !== CONST.REPORT.UNREPORTED_REPORT_ID ? item.transactionThreadReportID : item.reportID; @@ -430,6 +410,7 @@ function Search({queryJSON, currentSearchResults, lastNonEmptySearchResults, onS return; } + const backTo = Navigation.getActiveRoute(); const shouldHandleTransactionAsReport = isReportListItemType(item) || (isTransactionItem && isOpenedAsReport); if (isBetaEnabled(CONST.BETAS.TABLE_REPORT_VIEW) && shouldHandleTransactionAsReport) { @@ -437,6 +418,21 @@ function Search({queryJSON, currentSearchResults, lastNonEmptySearchResults, onS return; } + // If we're trying to open a legacy transaction without a transaction thread, let's create the thread and navigate the user + if (isTransactionItem && reportID === CONST.REPORT.UNREPORTED_REPORT_ID) { + reportID = generateReportID(); + updateSearchResultsWithTransactionThreadReportID(hash, item.transactionID, reportID); + Navigation.navigate( + ROUTES.SEARCH_REPORT.getRoute({ + reportID, + backTo, + moneyRequestReportActionID: item.moneyRequestReportActionID, + transactionID: item.transactionID, + }), + ); + return; + } + if (isReportActionListItemType(item)) { const reportActionID = item.reportActionID; Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute({reportID, reportActionID, backTo})); diff --git a/src/libs/API/parameters/CreatePerDiemRequestParams.ts b/src/libs/API/parameters/CreatePerDiemRequestParams.ts index 24f13f203adb3..480acd9ca23db 100644 --- a/src/libs/API/parameters/CreatePerDiemRequestParams.ts +++ b/src/libs/API/parameters/CreatePerDiemRequestParams.ts @@ -17,7 +17,7 @@ type CreatePerDiemRequestParams = { createdChatReportActionID?: string; createdIOUReportActionID?: string; reportPreviewReportActionID: string; - transactionThreadReportID?: string; + transactionThreadReportID: string; createdReportActionIDForThread: string | undefined; billable?: boolean; attendees?: string; diff --git a/src/libs/API/parameters/HoldMoneyRequestParams.ts b/src/libs/API/parameters/HoldMoneyRequestParams.ts index b22e421898d10..357194d7ae56a 100644 --- a/src/libs/API/parameters/HoldMoneyRequestParams.ts +++ b/src/libs/API/parameters/HoldMoneyRequestParams.ts @@ -3,8 +3,6 @@ type HoldMoneyRequestParams = { comment: string; reportActionID: string; commentReportActionID: string; - transactionThreadReportID?: string; - createdReportActionIDForThread?: string; }; export default HoldMoneyRequestParams; diff --git a/src/libs/API/parameters/MergeDuplicatesParams.ts b/src/libs/API/parameters/MergeDuplicatesParams.ts index e1cdfd0903c61..b5ef56a52502a 100644 --- a/src/libs/API/parameters/MergeDuplicatesParams.ts +++ b/src/libs/API/parameters/MergeDuplicatesParams.ts @@ -13,8 +13,6 @@ type MergeDuplicatesParams = { receiptID: number; reportID: string | undefined; reportActionID?: string | undefined; - transactionThreadReportID?: string; - createdReportActionIDForThread?: string; }; export default MergeDuplicatesParams; diff --git a/src/libs/API/parameters/RequestMoneyParams.ts b/src/libs/API/parameters/RequestMoneyParams.ts index f2b6d89aea973..42e0e9aed6dcd 100644 --- a/src/libs/API/parameters/RequestMoneyParams.ts +++ b/src/libs/API/parameters/RequestMoneyParams.ts @@ -25,8 +25,8 @@ type RequestMoneyParams = { taxAmount: number; billable?: boolean; receiptGpsPoints?: string; - transactionThreadReportID?: string; - createdReportActionIDForThread?: string | undefined; + transactionThreadReportID: string; + createdReportActionIDForThread: string | undefined; reimbursible?: boolean; description?: string; attendees?: string; diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index c0582583a2e80..1a830a3aa79c7 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1657,7 +1657,6 @@ type ReportsSplitNavigatorParamList = { backTo?: Routes; moneyRequestReportActionID?: string; transactionID?: string; - iouReportID?: string; }; }; diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index f2a85f3f02693..f491057e5bdf8 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -1249,6 +1249,7 @@ function getOneTransactionThreadReportID( if ( actionType && iouRequestTypesSet.has(actionType) && + action.childReportID && // Include deleted IOU reportActions if: // - they have an associated IOU transaction ID or // - they have visible childActions (like comments) that we'd want to display @@ -1276,8 +1277,8 @@ function getOneTransactionThreadReportID( return; } - // Since we don't always create transaction thread optimistically, we return CONST.FAKE_REPORT_ID - return singleAction?.childReportID ?? CONST.FAKE_REPORT_ID; + // Ensure we have a childReportID associated with the IOU report action + return singleAction?.childReportID; } /** diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 3eb83dc2ab3f5..526f58f5b387b 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -619,7 +619,7 @@ type OptimisticModifiedExpenseReportAction = Pick< | 'delegateAccountID' > & {reportID?: string}; -type BaseOptimisticMoneyRequestEntities = { +type OptimisticMoneyRequestEntities = { iouReport: Report; type: ValueOf; amount: number; @@ -637,10 +637,6 @@ type BaseOptimisticMoneyRequestEntities = { linkedTrackedExpenseReportAction?: ReportAction; }; -type OptimisticMoneyRequestEntities = BaseOptimisticMoneyRequestEntities & {shouldGenerateOptimisticTransactionThread?: boolean}; -type OptimisticMoneyRequestEntitiesWithTransactionThreadFlag = BaseOptimisticMoneyRequestEntities & {shouldGenerateOptimisticTransactionThread: boolean}; -type OptimisticMoneyRequestEntitiesWithoutTransactionThreadFlag = BaseOptimisticMoneyRequestEntities; - type OptimisticTaskReport = SetRequired< Pick< Report, @@ -4059,8 +4055,8 @@ const changeMoneyRequestHoldStatus = (reportAction: OnyxEntry): vo const transactionID = getOriginalMessage(reportAction)?.IOUTransactionID; - if (!transactionID) { - Log.warn('Missing transactionID during the change of the money request hold status'); + if (!transactionID || !reportAction.childReportID) { + Log.warn('Missing transactionID and reportAction.childReportID during the change of the money request hold status'); return; } @@ -4068,7 +4064,7 @@ const changeMoneyRequestHoldStatus = (reportAction: OnyxEntry): vo const isOnHold = isOnHoldTransactionUtils(transaction); const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${moneyRequestReport.policyID}`] ?? null; - if (isOnHold && reportAction.childReportID) { + if (isOnHold) { unholdRequest(transactionID, reportAction.childReportID); } else { const activeRoute = encodeURIComponent(Navigation.getActiveRoute()); @@ -7443,7 +7439,6 @@ function buildTransactionThread( reportAction: OnyxEntry, moneyRequestReport: OnyxEntry, existingTransactionThreadReportID?: string, - optimisticTransactionThreadReportID?: string, ): OptimisticChatReport { const participantAccountIDs = [...new Set([currentUserAccountID, Number(reportAction?.actorAccountID)])].filter(Boolean) as number[]; const existingTransactionThreadReport = getReportOrDraftReport(existingTransactionThreadReportID); @@ -7466,7 +7461,6 @@ function buildTransactionThread( notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, parentReportActionID: reportAction?.reportActionID, parentReportID: moneyRequestReport?.reportID, - optimisticReportID: optimisticTransactionThreadReportID, }); } @@ -7479,12 +7473,6 @@ function buildTransactionThread( * 4. Transaction Thread linked to the IOU action via `parentReportActionID` * 5. CREATED action for the Transaction Thread */ -function buildOptimisticMoneyRequestEntities( - optimisticMoneyRequestEntities: OptimisticMoneyRequestEntitiesWithoutTransactionThreadFlag, -): [OptimisticCreatedReportAction, OptimisticCreatedReportAction, OptimisticIOUReportAction, OptimisticChatReport, OptimisticCreatedReportAction | null]; -function buildOptimisticMoneyRequestEntities( - optimisticMoneyRequestEntities: OptimisticMoneyRequestEntitiesWithTransactionThreadFlag, -): [OptimisticCreatedReportAction, OptimisticCreatedReportAction, OptimisticIOUReportAction, OptimisticChatReport | undefined, OptimisticCreatedReportAction | null]; function buildOptimisticMoneyRequestEntities({ iouReport, type, @@ -7498,17 +7486,10 @@ function buildOptimisticMoneyRequestEntities({ isSettlingUp = false, isSendMoneyFlow = false, isOwnPolicyExpenseChat = false, - shouldGenerateOptimisticTransactionThread = true, isPersonalTrackingExpense, existingTransactionThreadReportID, linkedTrackedExpenseReportAction, -}: OptimisticMoneyRequestEntities): [ - OptimisticCreatedReportAction, - OptimisticCreatedReportAction, - OptimisticIOUReportAction, - OptimisticChatReport | undefined, - OptimisticCreatedReportAction | null, -] { +}: OptimisticMoneyRequestEntities): [OptimisticCreatedReportAction, OptimisticCreatedReportAction, OptimisticIOUReportAction, OptimisticChatReport, OptimisticCreatedReportAction | null] { const createdActionForChat = buildOptimisticCreatedReportAction(payeeEmail); // The `CREATED` action must be optimistically generated before the IOU action so that it won't appear after the IOU action in the chat. @@ -7533,11 +7514,11 @@ function buildOptimisticMoneyRequestEntities({ }); // Create optimistic transactionThread and the `CREATED` action for it, if existingTransactionThreadReportID is undefined - const transactionThread = shouldGenerateOptimisticTransactionThread ? buildTransactionThread(iouAction, iouReport, existingTransactionThreadReportID) : undefined; - const createdActionForTransactionThread = !!existingTransactionThreadReportID || !shouldGenerateOptimisticTransactionThread ? null : buildOptimisticCreatedReportAction(payeeEmail); + const transactionThread = buildTransactionThread(iouAction, iouReport, existingTransactionThreadReportID); + const createdActionForTransactionThread = existingTransactionThreadReportID ? null : buildOptimisticCreatedReportAction(payeeEmail); // The IOU action and the transactionThread are co-dependent as parent-child, so we need to link them together - iouAction.childReportID = existingTransactionThreadReportID ?? transactionThread?.reportID; + iouAction.childReportID = existingTransactionThreadReportID ?? transactionThread.reportID; return [createdActionForChat, createdActionForIOUReport, iouAction, transactionThread, createdActionForTransactionThread]; } @@ -9353,12 +9334,12 @@ function getAllAncestorReportActionIDs(report: Report | null | undefined, includ /** * Get optimistic data of parent report action - * @param reportOrID The reportID of the report that is updated or the optimistic report on its own + * @param reportID The reportID of the report that is updated * @param lastVisibleActionCreated Last visible action created of the child report * @param type The type of action in the child report */ -function getOptimisticDataForParentReportAction(reportOrID: Report | string | undefined, lastVisibleActionCreated: string, type: string): Array { - const report = typeof reportOrID === 'string' ? getReportOrDraftReport(reportOrID) : reportOrID; +function getOptimisticDataForParentReportAction(reportID: string | undefined, lastVisibleActionCreated: string, type: string): Array { + const report = getReportOrDraftReport(reportID); if (!report || isEmptyObject(report)) { return []; diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 4a9dd36b14216..8902787bb2029 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -16,7 +16,6 @@ import type { CreateWorkspaceParams, DeleteMoneyRequestParams, DetachReceiptParams, - HoldMoneyRequestParams, MergeDuplicatesParams, PayInvoiceParams, PayMoneyRequestParams, @@ -127,7 +126,6 @@ import { buildTransactionThread, canBeAutoReimbursed, canUserPerformWriteAction as canUserPerformWriteActionReportUtils, - generateReportID, getAllHeldTransactions as getAllHeldTransactionsReportUtils, getAllPolicyReports, getApprovalChain, @@ -263,7 +261,7 @@ type MoneyRequestInformation = { createdChatReportActionID?: string; createdIOUReportActionID?: string; reportPreviewAction: OnyxTypes.ReportAction; - transactionThreadReportID?: string; + transactionThreadReportID: string; createdReportActionIDForThread: string | undefined; onyxData: OnyxData; billable?: boolean; @@ -440,7 +438,6 @@ type MoneyRequestInformationParams = { existingTransactionID?: string; existingTransaction?: OnyxEntry; retryParams?: StartSplitBilActionParams | CreateTrackExpenseParams | RequestMoneyInformation | ReplaceReceipt; - shouldGenerateOptimisticTransactionThread?: boolean; isSplitExpense?: boolean; testDriveCommentReportActionID?: string; }; @@ -458,8 +455,8 @@ type MoneyRequestOptimisticParams = { }; transactionParams: { transaction: OnyxTypes.Transaction; - transactionThreadReport?: OptimisticChatReport | null; - transactionThreadCreatedReportAction?: OptimisticCreatedReportAction | null; + transactionThreadReport: OptimisticChatReport | null; + transactionThreadCreatedReportAction: OptimisticCreatedReportAction | null; }; policyRecentlyUsed: { categories?: string[]; @@ -3238,7 +3235,6 @@ function getMoneyRequestInformation(moneyRequestInformation: MoneyRequestInforma existingTransactionID, moneyRequestReportID = '', retryParams, - shouldGenerateOptimisticTransactionThread = true, isSplitExpense, testDriveCommentReportActionID, } = moneyRequestInformation; @@ -3381,7 +3377,6 @@ function getMoneyRequestInformation(moneyRequestInformation: MoneyRequestInforma paymentType: isSelectedManagerMcTest(participant.login) || transactionParams.receipt?.isTestDriveReceipt ? CONST.IOU.PAYMENT_TYPE.ELSEWHERE : undefined, existingTransactionThreadReportID: linkedTrackedExpenseReportAction?.childReportID, linkedTrackedExpenseReportAction, - shouldGenerateOptimisticTransactionThread, }); let reportPreviewAction = shouldCreateNewMoneyRequestReport ? null : getReportPreviewAction(chatReport.reportID, iouReport.reportID); @@ -4851,32 +4846,30 @@ const getConvertTrackedExpenseInformation = ( // Build modified expense report action with the transaction changes const modifiedExpenseReportAction = buildOptimisticMovedTransactionAction(transactionThreadReportID, moneyRequestReportID ?? CONST.REPORT.UNREPORTED_REPORT_ID); - if (transactionThreadReportID) { - optimisticData?.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReportID}`, - value: { - [modifiedExpenseReportAction.reportActionID]: modifiedExpenseReportAction, - }, - }); - successData?.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReportID}`, - value: { - [modifiedExpenseReportAction.reportActionID]: {pendingAction: null}, - }, - }); - failureData?.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReportID}`, - value: { - [modifiedExpenseReportAction.reportActionID]: { - ...modifiedExpenseReportAction, - errors: getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericEditFailureMessage'), - }, + optimisticData?.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReportID}`, + value: { + [modifiedExpenseReportAction.reportActionID]: modifiedExpenseReportAction, + }, + }); + successData?.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReportID}`, + value: { + [modifiedExpenseReportAction.reportActionID]: {pendingAction: null}, + }, + }); + failureData?.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReportID}`, + value: { + [modifiedExpenseReportAction.reportActionID]: { + ...modifiedExpenseReportAction, + errors: getMicroSecondOnyxErrorWithTranslationKey('iou.error.genericEditFailureMessage'), }, - }); - } + }, + }); return {optimisticData, successData, failureData, modifiedExpenseReportActionID: modifiedExpenseReportAction.reportActionID}; }; @@ -4926,7 +4919,7 @@ type ConvertTrackedExpenseToRequestParams = { merchant: string; created: string; attendees?: Attendee[]; - transactionThreadReportID?: string; + transactionThreadReportID: string; }; chatParams: { reportID: string; @@ -5247,19 +5240,30 @@ function requestMoney(requestMoneyInformation: RequestMoneyInformation) { }, }; - const {payerAccountID, payerEmail, iouReport, chatReport, transaction, iouAction, createdChatReportActionID, createdIOUReportActionID, reportPreviewAction, onyxData} = - getMoneyRequestInformation({ - parentChatReport: isMovingTransactionFromTrackExpense ? undefined : currentChatReport, - participantParams, - policyParams, - transactionParams, - moneyRequestReportID, - existingTransactionID, - existingTransaction: isDistanceRequestTransactionUtils(existingTransaction) ? existingTransaction : undefined, - retryParams, - shouldGenerateOptimisticTransactionThread: false, - testDriveCommentReportActionID, - }); + const { + payerAccountID, + payerEmail, + iouReport, + chatReport, + transaction, + iouAction, + createdChatReportActionID, + createdIOUReportActionID, + reportPreviewAction, + transactionThreadReportID, + createdReportActionIDForThread, + onyxData, + } = getMoneyRequestInformation({ + parentChatReport: isMovingTransactionFromTrackExpense ? undefined : currentChatReport, + participantParams, + policyParams, + transactionParams, + moneyRequestReportID, + existingTransactionID, + existingTransaction: isDistanceRequestTransactionUtils(existingTransaction) ? existingTransaction : undefined, + retryParams, + testDriveCommentReportActionID, + }); const activeReportID = isMoneyRequestReport ? report?.reportID : chatReport.reportID; if (shouldPlaySound) { @@ -5302,6 +5306,7 @@ function requestMoney(requestMoneyInformation: RequestMoneyInformation) { actionableWhisperReportActionID, linkedTrackedExpenseReportAction, linkedTrackedExpenseReportID, + transactionThreadReportID, }, chatParams: { reportID: chatReport.reportID, @@ -5352,6 +5357,8 @@ function requestMoney(requestMoneyInformation: RequestMoneyInformation) { billable, // This needs to be a string of JSON because of limitations with the fetch() API and nested objects receiptGpsPoints: gpsPoints ? JSON.stringify(gpsPoints) : undefined, + transactionThreadReportID, + createdReportActionIDForThread, reimbursible, description: parsedComment, attendees: attendees ? JSON.stringify(attendees) : undefined, @@ -10610,29 +10617,17 @@ function adjustRemainingSplitShares(transaction: NonNullable notifyNewAction(currentReportID, userAccountID)); @@ -11018,9 +10960,7 @@ function getIOUActionForTransactions(transactionIDList: Array value?.email, canBeMissing: false}); - - const {reportActions: reportActionsWithDeletedExpenses} = usePaginatedReportActions(reportIDFromRoute); - const reportActions = reportActionsWithDeletedExpenses.filter((value) => !isDeletedParentAction(value)); - const transactionThreadReportID = getOneTransactionThreadReportID(reportIDFromRoute, reportActions ?? [], isOffline); - const [transactionThreadReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`, {canBeMissing: true}); const {isEditingDisabled, isCurrentReportLoadedFromOnyx} = useIsReportReadyToDisplay(report, reportIDFromRoute); @@ -63,29 +52,9 @@ function SearchMoneyRequestReportPage({route}: SearchMoneyRequestPageProps) { const reportID = report?.reportID; - const fetchReport = useCallback(() => { - if (reportMetadata.isOptimisticReport) { - return; - } - - // If there is one transaction thread that has not yet been created, we should create it. - if (transactionThreadReportID === CONST.FAKE_REPORT_ID && !transactionThreadReport && currentUserEmail) { - const optimisticTransactionThreadReportID = generateReportID(); - const transactions = getReportTransactions(reportID).filter((transaction) => transaction.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE); - const oneTransactionID = transactions.at(0)?.transactionID; - const iouAction = getIOUActionForReportID(reportID, oneTransactionID); - const optimisticTransactionThread = buildTransactionThread(iouAction, report, undefined, optimisticTransactionThreadReportID); - openReport(optimisticTransactionThreadReportID, undefined, [currentUserEmail], optimisticTransactionThread, iouAction?.reportActionID, false, [], undefined, true); - } - - openReport(reportID, undefined, [], undefined, undefined, false, [], undefined, true); - // We don't want to call openReport when report is changed - // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps - }, [reportMetadata.isOptimisticReport, currentUserEmail, reportID, transactionThreadReport, transactionThreadReportID]); - useEffect(() => { - fetchReport(); - }, [fetchReport]); + openReport(reportIDFromRoute, '', [], undefined, undefined, false, [], undefined, true); + }, [reportIDFromRoute]); // eslint-disable-next-line rulesdir/no-negated-variables const shouldShowNotFoundPage = useMemo( diff --git a/src/pages/TransactionDuplicate/Confirmation.tsx b/src/pages/TransactionDuplicate/Confirmation.tsx index fd1b35ffc9a96..6078b38d5e1ef 100644 --- a/src/pages/TransactionDuplicate/Confirmation.tsx +++ b/src/pages/TransactionDuplicate/Confirmation.tsx @@ -26,7 +26,6 @@ import CONST from '@src/CONST'; import * as IOU from '@src/libs/actions/IOU'; import * as ReportActionsUtils from '@src/libs/ReportActionsUtils'; import * as ReportUtils from '@src/libs/ReportUtils'; -import {generateReportID} from '@src/libs/ReportUtils'; import * as TransactionUtils from '@src/libs/TransactionUtils'; import {getTransactionID} from '@src/libs/TransactionUtils'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -58,18 +57,15 @@ function Confirmation() { const isReportOwner = iouReport?.ownerAccountID === currentUserPersonalDetails?.accountID; const mergeDuplicates = useCallback(() => { - const transactionThreadReportID = reportAction?.childReportID ?? generateReportID(); - if (!reportAction?.childReportID) { - transactionsMergeParams.transactionThreadReportID = transactionThreadReportID; + return; } - IOU.mergeDuplicates(transactionsMergeParams); if (isBetaEnabled(CONST.BETAS.TABLE_REPORT_VIEW)) { Navigation.dismissModal(); return; } - Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(transactionThreadReportID), {compareParams: false}); + Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(reportAction.childReportID), {compareParams: false}); }, [reportAction?.childReportID, transactionsMergeParams, isBetaEnabled]); const resolveDuplicates = useCallback(() => { diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index e28add3bb9b5a..09c0f2a4df35d 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -40,9 +40,7 @@ import {getDisplayNameOrDefault} from '@libs/PersonalDetailsUtils'; import { getCombinedReportActions, getFilteredReportActionsForReportView, - getIOUActionForReportID, getOneTransactionThreadReportID, - getReportAction, isCreatedAction, isDeletedParentAction, isMoneyRequestAction, @@ -50,15 +48,11 @@ import { shouldReportActionBeVisible, } from '@libs/ReportActionsUtils'; import { - buildTransactionThread, canEditReportAction, canUserPerformWriteAction, findLastAccessedReport, - generateReportID, getParticipantsAccountIDsForDisplay, getReportOfflinePendingActionAndErrors, - getReportOrDraftReport, - getReportTransactions, isChatThread, isConciergeChatReport, isGroupChat, @@ -305,7 +299,6 @@ function ReportScreen({route, navigation}: ReportScreenProps) { }); const reportTransactionIDs = reportTransactions?.map((transaction) => transaction.transactionID); const transactionThreadReportID = getOneTransactionThreadReportID(reportID, reportActions ?? [], isOffline, reportTransactionIDs); - const [transactionThreadReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`, {canBeMissing: true}); const [transactionThreadReportActions = {}] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReportID}`, {canBeMissing: true}); const combinedReportActions = getCombinedReportActions(reportActions, transactionThreadReportID ?? null, Object.values(transactionThreadReportActions)); const lastReportAction = [...combinedReportActions, parentReportAction].find((action) => canEditReportAction(action) && !isMoneyRequestAction(action)); @@ -472,7 +465,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) { ); const fetchReport = useCallback(() => { - if (reportMetadata.isOptimisticReport && report?.type === CONST.REPORT.TYPE.CHAT && transactionThreadReportID !== CONST.FAKE_REPORT_ID) { + if (reportMetadata.isOptimisticReport && report?.type === CONST.REPORT.TYPE.CHAT) { return; } @@ -480,41 +473,26 @@ function ReportScreen({route, navigation}: ReportScreenProps) { return; } - const {moneyRequestReportActionID, transactionID, iouReportID} = route.params; + const moneyRequestReportActionID: string | undefined = route.params?.moneyRequestReportActionID; + const transactionID: string | undefined = route.params?.transactionID; // When we get here with a moneyRequestReportActionID and a transactionID from the route it means we don't have the transaction thread created yet // so we have to call OpenReport in a way that the transaction thread will be created and attached to the parentReportAction - if (transactionID && currentUserEmail && !report) { - const iouReport = getReportOrDraftReport(iouReportID); - const iouAction = getReportAction(iouReportID, moneyRequestReportActionID); - const optimisticTransactionThread = buildTransactionThread(iouAction, iouReport, undefined, reportIDFromRoute); - openReport(reportIDFromRoute, undefined, [currentUserEmail], optimisticTransactionThread, moneyRequestReportActionID, false, [], undefined, undefined, transactionID); + if (transactionID && currentUserEmail) { + openReport(reportIDFromRoute, '', [currentUserEmail], undefined, moneyRequestReportActionID, false, [], undefined, undefined, transactionID); return; } - - // If there is one transaction thread that has not yet been created, we should create it. - if (transactionThreadReportID === CONST.FAKE_REPORT_ID && !transactionThreadReport && parentReportAction?.childMoneyRequestCount === 1 && currentUserEmail) { - const optimisticTransactionThreadReportID = generateReportID(); - const transactions = getReportTransactions(reportID).filter((transaction) => transaction.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE); - const oneTransactionID = transactions.at(0)?.transactionID; - const iouAction = getIOUActionForReportID(reportID, oneTransactionID); - const optimisticTransactionThread = buildTransactionThread(iouAction, report, undefined, optimisticTransactionThreadReportID); - openReport(optimisticTransactionThreadReportID, undefined, [currentUserEmail], optimisticTransactionThread, iouAction?.reportActionID); - } - openReport(reportIDFromRoute, reportActionIDFromRoute); }, [ - parentReportAction?.childMoneyRequestCount, reportMetadata.isOptimisticReport, + report?.type, + report?.errorFields?.notFound, isOffline, - route.params, + route.params?.moneyRequestReportActionID, + route.params?.transactionID, currentUserEmail, reportIDFromRoute, reportActionIDFromRoute, - report, - reportID, - transactionThreadReport, - transactionThreadReportID, ]); useEffect(() => { @@ -581,7 +559,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) { // There should be only one openReport execution per page start or navigating fetchReport(); // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps - }, [route, isLinkedMessagePageReady, reportActionIDFromRoute, transactionThreadReportID]); + }, [route, isLinkedMessagePageReady, reportActionIDFromRoute]); const prevReportActions = usePrevious(reportActions); useEffect(() => { diff --git a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx index cb69e74ec9057..7febb8a9f72e8 100755 --- a/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx +++ b/src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx @@ -21,13 +21,12 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useRestoreInputFocus from '@hooks/useRestoreInputFocus'; import useStyleUtils from '@hooks/useStyleUtils'; import {getExpensifyCardFromReportAction} from '@libs/CardMessageUtils'; -import {getLinkedTransactionID, getOneTransactionThreadReportID, getOriginalMessage, getReportActions, isMoneyRequestAction} from '@libs/ReportActionsUtils'; +import {getLinkedTransactionID, getOneTransactionThreadReportID, getReportAction} from '@libs/ReportActionsUtils'; import { chatIncludesChronosWithID, getSourceIDFromReportAction, isArchivedNonExpenseReport, isArchivedNonExpenseReportWithID, - isIOUReport, isInvoiceReport as ReportUtilsIsInvoiceReport, isMoneyRequest as ReportUtilsIsMoneyRequest, isMoneyRequestReport as ReportUtilsIsMoneyRequestReport, @@ -166,7 +165,7 @@ function BaseReportActionContextMenu({ const [download] = useOnyx(`${ONYXKEYS.COLLECTION.DOWNLOAD}${sourceID}`, {canBeMissing: true}); const [childReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportAction?.childReportID}`, {canBeMissing: true}); - const childReportActions = childReport ? getReportActions(childReport) : undefined; + const parentReportAction = getReportAction(childReport?.parentReportID, childReport?.parentReportActionID); const {reportActions: paginatedReportActions} = usePaginatedReportActions(childReport?.reportID); const transactionThreadReportID = useMemo( @@ -181,29 +180,24 @@ function BaseReportActionContextMenu({ const requestParentReportAction = useMemo(() => { if (isMoneyRequestReport || isInvoiceReport) { - if (!paginatedReportActions) { + if (!paginatedReportActions || !transactionThreadReport?.parentReportActionID) { return undefined; } - if (transactionThreadReportID === CONST.FAKE_REPORT_ID) { - return Object.values(childReportActions ?? {}).find((action) => action.actionName === CONST.REPORT.ACTIONS.TYPE.IOU); - } - return paginatedReportActions.find((action) => action.reportActionID === transactionThreadReport?.parentReportActionID); + return paginatedReportActions.find((action) => action.reportActionID === transactionThreadReport.parentReportActionID); } - return reportAction; - }, [childReportActions, transactionThreadReportID, reportAction, isMoneyRequestReport, isInvoiceReport, paginatedReportActions, transactionThreadReport?.parentReportActionID]); + return parentReportAction; + }, [parentReportAction, isMoneyRequestReport, isInvoiceReport, paginatedReportActions, transactionThreadReport?.parentReportActionID]); - const moneyRequestAction = transactionThreadReportID ? requestParentReportAction : reportAction; + const moneyRequestAction = transactionThreadReportID ? requestParentReportAction : parentReportAction; const [childReportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${childReport?.reportID}`, {canBeMissing: true}); const [parentReportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${childReport?.parentReportID}`, {canBeMissing: true}); - const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${childReport?.parentReportID ?? (isMoneyRequestAction(reportAction) && getOriginalMessage(reportAction)?.IOUReportID)}`, { - canBeMissing: true, - }); + const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${childReport?.parentReportID}`, {canBeMissing: true}); const isMoneyRequest = useMemo(() => ReportUtilsIsMoneyRequest(childReport), [childReport]); const isTrackExpenseReport = ReportUtilsIsTrackExpenseReport(childReport); const isSingleTransactionView = isMoneyRequest || isTrackExpenseReport; - const isMoneyRequestOrReport = isMoneyRequestReport || isSingleTransactionView || isIOUReport(parentReport); + const isMoneyRequestOrReport = isMoneyRequestReport || isSingleTransactionView; const areHoldRequirementsMet = !isInvoiceReport && diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx index 96bbecddc053a..0d1aa64237527 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx @@ -108,6 +108,7 @@ import { deleteReportActionDraft, markCommentAsUnread, navigateToAndOpenChildReport, + openReport, readNewestAction, saveReportActionDraft, toggleEmojiReaction, @@ -327,8 +328,9 @@ const ContextMenuActions: ContextMenuAction[] = [ onPress: (closePopover, {reportID, reportAction, draftMessage}) => { if (isMoneyRequestAction(reportAction)) { hideContextMenu(false); - const originalReportID = getOriginalReportID(reportID, reportAction); - navigateToAndOpenChildReport(reportAction?.childReportID, reportAction, originalReportID); + const childReportID = reportAction?.childReportID; + openReport(childReportID); + Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(childReportID)); return; } const editAction = () => { diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index 109611e70a119..c9e2e97beac54 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -630,6 +630,8 @@ describe('actions/IOU', () => { let createdAction: OnyxEntry; let iouAction: OnyxEntry>; let transactionID: string | undefined; + let transactionThread: OnyxEntry; + let transactionThreadCreatedAction: OnyxEntry; mockFetch?.pause?.(); requestMoney({ report: {reportID: ''}, @@ -657,14 +659,16 @@ describe('actions/IOU', () => { callback: (allReports) => { Onyx.disconnect(connection); - // A chat report and an iou report should be created + // A chat report, a transaction thread, and an iou report should be created const chatReports = Object.values(allReports ?? {}).filter((report) => report?.type === CONST.REPORT.TYPE.CHAT); const iouReports = Object.values(allReports ?? {}).filter((report) => report?.type === CONST.REPORT.TYPE.IOU); - expect(Object.keys(chatReports).length).toBe(1); + expect(Object.keys(chatReports).length).toBe(2); expect(Object.keys(iouReports).length).toBe(1); const chatReport = chatReports.at(0); + const transactionThreadReport = chatReports.at(1); const iouReport = iouReports.at(0); iouReportID = iouReport?.reportID; + transactionThread = transactionThreadReport; expect(iouReport?.participants).toEqual({ [RORY_ACCOUNT_ID]: {notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN}, @@ -727,6 +731,29 @@ describe('actions/IOU', () => { }); }), ) + .then( + () => + new Promise((resolve) => { + const connection = Onyx.connect({ + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThread?.reportID}`, + waitForCollectionCallback: false, + callback: (reportActionsForTransactionThread) => { + Onyx.disconnect(connection); + + // The transaction thread should have a CREATED action + expect(Object.values(reportActionsForTransactionThread ?? {}).length).toBe(1); + const createdActions = Object.values(reportActionsForTransactionThread ?? {}).filter( + (reportAction) => reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED, + ); + expect(Object.values(createdActions).length).toBe(1); + transactionThreadCreatedAction = createdActions.at(0); + + expect(transactionThreadCreatedAction?.pendingAction).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD); + resolve(); + }, + }); + }), + ) .then( () => new Promise((resolve) => { @@ -1189,6 +1216,8 @@ describe('actions/IOU', () => { let createdAction: OnyxEntry; let iouAction: OnyxEntry>; let transactionID: string | undefined; + let transactionThreadReport: OnyxEntry; + let transactionThreadAction: OnyxEntry; mockFetch?.pause?.(); requestMoney({ report: {reportID: ''}, @@ -1217,13 +1246,14 @@ describe('actions/IOU', () => { callback: (allReports) => { Onyx.disconnect(connection); - // A chat report and an iou report should be created + // A chat report, transaction thread and an iou report should be created const chatReports = Object.values(allReports ?? {}).filter((report) => report?.type === CONST.REPORT.TYPE.CHAT); const iouReports = Object.values(allReports ?? {}).filter((report) => report?.type === CONST.REPORT.TYPE.IOU); - expect(Object.values(chatReports).length).toBe(1); + expect(Object.values(chatReports).length).toBe(2); expect(Object.values(iouReports).length).toBe(1); const chatReport = chatReports.at(0); chatReportID = chatReport?.reportID; + transactionThreadReport = chatReports.at(1); const iouReport = iouReports.at(0); iouReportID = iouReport?.reportID; @@ -1336,6 +1366,24 @@ describe('actions/IOU', () => { }); }), ) + .then( + () => + new Promise((resolve) => { + const connection = Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, + waitForCollectionCallback: true, + callback: (reportActionsForTransactionThread) => { + Onyx.disconnect(connection); + expect(Object.values(reportActionsForTransactionThread ?? {}).length).toBe(3); + transactionThreadAction = Object.values( + reportActionsForTransactionThread?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReport?.reportID}`] ?? {}, + ).find((reportAction) => reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED); + expect(transactionThreadAction?.pendingAction).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD); + resolve(); + }, + }); + }), + ) .then( () => new Promise((resolve) => { @@ -1402,6 +1450,22 @@ describe('actions/IOU', () => { }), ) + // Then the reportAction from transaction report should be removed from Onyx + .then( + () => + new Promise((resolve) => { + const connection = Onyx.connect({ + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReport?.reportID}`, + waitForCollectionCallback: false, + callback: (reportActionsForReport) => { + Onyx.disconnect(connection); + expect(reportActionsForReport).toMatchObject({}); + resolve(); + }, + }); + }), + ) + // Along with the associated transaction .then( () => @@ -1425,6 +1489,9 @@ describe('actions/IOU', () => { if (chatReportID) { deleteReport(chatReportID); } + if (transactionThreadReport?.reportID) { + deleteReport(transactionThreadReport?.reportID); + } resolve(); }), )