diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx index 7c5825f598d6..5bf6897c6cb2 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx @@ -3,7 +3,7 @@ import isEmpty from 'lodash/isEmpty'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {InteractionManager, View} from 'react-native'; import type {NativeScrollEvent, NativeSyntheticEvent} from 'react-native'; -import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; import {useOnyx} from 'react-native-onyx'; import FlatList from '@components/FlatList'; import {AUTOSCROLL_TO_TOP_THRESHOLD} from '@components/InvertedFlatList/BaseInvertedFlatList'; @@ -17,7 +17,6 @@ import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import {isActionVisibleOnMoneyRequestReport} from '@libs/MoneyRequestReportUtils'; import { getFirstVisibleReportActionID, - getIOUActionForTransactionID, getMostRecentIOURequestActionID, getOneTransactionThreadReportID, hasNextActionMadeBySameActor, @@ -39,7 +38,6 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; -import type Transaction from '@src/types/onyx/Transaction'; import MoneyRequestReportTransactionList from './MoneyRequestReportTransactionList'; import SearchMoneyRequestReportEmptyState from './SearchMoneyRequestReportEmptyState'; @@ -57,6 +55,9 @@ type MoneyRequestReportListProps = { /** Array of report actions for this report */ reportActions?: OnyxTypes.ReportAction[]; + /** List of transactions belonging to this report */ + transactions: OnyxTypes.Transaction[]; + /** If the report has newer actions to load */ hasNewerActions: boolean; @@ -71,21 +72,11 @@ function getParentReportAction(parentReportActions: OnyxEntry, reportID: string, reportActions: OnyxTypes.ReportAction[]) { - return Object.values(transactions ?? {}).filter((transaction): transaction is Transaction => { - if (!transaction) { - return false; - } - const action = getIOUActionForTransactionID(reportActions, transaction.transactionID); - return transaction.reportID === reportID && !isDeletedParentAction(action); - }); -} - /** * TODO make this component have the same functionalities as `ReportActionsList` * - shouldDisplayNewMarker */ -function MoneyRequestReportActionsList({report, reportActions = [], hasNewerActions, hasOlderActions}: MoneyRequestReportListProps) { +function MoneyRequestReportActionsList({report, reportActions = [], transactions = [], hasNewerActions, hasOlderActions}: MoneyRequestReportListProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const {preferredLocale} = useLocalize(); @@ -101,9 +92,6 @@ function MoneyRequestReportActionsList({report, reportActions = [], hasNewerActi const mostRecentIOUReportActionID = useMemo(() => getMostRecentIOURequestActionID(reportActions), [reportActions]); const transactionThreadReportID = getOneTransactionThreadReportID(reportID, reportActions ?? [], false); const firstVisibleReportActionID = useMemo(() => getFirstVisibleReportActionID(reportActions, isOffline), [reportActions, isOffline]); - const [transactions = []] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, { - selector: (allTransactions): OnyxTypes.Transaction[] => selectTransactionsForReportID(allTransactions, reportID, reportActions), - }); const [transactionThreadReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID ?? CONST.DEFAULT_NUMBER_ID}`); const [currentUserAccountID] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.accountID}); diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx index 4d57e972cac0..8a295949f185 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx @@ -1,6 +1,6 @@ import React, {useCallback} from 'react'; import {InteractionManager, View} from 'react-native'; -import type {OnyxEntry} from 'react-native-onyx'; +import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import {useOnyx} from 'react-native-onyx'; import HeaderGap from '@components/HeaderGap'; import MoneyReportHeader from '@components/MoneyReportHeader'; @@ -15,10 +15,11 @@ import {removeFailedReport} from '@libs/actions/Report'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import Log from '@libs/Log'; import navigationRef from '@libs/Navigation/navigationRef'; -import {isMoneyRequestAction} from '@libs/ReportActionsUtils'; +import {getIOUActionForTransactionID, getOneTransactionThreadReportID, isDeletedParentAction, isMoneyRequestAction} from '@libs/ReportActionsUtils'; import {canEditReportAction, getReportOfflinePendingActionAndErrors} from '@libs/ReportUtils'; import {buildCannedSearchQuery} from '@libs/SearchQueryUtils'; import Navigation from '@navigation/Navigation'; +import ReportActionsView from '@pages/home/report/ReportActionsView'; import ReportFooter from '@pages/home/report/ReportFooter'; import NAVIGATORS from '@src/NAVIGATORS'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -81,6 +82,20 @@ function getParentReportAction(parentReportActions: OnyxEntry, reportID: string | undefined, reportActions: OnyxTypes.ReportAction[]) { + if (!reportID) { + return []; + } + + return Object.values(transactions ?? {}).filter((transaction): transaction is OnyxTypes.Transaction => { + if (!transaction) { + return false; + } + const action = getIOUActionForTransactionID(reportActions, transaction.transactionID); + return transaction.reportID === reportID && !isDeletedParentAction(action); + }); +} + function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayReportFooter, backToRoute}: MoneyRequestReportViewProps) { const styles = useThemeStyles(); const {isOffline} = useNetwork(); @@ -92,6 +107,12 @@ function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayRe const {reportPendingAction, reportErrors} = getReportOfflinePendingActionAndErrors(report); const {reportActions, hasNewerActions, hasOlderActions} = usePaginatedReportActions(reportID); + const transactionThreadReportID = getOneTransactionThreadReportID(reportID, reportActions ?? [], isOffline); + + const [transactions = []] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, { + selector: (allTransactions): OnyxTypes.Transaction[] => selectTransactionsForReportID(allTransactions, reportID, reportActions), + }); + const shouldUseSingleTransactionView = transactions.length === 1; const [parentReportAction] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getNonEmptyStringOnyxID(report?.parentReportID)}`, { canEvict: false, @@ -118,6 +139,26 @@ function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayRe return; } + if (isLoadingApp) { + return ( + + + + + {shouldDisplayReportFooter ? ( + + ) : null} + + ); + } + return ( - {!isLoadingApp ? ( - { + if (!backToRoute) { + goBackFromSearchMoneyRequest(activeWorkspaceID); + return; + } + Navigation.goBack(backToRoute); + }} + /> + {shouldUseSingleTransactionView ? ( + // This component originally lives in ReportScreen, it is used here to handle the case when the report has a single transaction. Any other case will be handled by MoneyRequestReportActionsList + { - if (!backToRoute) { - goBackFromSearchMoneyRequest(activeWorkspaceID); - return; - } - Navigation.goBack(backToRoute); - }} + isLoadingInitialReportActions={reportMetadata?.isLoadingInitialReportActions} + hasNewerActions={hasNewerActions} + hasOlderActions={hasOlderActions} + parentReportAction={parentReportAction} + transactionThreadReportID={transactionThreadReportID} /> ) : ( - - )} - {!isLoadingApp ? ( - ) : ( - )} {shouldDisplayReportFooter ? ( , timeOfCreation: string) { +function buildOptimisticEmptyReport(reportID: string, accountID: number, parentReport: OnyxEntry, policy: OnyxEntry, timeOfCreation: string) { const {stateNum, statusNum} = getExpenseReportStateAndStatus(policy); const titleReportField = getTitleReportField(getReportFieldsByPolicyID(policy?.id) ?? {}); const optimisticEmptyReport: OptimisticNewReport = { @@ -5503,8 +5503,9 @@ function buildOptimisticEmptyReport(reportID: string, accountID: number, parentR participants: {}, lastVisibleActionCreated: timeOfCreation, pendingFields: {createReport: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}, - parentReportID, - chatReportID: parentReportID, + parentReportID: parentReport?.reportID, + chatReportID: parentReport?.reportID, + managerID: getSubmitToAccountID(policy, undefined), }; const optimisticReportName = populateOptimisticReportFormula(titleReportField?.defaultValue ?? CONST.POLICY.DEFAULT_REPORT_NAME_PATTERN, optimisticEmptyReport, policy); diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index b3cbb7406346..e8a431e2abe1 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2509,7 +2509,7 @@ function buildNewReportOptimisticData(policy: OnyxEntry, reportID: strin const {accountID, login} = creatorPersonalDetails; const timeOfCreation = DateUtils.getDBTime(); const parentReport = getPolicyExpenseChat(accountID, policy?.id); - const optimisticReportData = buildOptimisticEmptyReport(reportID, accountID, parentReport?.reportID, policy, timeOfCreation); + const optimisticReportData = buildOptimisticEmptyReport(reportID, accountID, parentReport, policy, timeOfCreation); const optimisticCreateAction = { action: CONST.REPORT.ACTIONS.TYPE.CREATED, diff --git a/tests/unit/NextStepUtilsTest.ts b/tests/unit/NextStepUtilsTest.ts index 480dab3d31d7..cd50848fbdca 100644 --- a/tests/unit/NextStepUtilsTest.ts +++ b/tests/unit/NextStepUtilsTest.ts @@ -77,7 +77,7 @@ describe('libs/NextStepUtils', () => { describe('it generates and optimistic nextStep once a report has been created', () => { test('Correct next steps message', () => { - const emptyReport = buildOptimisticEmptyReport('fake-empty-report-id-2', currentUserAccountID, 'fake-parent-report-id-3', policy, '2025-03-31 13:23:11'); + const emptyReport = buildOptimisticEmptyReport('fake-empty-report-id-2', currentUserAccountID, {reportID: 'fake-parent-report-id-3'}, policy, '2025-03-31 13:23:11'); optimisticNextStep.message = [ {