From 0492dc8acd51489767d703003da9567d97106386 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Fri, 4 Apr 2025 17:10:43 +0200 Subject: [PATCH 1/6] add single transaction view to MoneyRequestReportView --- .../MoneyRequestReportView.tsx | 70 ++++++++++++++----- 1 file changed, 54 insertions(+), 16 deletions(-) diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx index f53fa8f19e89..742e92e9377d 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx @@ -1,4 +1,5 @@ -import React from 'react'; +/* eslint-disable no-nested-ternary */ +import React, {useMemo} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {useOnyx} from 'react-native-onyx'; @@ -10,10 +11,12 @@ import useNetwork from '@hooks/useNetwork'; import usePaginatedReportActions from '@hooks/usePaginatedReportActions'; import useThemeStyles from '@hooks/useThemeStyles'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; -import {isMoneyRequestAction} from '@libs/ReportActionsUtils'; +import {getOneTransactionThreadReportID, isMoneyRequestAction} from '@libs/ReportActionsUtils'; import {canEditReportAction, getReportOfflinePendingActionAndErrors} from '@libs/ReportUtils'; import Navigation from '@navigation/Navigation'; +import ReportActionsView from '@pages/home/report/ReportActionsView'; import ReportFooter from '@pages/home/report/ReportFooter'; +import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; import type {ThemeStyles} from '@src/styles'; @@ -65,6 +68,17 @@ function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayRe const {reportPendingAction} = getReportOfflinePendingActionAndErrors(report); const {reportActions, hasNewerActions, hasOlderActions} = usePaginatedReportActions(reportID); + const transactionThreadReportID = getOneTransactionThreadReportID(reportID, reportActions ?? [], isOffline); + const shouldUseSingleTransactionView = useMemo( + () => + reportActions.reduce((acc, action) => { + if (action.actionName === CONST.REPORT.ACTIONS.TYPE.IOU) { + return acc + 1; + } + return acc; + }, 0) === 1, + [reportActions], + ); const [parentReportAction] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getNonEmptyStringOnyxID(report?.parentReportID)}`, { canEvict: false, @@ -86,32 +100,56 @@ function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayRe return; } + if (isLoadingApp) { + return ( + + + + + {shouldDisplayReportFooter ? ( + + ) : null} + + ); + } + return ( - {!isLoadingApp ? ( - { + Navigation.goBack(backToRoute); + }} + /> + {shouldUseSingleTransactionView ? ( + { - Navigation.goBack(backToRoute); - }} + reportActions={reportActions} + isLoadingInitialReportActions={reportMetadata?.isLoadingInitialReportActions} + hasNewerActions={hasNewerActions} + hasOlderActions={hasOlderActions} + parentReportAction={parentReportAction} + transactionThreadReportID={transactionThreadReportID} /> ) : ( - - )} - {!isLoadingApp ? ( - ) : ( - )} {shouldDisplayReportFooter ? ( Date: Mon, 7 Apr 2025 17:03:52 +0200 Subject: [PATCH 2/6] add code explanation comment --- src/components/MoneyRequestReportView/MoneyRequestReportView.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx index acfff9963e5d..1b7d50e3d8a5 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx @@ -179,6 +179,7 @@ function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayRe }} /> {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 Date: Tue, 8 Apr 2025 15:19:05 +0200 Subject: [PATCH 3/6] fix not able to create expense in offline created report --- src/libs/ReportUtils.ts | 7 ++++--- src/libs/actions/Report.ts | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 48ee39736d06..de673e2426d0 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5485,7 +5485,7 @@ function buildOptimisticExpenseReport( return expenseReport; } -function buildOptimisticEmptyReport(reportID: string, accountID: number, parentReportID: string | undefined, policy: OnyxEntry, 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 = { @@ -5502,8 +5502,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 39cae82f3e5a..d541aa28edc0 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2486,7 +2486,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, From 33f6b0a3b823d694705484b510b4bc3b6a190d30 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Tue, 8 Apr 2025 16:10:42 +0200 Subject: [PATCH 4/6] fix PR comments --- .../MoneyRequestReportView/MoneyRequestReportView.tsx | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx index 9dffe5b02560..a742237587f9 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx @@ -96,16 +96,7 @@ function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayRe const {reportActions, hasNewerActions, hasOlderActions} = usePaginatedReportActions(reportID); const transactionThreadReportID = getOneTransactionThreadReportID(reportID, reportActions ?? [], isOffline); - const shouldUseSingleTransactionView = useMemo( - () => - reportActions.reduce((acc, action) => { - if (action.actionName === CONST.REPORT.ACTIONS.TYPE.IOU) { - return acc + 1; - } - return acc; - }, 0) === 1, - [reportActions], - ); + const shouldUseSingleTransactionView = useMemo(() => reportActions.filter((action) => action.actionName === CONST.REPORT.ACTIONS.TYPE.IOU).length === 1, [reportActions]); const [parentReportAction] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getNonEmptyStringOnyxID(report?.parentReportID)}`, { canEvict: false, From 24212156ea82c74c042c19bf7a666e31d8f22381 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Wed, 9 Apr 2025 09:32:25 +0200 Subject: [PATCH 5/6] change shouldUseSingleTransactionView condition to work on transactions --- .../MoneyRequestReportActionsList.tsx | 22 ++++---------- .../MoneyRequestReportView.tsx | 29 +++++++++++++++---- 2 files changed, 28 insertions(+), 23 deletions(-) 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 a742237587f9..8a295949f185 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx @@ -1,7 +1,6 @@ -/* eslint-disable no-nested-ternary */ -import React, {useCallback, useMemo} from 'react'; +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'; @@ -16,13 +15,12 @@ import {removeFailedReport} from '@libs/actions/Report'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import Log from '@libs/Log'; import navigationRef from '@libs/Navigation/navigationRef'; -import {getOneTransactionThreadReportID, 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 CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; @@ -84,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(); @@ -96,7 +108,11 @@ function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayRe const {reportActions, hasNewerActions, hasOlderActions} = usePaginatedReportActions(reportID); const transactionThreadReportID = getOneTransactionThreadReportID(reportID, reportActions ?? [], isOffline); - const shouldUseSingleTransactionView = useMemo(() => reportActions.filter((action) => action.actionName === CONST.REPORT.ACTIONS.TYPE.IOU).length === 1, [reportActions]); + + 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, @@ -183,6 +199,7 @@ function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayRe ) : ( Date: Wed, 9 Apr 2025 09:37:12 +0200 Subject: [PATCH 6/6] fix typecheck --- tests/unit/NextStepUtilsTest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 = [ {