From c376da112232c98d0e6f13a51bf6298a9e2b5bf6 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Fri, 30 May 2025 15:59:19 +0200 Subject: [PATCH 01/30] create derived value for transactions by report --- src/ONYXKEYS.ts | 2 ++ .../useReportWithTransactionsAndViolations.ts | 20 ++++++------- .../OnyxDerived/ONYX_DERIVED_VALUES.ts | 2 ++ .../OnyxDerived/configs/reportTransactions.ts | 28 +++++++++++++++++++ src/types/onyx/DerivedValues.ts | 8 +++++- src/types/onyx/index.ts | 3 +- 6 files changed, 51 insertions(+), 12 deletions(-) create mode 100644 src/libs/actions/OnyxDerived/configs/reportTransactions.ts diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 7994a2b29b5c..86d52d25082a 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -833,6 +833,7 @@ const ONYXKEYS = { }, DERIVED: { REPORT_ATTRIBUTES: 'reportAttributes', + REPORT_TRANSACTIONS: 'reportTransactions', }, } as const; @@ -1182,6 +1183,7 @@ type OnyxValuesMapping = { type OnyxDerivedValuesMapping = { [ONYXKEYS.DERIVED.REPORT_ATTRIBUTES]: OnyxTypes.ReportAttributesDerivedValue; + [ONYXKEYS.DERIVED.REPORT_TRANSACTIONS]: OnyxTypes.ReportTransactionsDerivedValue; }; type OnyxValues = OnyxValuesMapping & OnyxCollectionValuesMapping & OnyxFormValuesMapping & OnyxFormDraftValuesMapping & OnyxDerivedValuesMapping; diff --git a/src/hooks/useReportWithTransactionsAndViolations.ts b/src/hooks/useReportWithTransactionsAndViolations.ts index f9308165fc85..76f6a6260c9a 100644 --- a/src/hooks/useReportWithTransactionsAndViolations.ts +++ b/src/hooks/useReportWithTransactionsAndViolations.ts @@ -1,5 +1,4 @@ import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; -import {reportTransactionsSelector} from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Report, Transaction, TransactionViolation} from '@src/types/onyx'; @@ -10,16 +9,17 @@ const DEFAULT_VIOLATIONS: Record = {}; function useReportWithTransactionsAndViolations(reportID?: string): [OnyxEntry, Transaction[], OnyxCollection] { const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID ?? CONST.DEFAULT_NUMBER_ID}`); - const [transactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, { - selector: (_transactions) => reportTransactionsSelector(_transactions, reportID), - }); + const [reportTransactions] = useOnyx(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS, {canBeMissing: true}); + const transactions = reportTransactions?.[reportID ?? CONST.DEFAULT_NUMBER_ID]; const [violations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, { - selector: (allViolations) => - Object.fromEntries( - Object.entries(allViolations ?? {}).filter(([key]) => - transactions?.some((transaction) => transaction.transactionID === key.replace(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, '')), - ), - ), + selector: (allViolations) => { + if (!transactions?.length) { + return {}; + } + + const transactionIDs = new Set(transactions.map((t) => t.transactionID)); + return Object.fromEntries(Object.entries(allViolations ?? {}).filter(([key]) => transactionIDs.has(key.replace(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, '')))); + }, }); return [report, transactions ?? DEFAULT_TRANSACTIONS, violations ?? DEFAULT_VIOLATIONS]; } diff --git a/src/libs/actions/OnyxDerived/ONYX_DERIVED_VALUES.ts b/src/libs/actions/OnyxDerived/ONYX_DERIVED_VALUES.ts index ff9758d3645e..8e311bf09099 100644 --- a/src/libs/actions/OnyxDerived/ONYX_DERIVED_VALUES.ts +++ b/src/libs/actions/OnyxDerived/ONYX_DERIVED_VALUES.ts @@ -1,6 +1,7 @@ import type {ValueOf} from 'type-fest'; import ONYXKEYS from '@src/ONYXKEYS'; import reportAttributesConfig from './configs/reportAttributes'; +import reportTransactionsConfig from './configs/reportTransactions'; import type {OnyxDerivedValueConfig} from './types'; /** @@ -9,6 +10,7 @@ import type {OnyxDerivedValueConfig} from './types'; */ const ONYX_DERIVED_VALUES = { [ONYXKEYS.DERIVED.REPORT_ATTRIBUTES]: reportAttributesConfig, + [ONYXKEYS.DERIVED.REPORT_TRANSACTIONS]: reportTransactionsConfig, } as const satisfies { // eslint-disable-next-line @typescript-eslint/no-explicit-any [Key in ValueOf]: OnyxDerivedValueConfig; diff --git a/src/libs/actions/OnyxDerived/configs/reportTransactions.ts b/src/libs/actions/OnyxDerived/configs/reportTransactions.ts new file mode 100644 index 000000000000..4aad60829014 --- /dev/null +++ b/src/libs/actions/OnyxDerived/configs/reportTransactions.ts @@ -0,0 +1,28 @@ +import createOnyxDerivedValueConfig from '@userActions/OnyxDerived/createOnyxDerivedValueConfig'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {ReportTransactionsDerivedValue} from '@src/types/onyx'; + +export default createOnyxDerivedValueConfig({ + key: ONYXKEYS.DERIVED.REPORT_TRANSACTIONS, + dependencies: [ONYXKEYS.COLLECTION.TRANSACTION], + compute: ([transactions]) => { + if (!transactions) { + return {}; + } + + return Object.values(transactions).reduce((acc, transaction) => { + const reportID = transaction?.reportID; + if (!reportID) { + return acc; + } + + if (!acc[reportID]) { + acc[reportID] = []; + } + + acc[reportID].push(transaction); + + return acc; + }, {}); + }, +}); diff --git a/src/types/onyx/DerivedValues.ts b/src/types/onyx/DerivedValues.ts index 1a396e81fb4c..8a6c1b69e41e 100644 --- a/src/types/onyx/DerivedValues.ts +++ b/src/types/onyx/DerivedValues.ts @@ -1,6 +1,7 @@ import type {ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; import type {Errors} from './OnyxCommon'; +import type Transaction from './Transaction'; /** * The attributes of a report. @@ -42,5 +43,10 @@ type ReportAttributesDerivedValue = { locale: string | null; }; +/** + * The derived value for report transactions. + */ +type ReportTransactionsDerivedValue = Record; + export default ReportAttributesDerivedValue; -export type {ReportAttributes}; +export type {ReportAttributes, ReportAttributesDerivedValue, ReportTransactionsDerivedValue}; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 7588584b8322..765fc6ce8f29 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -23,7 +23,7 @@ import type Credentials from './Credentials'; import type Currency from './Currency'; import type {CurrencyList} from './Currency'; import type CustomStatusDraft from './CustomStatusDraft'; -import type ReportAttributesDerivedValue from './DerivedValues'; +import type {ReportAttributesDerivedValue, ReportTransactionsDerivedValue} from './DerivedValues'; import type DismissedProductTraining from './DismissedProductTraining'; import type DismissedReferralBanners from './DismissedReferralBanners'; import type Download from './Download'; @@ -259,6 +259,7 @@ export type { SidePanel, LastPaymentMethodType, ReportAttributesDerivedValue, + ReportTransactionsDerivedValue, TalkToAISales, ScheduleCallDraft, ValidateUserAndGetAccessiblePolicies, From e5ba3bf0532ff1d2a8a7f75023c88de420fcb61f Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Tue, 3 Jun 2025 13:08:52 +0200 Subject: [PATCH 02/30] wip: report transactions derived value --- .../useReportWithTransactionsAndViolations.ts | 13 ++---------- .../OnyxDerived/configs/reportTransactions.ts | 20 +++++++++++++++---- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/hooks/useReportWithTransactionsAndViolations.ts b/src/hooks/useReportWithTransactionsAndViolations.ts index 76f6a6260c9a..141de209f195 100644 --- a/src/hooks/useReportWithTransactionsAndViolations.ts +++ b/src/hooks/useReportWithTransactionsAndViolations.ts @@ -8,19 +8,10 @@ const DEFAULT_TRANSACTIONS: Transaction[] = []; const DEFAULT_VIOLATIONS: Record = {}; function useReportWithTransactionsAndViolations(reportID?: string): [OnyxEntry, Transaction[], OnyxCollection] { - const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID ?? CONST.DEFAULT_NUMBER_ID}`); + const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID ?? CONST.DEFAULT_NUMBER_ID}`, {canBeMissing: true}); const [reportTransactions] = useOnyx(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS, {canBeMissing: true}); const transactions = reportTransactions?.[reportID ?? CONST.DEFAULT_NUMBER_ID]; - const [violations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, { - selector: (allViolations) => { - if (!transactions?.length) { - return {}; - } - - const transactionIDs = new Set(transactions.map((t) => t.transactionID)); - return Object.fromEntries(Object.entries(allViolations ?? {}).filter(([key]) => transactionIDs.has(key.replace(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, '')))); - }, - }); + const violations = transactions?.violations; return [report, transactions ?? DEFAULT_TRANSACTIONS, violations ?? DEFAULT_VIOLATIONS]; } diff --git a/src/libs/actions/OnyxDerived/configs/reportTransactions.ts b/src/libs/actions/OnyxDerived/configs/reportTransactions.ts index 4aad60829014..83266bdff626 100644 --- a/src/libs/actions/OnyxDerived/configs/reportTransactions.ts +++ b/src/libs/actions/OnyxDerived/configs/reportTransactions.ts @@ -4,13 +4,23 @@ import type {ReportTransactionsDerivedValue} from '@src/types/onyx'; export default createOnyxDerivedValueConfig({ key: ONYXKEYS.DERIVED.REPORT_TRANSACTIONS, - dependencies: [ONYXKEYS.COLLECTION.TRANSACTION], - compute: ([transactions]) => { + dependencies: [ONYXKEYS.COLLECTION.TRANSACTION, ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS], + compute: ([transactions, violations], {sourceValues}) => { if (!transactions) { return {}; } - return Object.values(transactions).reduce((acc, transaction) => { + let data = transactions; + if (sourceValues?.[ONYXKEYS.COLLECTION.TRANSACTION]) { + data = Object.keys(sourceValues?.[ONYXKEYS.COLLECTION.TRANSACTION]).map((key) => transactions[key]); + } + if (sourceValues?.[ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS]) { + data = Object.keys(sourceValues?.[ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS]).map( + (key) => transactions[key.replace(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, ONYXKEYS.COLLECTION.TRANSACTION)], + ); + } + + return Object.values(data).reduce((acc, transaction) => { const reportID = transaction?.reportID; if (!reportID) { return acc; @@ -20,7 +30,9 @@ export default createOnyxDerivedValueConfig({ acc[reportID] = []; } - acc[reportID].push(transaction); + const transactionID = transaction.transactionID; + const violationsForTransaction = violations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`]; + acc[reportID].push({...transaction, violations: violationsForTransaction}); return acc; }, {}); From bc5c2f6354065cdda25fa4a860021c9efcbaa0db Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Wed, 4 Jun 2025 15:30:01 +0200 Subject: [PATCH 03/30] wip: update components to use derived value --- .../MoneyRequestReportActionsList.tsx | 7 ++++++ .../MoneyRequestReportPreview/index.tsx | 5 +++-- .../MoneyRequestReportPreview/types.ts | 3 +++ .../ReportActionItem/ReportPreview.tsx | 8 +++++-- .../ReportActionItem/TripRoomPreview.tsx | 8 +++++-- src/hooks/useReportTransactionViolations.ts | 22 +++++++++++++++++++ .../useReportWithTransactionsAndViolations.ts | 18 --------------- src/hooks/useTripTransactions.ts | 16 +++----------- .../OnyxDerived/configs/reportTransactions.ts | 15 ++++--------- .../home/report/PureReportActionItem.tsx | 13 ++++++++++- src/pages/home/report/ReportActionsList.tsx | 18 ++++++++++----- .../report/ReportActionsListItemRenderer.tsx | 7 +++++- src/pages/home/report/TripSummary.tsx | 8 +++++-- 13 files changed, 90 insertions(+), 58 deletions(-) create mode 100644 src/hooks/useReportTransactionViolations.ts delete mode 100644 src/hooks/useReportWithTransactionsAndViolations.ts diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx index a9777ede65bd..b4fd4f907207 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx @@ -133,6 +133,11 @@ function MoneyRequestReportActionsList({ selector: (parentReportActions) => getParentReportAction(parentReportActions, report?.parentReportActionID), }); + const [reportTransactions] = useOnyx(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS, { + canBeMissing: true, + selector: (transactionsByReportID) => transactionsByReportID?.[report.reportID], + }); + const mostRecentIOUReportActionID = useMemo(() => getMostRecentIOURequestActionID(reportActions), [reportActions]); const transactionThreadReportID = getOneTransactionThreadReportID(reportID, reportActions ?? [], false); const firstVisibleReportActionID = useMemo(() => getFirstVisibleReportActionID(reportActions, isOffline), [reportActions, isOffline]); @@ -477,6 +482,7 @@ function MoneyRequestReportActionsList({ isFirstVisibleReportAction={firstVisibleReportActionID === reportAction.reportActionID} shouldHideThreadDividerLine linkedReportActionID={linkedReportActionID} + transactions={reportTransactions ?? []} /> ); }, @@ -490,6 +496,7 @@ function MoneyRequestReportActionsList({ unreadMarkerReportActionID, firstVisibleReportActionID, linkedReportActionID, + reportTransactions, ], ); diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx index dc8a7f984d0b..7a529d63bc44 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx @@ -4,7 +4,7 @@ import {useOnyx} from 'react-native-onyx'; import TransactionPreview from '@components/ReportActionItem/TransactionPreview'; import useDelegateUserDetails from '@hooks/useDelegateUserDetails'; import usePolicy from '@hooks/usePolicy'; -import useReportWithTransactionsAndViolations from '@hooks/useReportWithTransactionsAndViolations'; +import useReportTransactionViolations from '@hooks/useReportTransactionViolations'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -37,6 +37,7 @@ function MoneyRequestReportPreview({ shouldDisplayContextMenu = true, isInvoice = false, shouldShowBorder, + transactions, }: MoneyRequestReportPreviewProps) { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -51,7 +52,7 @@ function MoneyRequestReportPreview({ personalDetails?.[chatReport?.invoiceReceiver && 'accountID' in chatReport.invoiceReceiver ? chatReport.invoiceReceiver.accountID : CONST.DEFAULT_NUMBER_ID], canBeMissing: true, }); - const [iouReport, transactions, violations] = useReportWithTransactionsAndViolations(iouReportID); + const [iouReport, violations] = useReportTransactionViolations(iouReportID, transactions); const policy = usePolicy(policyID); const lastTransaction = transactions?.at(0); const lastTransactionViolations = useTransactionViolations(lastTransaction?.transactionID); diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/types.ts b/src/components/ReportActionItem/MoneyRequestReportPreview/types.ts index 2aadc99ed26e..98704214682a 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/types.ts +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/types.ts @@ -59,6 +59,9 @@ type MoneyRequestReportPreviewProps = { /** Whether to show a border to separate Reports Chat Item and Money Request Report Preview */ shouldShowBorder?: boolean; + + /** The transactions for the report */ + transactions: Transaction[]; }; type MoneyRequestReportPreviewContentOnyxProps = { diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 0847e1f68053..d5723e6ef5ad 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -25,7 +25,7 @@ import useNetwork from '@hooks/useNetwork'; import usePaymentAnimations from '@hooks/usePaymentAnimations'; import usePolicy from '@hooks/usePolicy'; import useReportIsArchived from '@hooks/useReportIsArchived'; -import useReportWithTransactionsAndViolations from '@hooks/useReportWithTransactionsAndViolations'; +import useReportTransactionViolations from '@hooks/useReportTransactionViolations'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useTransactionViolations from '@hooks/useTransactionViolations'; @@ -140,6 +140,9 @@ type ReportPreviewProps = { /** Whether context menu should be shown on press */ shouldDisplayContextMenu?: boolean; + + /** The transactions for the report */ + transactions: Transaction[]; }; function ReportPreview({ @@ -156,10 +159,11 @@ function ReportPreview({ onPaymentOptionsHide, onShowContextMenu = () => {}, shouldDisplayContextMenu = true, + transactions, }: ReportPreviewProps) { const policy = usePolicy(policyID); const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`, {canBeMissing: false}); - const [iouReport, transactions, violations] = useReportWithTransactionsAndViolations(iouReportID); + const [iouReport, violations] = useReportTransactionViolations(iouReportID, transactions); const isIouReportArchived = useReportIsArchived(iouReportID); const lastTransaction = transactions?.at(0); const transactionIDList = transactions?.map((reportTransaction) => reportTransaction.transactionID) ?? []; diff --git a/src/components/ReportActionItem/TripRoomPreview.tsx b/src/components/ReportActionItem/TripRoomPreview.tsx index 616bbfd8bd9e..b53bc5aa8de3 100644 --- a/src/components/ReportActionItem/TripRoomPreview.tsx +++ b/src/components/ReportActionItem/TripRoomPreview.tsx @@ -29,7 +29,7 @@ import * as Expensicons from '@src/components/Icon/Expensicons'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {ReportAction} from '@src/types/onyx'; +import type {ReportAction, Transaction} from '@src/types/onyx'; import type {Reservation} from '@src/types/onyx/Transaction'; type TripRoomPreviewProps = { @@ -53,6 +53,9 @@ type TripRoomPreviewProps = { /** Whether context menu should be shown on press */ shouldDisplayContextMenu?: boolean; + + /** The transactions for the report */ + transactions: Transaction[]; }; type ReservationViewProps = { @@ -125,12 +128,13 @@ function TripRoomPreview({ isHovered = false, checkIfContextMenuActive = () => {}, shouldDisplayContextMenu = true, + transactions, }: TripRoomPreviewProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`, {canBeMissing: true}); const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReport?.iouReportID}`, {canBeMissing: true}); - const tripTransactions = useTripTransactions(chatReportID); + const tripTransactions = useTripTransactions(chatReportID, transactions); const reservationsData: ReservationData[] = getReservationsFromTripTransactions(tripTransactions); const dateInfo = diff --git a/src/hooks/useReportTransactionViolations.ts b/src/hooks/useReportTransactionViolations.ts new file mode 100644 index 000000000000..55908a0d83ce --- /dev/null +++ b/src/hooks/useReportTransactionViolations.ts @@ -0,0 +1,22 @@ +import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {Report, Transaction, TransactionViolation} from '@src/types/onyx'; +import useOnyx from './useOnyx'; + +const DEFAULT_VIOLATIONS: Record = {}; + +function useReportTransactionViolations(reportID?: string, reportTransactions?: Transaction[]): [OnyxEntry, OnyxCollection] { + const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID ?? CONST.DEFAULT_NUMBER_ID}`, {canBeMissing: true}); + const [violations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, { + selector: (allViolations) => + Object.fromEntries( + Object.entries(allViolations ?? {}).filter(([key]) => + reportTransactions?.some((transaction) => transaction.transactionID === key.replace(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, '')), + ), + ), + }); + return [report, violations ?? DEFAULT_VIOLATIONS]; +} + +export default useReportTransactionViolations; diff --git a/src/hooks/useReportWithTransactionsAndViolations.ts b/src/hooks/useReportWithTransactionsAndViolations.ts deleted file mode 100644 index 141de209f195..000000000000 --- a/src/hooks/useReportWithTransactionsAndViolations.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import type {Report, Transaction, TransactionViolation} from '@src/types/onyx'; -import useOnyx from './useOnyx'; - -const DEFAULT_TRANSACTIONS: Transaction[] = []; -const DEFAULT_VIOLATIONS: Record = {}; - -function useReportWithTransactionsAndViolations(reportID?: string): [OnyxEntry, Transaction[], OnyxCollection] { - const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID ?? CONST.DEFAULT_NUMBER_ID}`, {canBeMissing: true}); - const [reportTransactions] = useOnyx(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS, {canBeMissing: true}); - const transactions = reportTransactions?.[reportID ?? CONST.DEFAULT_NUMBER_ID]; - const violations = transactions?.violations; - return [report, transactions ?? DEFAULT_TRANSACTIONS, violations ?? DEFAULT_VIOLATIONS]; -} - -export default useReportWithTransactionsAndViolations; diff --git a/src/hooks/useTripTransactions.ts b/src/hooks/useTripTransactions.ts index 2260097a4112..0babd18dfa2e 100644 --- a/src/hooks/useTripTransactions.ts +++ b/src/hooks/useTripTransactions.ts @@ -12,26 +12,16 @@ import type {Transaction} from '@src/types/onyx'; * @param reportID - The trip room's reportID. * @returns Transactions linked to the specified trip room. */ -function useTripTransactions(reportID: string | undefined): Transaction[] { +function useTripTransactions(reportID: string | undefined, reportTransactions: Transaction[]): Transaction[] { const [tripTransactionReportIDs = []] = useOnyx(ONYXKEYS.COLLECTION.REPORT, { selector: (reports) => Object.values(reports ?? {}) .filter((report) => report && report.chatReportID === reportID) .map((report) => report?.reportID), }); - const [tripTransactions = []] = useOnyx( - ONYXKEYS.COLLECTION.TRANSACTION, - { - selector: (transactions) => { - if (!tripTransactionReportIDs.length) { - return []; - } - return Object.values(transactions ?? {}).filter((transaction): transaction is Transaction => !!transaction && tripTransactionReportIDs.includes(transaction.reportID)); - }, - }, - [tripTransactionReportIDs], - ); + const tripTransactions = tripTransactionReportIDs.flatMap((transactionReportID) => reportTransactions[transactionReportID ?? 0] ?? []); + return tripTransactions; } diff --git a/src/libs/actions/OnyxDerived/configs/reportTransactions.ts b/src/libs/actions/OnyxDerived/configs/reportTransactions.ts index 83266bdff626..1c5497686d19 100644 --- a/src/libs/actions/OnyxDerived/configs/reportTransactions.ts +++ b/src/libs/actions/OnyxDerived/configs/reportTransactions.ts @@ -4,8 +4,8 @@ import type {ReportTransactionsDerivedValue} from '@src/types/onyx'; export default createOnyxDerivedValueConfig({ key: ONYXKEYS.DERIVED.REPORT_TRANSACTIONS, - dependencies: [ONYXKEYS.COLLECTION.TRANSACTION, ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS], - compute: ([transactions, violations], {sourceValues}) => { + dependencies: [ONYXKEYS.COLLECTION.TRANSACTION], + compute: ([transactions], {sourceValues, currentValue}) => { if (!transactions) { return {}; } @@ -14,11 +14,6 @@ export default createOnyxDerivedValueConfig({ if (sourceValues?.[ONYXKEYS.COLLECTION.TRANSACTION]) { data = Object.keys(sourceValues?.[ONYXKEYS.COLLECTION.TRANSACTION]).map((key) => transactions[key]); } - if (sourceValues?.[ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS]) { - data = Object.keys(sourceValues?.[ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS]).map( - (key) => transactions[key.replace(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, ONYXKEYS.COLLECTION.TRANSACTION)], - ); - } return Object.values(data).reduce((acc, transaction) => { const reportID = transaction?.reportID; @@ -30,11 +25,9 @@ export default createOnyxDerivedValueConfig({ acc[reportID] = []; } - const transactionID = transaction.transactionID; - const violationsForTransaction = violations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`]; - acc[reportID].push({...transaction, violations: violationsForTransaction}); + acc[reportID].push(transaction); return acc; - }, {}); + }, currentValue ?? {}); }, }); diff --git a/src/pages/home/report/PureReportActionItem.tsx b/src/pages/home/report/PureReportActionItem.tsx index 0f4285c4b8d9..33a3f61526fc 100644 --- a/src/pages/home/report/PureReportActionItem.tsx +++ b/src/pages/home/report/PureReportActionItem.tsx @@ -181,6 +181,9 @@ type PureReportActionItemProps = { /** The transaction thread report associated with the report for this action, if any */ transactionThreadReport?: OnyxEntry; + /** The transactions for the report */ + transactions: OnyxTypes.Transaction[]; + /** Array of report actions for the report for this action */ // eslint-disable-next-line react/no-unused-prop-types reportActions: OnyxTypes.ReportAction[]; @@ -356,6 +359,7 @@ function PureReportActionItem({ action, report, transactionThreadReport, + transactions, linkedReportActionID, displayAsGroup, index, @@ -879,6 +883,7 @@ function PureReportActionItem({ containerStyles={displayAsGroup ? [] : [styles.mt2]} checkIfContextMenuActive={toggleContextMenuFromActiveReportAction} shouldDisplayContextMenu={shouldDisplayContextMenu} + transactions={transactions} /> ); } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW && isClosedExpenseReportWithNoExpenses) { @@ -899,6 +904,7 @@ function PureReportActionItem({ onPaymentOptionsHide={() => setIsPaymentMethodPopoverActive(false)} shouldDisplayContextMenu={shouldDisplayContextMenu} shouldShowBorder={shouldShowBorder} + transactions={transactions} /> ); } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW) { @@ -1330,7 +1336,12 @@ function PureReportActionItem({ } if (isTripPreview(action) && isThreadReportParentAction) { - return ; + return ( + + ); } if (isChronosOOOListAction(action)) { diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index 14bb612fa872..bc1657da0ba2 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -167,6 +167,10 @@ function ReportActionsList({ const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`, {canBeMissing: true}); const [accountID] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.accountID, canBeMissing: true}); + const [reportTransactions] = useOnyx(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS, { + canBeMissing: true, + selector: (transactions) => transactions?.[report.reportID], + }); const participantsContext = useContext(PersonalDetailsContext); const [isScrollToBottomEnabled, setIsScrollToBottomEnabled] = useState(false); @@ -583,6 +587,7 @@ function ReportActionsList({ index={index} report={report} transactionThreadReport={transactionThreadReport} + transactions={reportTransactions ?? []} linkedReportActionID={linkedReportActionID} displayAsGroup={ !isConsecutiveChronosAutomaticTimerAction(sortedVisibleReportActions, index, chatIncludesChronosWithID(reportAction?.reportID)) && @@ -598,18 +603,19 @@ function ReportActionsList({ ); }, [ + sortedReportActions, + parentReportAction, + parentReportActionForTransactionThread, report, + transactionThreadReport, linkedReportActionID, sortedVisibleReportActions, mostRecentIOUReportActionID, shouldHideThreadDividerLine, - parentReportAction, - sortedReportActions, - transactionThreadReport, - parentReportActionForTransactionThread, - shouldUseThreadDividerLine, - firstVisibleReportActionID, unreadMarkerReportActionID, + firstVisibleReportActionID, + shouldUseThreadDividerLine, + reportTransactions, ], ); diff --git a/src/pages/home/report/ReportActionsListItemRenderer.tsx b/src/pages/home/report/ReportActionsListItemRenderer.tsx index 2d61ae7f7fbb..89a8ac15a88c 100644 --- a/src/pages/home/report/ReportActionsListItemRenderer.tsx +++ b/src/pages/home/report/ReportActionsListItemRenderer.tsx @@ -3,7 +3,7 @@ import type {OnyxEntry} from 'react-native-onyx'; import {getOriginalMessage, isSentMoneyReportAction, isTransactionThread} from '@libs/ReportActionsUtils'; import {isChatThread, isInvoiceRoom, isPolicyExpenseChat} from '@libs/ReportUtils'; import CONST from '@src/CONST'; -import type {Report, ReportAction} from '@src/types/onyx'; +import type {Report, ReportAction, Transaction} from '@src/types/onyx'; import ReportActionItem from './ReportActionItem'; import ReportActionItemParentAction from './ReportActionItemParentAction'; @@ -29,6 +29,9 @@ type ReportActionsListItemRendererProps = { /** The transaction thread report associated with the report for this action, if any */ transactionThreadReport: OnyxEntry; + /** The transactions for the report */ + transactions: Transaction[]; + /** Should the comment have the appearance of being grouped with the previous comment? */ displayAsGroup: boolean; @@ -61,6 +64,7 @@ function ReportActionsListItemRenderer({ index, report, transactionThreadReport, + transactions, displayAsGroup, mostRecentIOUReportActionID = '', shouldHideThreadDividerLine, @@ -187,6 +191,7 @@ function ReportActionsListItemRenderer({ index={index} isFirstVisibleReportAction={isFirstVisibleReportAction} shouldUseThreadDividerLine={shouldUseThreadDividerLine} + transactions={transactions} /> ); } diff --git a/src/pages/home/report/TripSummary.tsx b/src/pages/home/report/TripSummary.tsx index 89abed699cc7..cdca287f6fe5 100644 --- a/src/pages/home/report/TripSummary.tsx +++ b/src/pages/home/report/TripSummary.tsx @@ -5,15 +5,19 @@ import TripDetailsView from '@components/ReportActionItem/TripDetailsView'; import useTripTransactions from '@hooks/useTripTransactions'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import type {Transaction} from '@src/types/onyx'; type TripSummaryProps = { /** The report ID */ reportID: string | undefined; + + /** The transactions for the report */ + transactions: Transaction[]; }; -function TripSummary({reportID}: TripSummaryProps) { +function TripSummary({reportID, transactions}: TripSummaryProps) { const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID ?? CONST.DEFAULT_NUMBER_ID}`); - const tripTransactions = useTripTransactions(reportID); + const tripTransactions = useTripTransactions(reportID, transactions); if (!reportID || tripTransactions.length === 0) { return null; From 1d1948742645c45feac3cc217997d82aeb93963a Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Wed, 4 Jun 2025 15:57:53 +0200 Subject: [PATCH 04/30] replace selectAllTransactionsForReport --- .../MoneyRequestReportView.tsx | 6 ++-- src/libs/MoneyRequestReportUtils.ts | 31 ++----------------- .../OnyxDerived/configs/reportTransactions.ts | 11 ++----- src/pages/home/ReportScreen.tsx | 6 ++-- 4 files changed, 12 insertions(+), 42 deletions(-) diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx index 5024b145c332..d9fbb721a185 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx @@ -16,7 +16,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import {removeFailedReport} from '@libs/actions/Report'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import Log from '@libs/Log'; -import {selectAllTransactionsForReport, shouldDisplayReportTableView} from '@libs/MoneyRequestReportUtils'; +import {shouldDisplayReportTableView} from '@libs/MoneyRequestReportUtils'; import navigationRef from '@libs/Navigation/navigationRef'; import {getFilteredReportActionsForReportView, getOneTransactionThreadReportID, isMoneyRequestAction} from '@libs/ReportActionsUtils'; import {canEditReportAction, getReportOfflinePendingActionAndErrors, isReportTransactionThread} from '@libs/ReportUtils'; @@ -99,8 +99,8 @@ function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayRe const transactionThreadReportID = getOneTransactionThreadReportID(reportID, reportActions ?? [], isOffline); - const [transactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, { - selector: (allTransactions: OnyxCollection) => selectAllTransactionsForReport(allTransactions, reportID, reportActions), + const [transactions] = useOnyx(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS, { + selector: (allTransactions): OnyxTypes.Transaction[] => allTransactions?.[reportID] ?? [], canBeMissing: true, }); diff --git a/src/libs/MoneyRequestReportUtils.ts b/src/libs/MoneyRequestReportUtils.ts index dcbb7f1aa848..2c43d26f33c1 100644 --- a/src/libs/MoneyRequestReportUtils.ts +++ b/src/libs/MoneyRequestReportUtils.ts @@ -1,9 +1,9 @@ -import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; import type {OriginalMessageIOU, Policy, Report, ReportAction, Transaction} from '@src/types/onyx'; import {convertToDisplayString} from './CurrencyUtils'; -import {getIOUActionForTransactionID, getOriginalMessage, isDeletedParentAction, isMoneyRequestAction} from './ReportActionsUtils'; +import {getIOUActionForTransactionID, getOriginalMessage, isMoneyRequestAction} from './ReportActionsUtils'; import { getMoneyRequestSpendBreakdown, getNonHeldAndFullAmount, @@ -49,24 +49,6 @@ function getThreadReportIDsForTransactions(reportActions: ReportAction[], transa .filter((reportID): reportID is string => !!reportID); } -/** - * Filters all available transactions and returns the ones that belong to a specific report (by `reportID`). - * It is used as an onyx selector, to make sure that report related views do not process all transactions in onyx. - */ -function selectAllTransactionsForReport(transactions: OnyxCollection, reportID: string | undefined, reportActions: ReportAction[]) { - if (!reportID) { - return []; - } - - 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); - }); -} - /** * Given a list of transaction, this function checks if a given report has exactly one transaction * @@ -127,11 +109,4 @@ const getTotalAmountForIOUReportPreviewButton = (report: OnyxEntry, poli return convertToDisplayString(totalDisplaySpend, report?.currency); }; -export { - isActionVisibleOnMoneyRequestReport, - getThreadReportIDsForTransactions, - getTotalAmountForIOUReportPreviewButton, - selectAllTransactionsForReport, - isSingleTransactionReport, - shouldDisplayReportTableView, -}; +export {isActionVisibleOnMoneyRequestReport, getThreadReportIDsForTransactions, getTotalAmountForIOUReportPreviewButton, isSingleTransactionReport, shouldDisplayReportTableView}; diff --git a/src/libs/actions/OnyxDerived/configs/reportTransactions.ts b/src/libs/actions/OnyxDerived/configs/reportTransactions.ts index 1c5497686d19..4aad60829014 100644 --- a/src/libs/actions/OnyxDerived/configs/reportTransactions.ts +++ b/src/libs/actions/OnyxDerived/configs/reportTransactions.ts @@ -5,17 +5,12 @@ import type {ReportTransactionsDerivedValue} from '@src/types/onyx'; export default createOnyxDerivedValueConfig({ key: ONYXKEYS.DERIVED.REPORT_TRANSACTIONS, dependencies: [ONYXKEYS.COLLECTION.TRANSACTION], - compute: ([transactions], {sourceValues, currentValue}) => { + compute: ([transactions]) => { if (!transactions) { return {}; } - let data = transactions; - if (sourceValues?.[ONYXKEYS.COLLECTION.TRANSACTION]) { - data = Object.keys(sourceValues?.[ONYXKEYS.COLLECTION.TRANSACTION]).map((key) => transactions[key]); - } - - return Object.values(data).reduce((acc, transaction) => { + return Object.values(transactions).reduce((acc, transaction) => { const reportID = transaction?.reportID; if (!reportID) { return acc; @@ -28,6 +23,6 @@ export default createOnyxDerivedValueConfig({ acc[reportID].push(transaction); return acc; - }, currentValue ?? {}); + }, {}); }, }); diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 2abc3760b3e6..df157f0aecaf 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -31,7 +31,7 @@ import useViewportOffsetTop from '@hooks/useViewportOffsetTop'; import {hideEmojiPicker} from '@libs/actions/EmojiPickerAction'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import Log from '@libs/Log'; -import {selectAllTransactionsForReport, shouldDisplayReportTableView} from '@libs/MoneyRequestReportUtils'; +import {shouldDisplayReportTableView} from '@libs/MoneyRequestReportUtils'; import Navigation, {navigationRef} from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import clearReportNotifications from '@libs/Notification/clearReportNotifications'; @@ -299,8 +299,8 @@ function ReportScreen({route, navigation}: ReportScreenProps) { // OpenReport will be called each time the user scrolls up the report a bit, clicks on report preview, and then goes back." const isLinkedMessagePageReady = isLinkedMessageAvailable && (reportActions.length - indexOfLinkedMessage >= CONST.REPORT.MIN_INITIAL_REPORT_ACTION_COUNT || doesCreatedActionExists()); - const [reportTransactions = []] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, { - selector: (allTransactions): OnyxTypes.Transaction[] => selectAllTransactionsForReport(allTransactions, reportIDFromRoute, reportActions), + const [reportTransactions = []] = useOnyx(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS, { + selector: (allTransactions): OnyxTypes.Transaction[] => allTransactions?.[reportIDFromRoute] ?? [], canBeMissing: false, }); const transactionThreadReportID = getOneTransactionThreadReportID(reportID, reportActions ?? [], isOffline); From 131b89b95f65db540ba879187ec2940e60bf60e4 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Wed, 4 Jun 2025 16:07:16 +0200 Subject: [PATCH 05/30] use derived value in report utils --- src/libs/ReportUtils.ts | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 8a9c85cb0406..dd0f4808d814 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -970,29 +970,22 @@ Onyx.connect({ }); let allTransactions: OnyxCollection = {}; -let reportsTransactions: Record = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION, waitForCollectionCallback: true, + callback: (value) => { + allTransactions = Object.fromEntries(Object.entries(value).filter(([, transaction]) => transaction)); + }, +}); + +let reportsTransactions: Record = {}; +Onyx.connect({ + key: ONYXKEYS.DERIVED.REPORT_TRANSACTIONS, callback: (value) => { if (!value) { return; } - allTransactions = Object.fromEntries(Object.entries(value).filter(([, transaction]) => transaction)); - - reportsTransactions = Object.values(value).reduce>((all, transaction) => { - const reportsMap = all; - if (!transaction?.reportID) { - return reportsMap; - } - - if (!reportsMap[transaction.reportID]) { - reportsMap[transaction.reportID] = []; - } - reportsMap[transaction.reportID].push(transaction); - - return all; - }, {}); + reportsTransactions = value; }, }); From 831d437dc087aec9ee8b1bcedd9843bf30f90d3b Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Thu, 5 Jun 2025 08:56:49 +0200 Subject: [PATCH 06/30] remove selectAllTransactionsForReport from ReportActionsView --- src/pages/home/report/ReportActionsView.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx index c0679a28f2b1..cc6efaa88fab 100755 --- a/src/pages/home/report/ReportActionsView.tsx +++ b/src/pages/home/report/ReportActionsView.tsx @@ -13,7 +13,6 @@ import {updateLoadingInitialReportAction} from '@libs/actions/Report'; import Timing from '@libs/actions/Timing'; import DateUtils from '@libs/DateUtils'; import getIsReportFullyVisible from '@libs/getIsReportFullyVisible'; -import {selectAllTransactionsForReport} from '@libs/MoneyRequestReportUtils'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; import type {ReportsSplitNavigatorParamList} from '@libs/Navigation/types'; import {generateNewRandomInt, rand64} from '@libs/NumberUtils'; @@ -95,9 +94,8 @@ function ReportActionsView({ const prevShouldUseNarrowLayoutRef = useRef(shouldUseNarrowLayout); const reportID = report.reportID; const isReportFullyVisible = useMemo((): boolean => getIsReportFullyVisible(isFocused), [isFocused]); - const [reportTransactionIDs] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, { - selector: (allTransactions: OnyxCollection) => - selectAllTransactionsForReport(allTransactions, reportID, allReportActions ?? []).map((transaction) => transaction.transactionID), + const [reportTransactionIDs] = useOnyx(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS, { + selector: (allTransactions: OnyxCollection) => allTransactions[reportID]?.map((transaction) => transaction.transactionID), canBeMissing: true, }); From e4aa0d3e9f3eaaa53a6e8bf6fccbc1d029c69cc8 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Thu, 5 Jun 2025 10:33:33 +0200 Subject: [PATCH 07/30] cleanup transaction calls --- Mobile-Expensify | 2 +- .../MoneyRequestReportActionsList.tsx | 29 +++++++-------- .../MoneyRequestReportView.tsx | 35 +++++++++---------- .../Search/SearchMoneyRequestReportPage.tsx | 6 ++++ src/pages/home/ReportScreen.tsx | 7 ++-- src/pages/home/report/ReportActionsList.tsx | 10 +++--- src/pages/home/report/ReportActionsView.tsx | 12 ++++--- 7 files changed, 52 insertions(+), 49 deletions(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index ed98bbf74038..5271a4ae97f9 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit ed98bbf740382004f894267a9bd0c7a8096416b8 +Subproject commit 5271a4ae97f95f32d60b68852f38dacc7770fc94 diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx index bdf96c82a449..01f68cdbfefb 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx @@ -83,7 +83,7 @@ type MoneyRequestReportListProps = { reportActions?: OnyxTypes.ReportAction[]; /** List of transactions belonging to this report */ - transactions?: OnyxTypes.Transaction[]; + reportTransactions?: OnyxTypes.Transaction[]; /** List of transactions that arrived when the report was open */ newTransactions: OnyxTypes.Transaction[]; @@ -109,7 +109,7 @@ function MoneyRequestReportActionsList({ report, policy, reportActions = [], - transactions = [], + reportTransactions = [], newTransactions, hasNewerActions, hasOlderActions, @@ -124,7 +124,7 @@ function MoneyRequestReportActionsList({ const [isVisible, setIsVisible] = useState(Visibility.isVisible); const isFocused = useIsFocused(); const route = useRoute>(); - const reportTransactionIDs = transactions.map((transaction) => transaction.transactionID); + const reportTransactionIDs = reportTransactions.map((transaction) => transaction.transactionID); const reportID = report?.reportID; const linkedReportActionID = route?.params?.reportActionID; @@ -135,11 +135,6 @@ function MoneyRequestReportActionsList({ selector: (parentReportActions) => getParentReportAction(parentReportActions, report?.parentReportActionID), }); - const [reportTransactions] = useOnyx(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS, { - canBeMissing: true, - selector: (transactionsByReportID) => transactionsByReportID?.[report.reportID], - }); - const mostRecentIOUReportActionID = useMemo(() => getMostRecentIOURequestActionID(reportActions), [reportActions]); const transactionThreadReportID = getOneTransactionThreadReportID(reportID, reportActions ?? [], false, reportTransactionIDs); const firstVisibleReportActionID = useMemo(() => getFirstVisibleReportActionID(reportActions, isOffline), [reportActions, isOffline]); @@ -161,7 +156,7 @@ function MoneyRequestReportActionsList({ handleDeleteTransactions, isDeleteModalVisible, hideDeleteModal, - } = useSelectedTransactionsActions({report, reportActions, allTransactionsLength: transactions.length, session, onExportFailed: () => setIsDownloadErrorModalVisible(true)}); + } = useSelectedTransactionsActions({report, reportActions, allTransactionsLength: reportTransactions.length, session, onExportFailed: () => setIsDownloadErrorModalVisible(true)}); // We are reversing actions because in this View we are starting at the top and don't use Inverted list const visibleReportActions = useMemo(() => { @@ -552,28 +547,28 @@ function MoneyRequestReportActionsList({ 0 && selectedTransactionIDs.length !== transactions.length} + isChecked={selectedTransactionIDs.length === reportTransactions.length} + isIndeterminate={selectedTransactionIDs.length > 0 && selectedTransactionIDs.length !== reportTransactions.length} onPress={() => { if (selectedTransactionIDs.length !== 0) { clearSelectedTransactions(true); } else { - setSelectedTransactions(transactions.filter((t) => !isTransactionPendingDelete(t)).map((t) => t.transactionID)); + setSelectedTransactions(reportTransactions.filter((t) => !isTransactionPendingDelete(t)).map((t) => t.transactionID)); } }} /> { - if (selectedTransactionIDs.length === transactions.length) { + if (selectedTransactionIDs.length === reportTransactions.length) { clearSelectedTransactions(true); } else { - setSelectedTransactions(transactions.filter((t) => !isTransactionPendingDelete(t)).map((t) => t.transactionID)); + setSelectedTransactions(reportTransactions.filter((t) => !isTransactionPendingDelete(t)).map((t) => t.transactionID)); } }} accessibilityLabel={translate('workspace.people.selectAll')} role="button" - accessibilityState={{checked: selectedTransactionIDs.length === transactions.length}} + accessibilityState={{checked: selectedTransactionIDs.length === reportTransactions.length}} dataSet={{[CONST.SELECTION_SCRAPER_HIDDEN_ELEMENT]: true}} > {translate('workspace.people.selectAll')} @@ -597,7 +592,7 @@ function MoneyRequestReportActionsList({ isActive={isFloatingMessageCounterVisible} onClick={scrollToBottomAndMarkReportAsRead} /> - {isEmpty(visibleReportActions) && isEmpty(transactions) && !isLoadingInitialReportActions ? ( + {isEmpty(visibleReportActions) && isEmpty(reportTransactions) && !isLoadingInitialReportActions ? ( <> allTransactions?.[reportID] ?? [], - canBeMissing: true, - }); - - const prevTransactions = usePrevious(transactions); + const prevTransactions = usePrevious(reportTransactions); const newTransactions = useMemo(() => { - if (!prevTransactions || !transactions || transactions.length <= prevTransactions.length) { + if (!prevTransactions || !reportTransactions || reportTransactions.length <= prevTransactions.length) { return CONST.EMPTY_ARRAY as unknown as OnyxTypes.Transaction[]; } - return transactions.filter((transaction) => !prevTransactions?.some((prevTransaction) => prevTransaction.transactionID === transaction.transactionID)); - // Depending only on transactions is enough because prevTransactions is a helper object. + return reportTransactions.filter((transaction) => !prevTransactions?.some((prevTransaction) => prevTransaction.transactionID === transaction.transactionID)); + // Depending only on reportTransactions is enough because prevTransactions is a helper object. // eslint-disable-next-line react-compiler/react-compiler // eslint-disable-next-line react-hooks/exhaustive-deps - }, [transactions]); + }, [reportTransactions]); const [parentReportAction] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getNonEmptyStringOnyxID(report?.parentReportID)}`, { canEvict: false, @@ -135,16 +133,16 @@ function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayRe const isTransactionThreadView = isReportTransactionThread(report); // Prevent flash by ensuring transaction data is fully loaded before deciding which view to render - // We need to wait for both the selector to finish AND ensure we're not in a loading state where transactions could still populate - const isTransactionDataReady = transactions !== undefined; + // We need to wait for both the selector to finish AND ensure we're not in a loading state where reportTransactions could still populate + const isTransactionDataReady = reportTransactions !== undefined; const isStillLoadingData = !!isLoadingInitialReportActions || !!reportMetadata?.isLoadingOlderReportActions || !!reportMetadata?.isLoadingNewerReportActions; const shouldWaitForData = - (!isTransactionDataReady || (isStillLoadingData && transactions?.length === 0)) && + (!isTransactionDataReady || (isStillLoadingData && reportTransactions?.length === 0)) && !isTransactionThreadView && report?.pendingFields?.createReport !== CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD; - const isEmptyTransactionReport = transactions && transactions.length === 0 && transactionThreadReportID === undefined; - const shouldDisplayMoneyRequestActionsList = !!isEmptyTransactionReport || shouldDisplayReportTableView(report, transactions ?? []); + const isEmptyTransactionReport = reportTransactions && reportTransactions.length === 0 && transactionThreadReportID === undefined; + const shouldDisplayMoneyRequestActionsList = !!isEmptyTransactionReport || shouldDisplayReportTableView(report, reportTransactions ?? []); const reportHeaderView = useMemo( () => @@ -231,7 +229,7 @@ function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayRe )} {shouldDisplayReportFooter ? ( diff --git a/src/pages/Search/SearchMoneyRequestReportPage.tsx b/src/pages/Search/SearchMoneyRequestReportPage.tsx index bd626fb92fe9..c3a99d718165 100644 --- a/src/pages/Search/SearchMoneyRequestReportPage.tsx +++ b/src/pages/Search/SearchMoneyRequestReportPage.tsx @@ -49,6 +49,10 @@ function SearchMoneyRequestReportPage({route}: SearchMoneyRequestPageProps) { const policy = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`]; const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP, {canBeMissing: true}); const [currentUserEmail] = useOnyx(ONYXKEYS.SESSION, {selector: (value) => value?.email, canBeMissing: false}); + const [reportTransactions = []] = useOnyx(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS, { + canBeMissing: true, + selector: (transactions) => transactions?.[reportIDFromRoute ?? CONST.DEFAULT_NUMBER_ID], + }); const {reportActions: reportActionsWithDeletedExpenses} = usePaginatedReportActions(reportIDFromRoute); const reportActions = reportActionsWithDeletedExpenses.filter((value) => !isDeletedParentAction(value)); @@ -130,6 +134,7 @@ function SearchMoneyRequestReportPage({route}: SearchMoneyRequestPageProps) { policy={policy} shouldDisplayReportFooter={isCurrentReportLoadedFromOnyx} backToRoute={route.params.backTo} + reportTransactions={reportTransactions} /> @@ -164,6 +169,7 @@ function SearchMoneyRequestReportPage({route}: SearchMoneyRequestPageProps) { policy={policy} shouldDisplayReportFooter={isCurrentReportLoadedFromOnyx} backToRoute={route.params.backTo} + reportTransactions={reportTransactions} /> diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 9fcf5c2257f6..639fa6843866 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -299,10 +299,10 @@ function ReportScreen({route, navigation}: ReportScreenProps) { // OpenReport will be called each time the user scrolls up the report a bit, clicks on report preview, and then goes back." const isLinkedMessagePageReady = isLinkedMessageAvailable && (reportActions.length - indexOfLinkedMessage >= CONST.REPORT.MIN_INITIAL_REPORT_ACTION_COUNT || doesCreatedActionExists()); - const [reportTransactions = []] = useOnyx(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS, { - selector: (allTransactions): OnyxTypes.Transaction[] => allTransactions?.[reportIDFromRoute] ?? [], + const [transactionsByReportID] = useOnyx(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS, { canBeMissing: false, }); + const reportTransactions = transactionsByReportID?.[reportID ?? CONST.DEFAULT_NUMBER_ID] ?? []; const reportTransactionIDs = reportTransactions?.map((transaction) => transaction.transactionID); const transactionThreadReportID = getOneTransactionThreadReportID(reportID, reportActions ?? [], isOffline, reportTransactionIDs); const [transactionThreadReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`, {canBeMissing: true}); @@ -844,6 +844,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) { hasOlderActions={hasOlderActions} parentReportAction={parentReportAction} transactionThreadReportID={transactionThreadReportID} + reportTransactions={reportTransactions} /> ) : null} {!!report && shouldDisplayMoneyRequestActionsList ? ( @@ -851,7 +852,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) { report={report} policy={policy} reportActions={reportActions} - transactions={reportTransactions} + reportTransactions={reportTransactions} newTransactions={newTransactions} hasOlderActions={hasOlderActions} hasNewerActions={hasNewerActions} diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index bc1657da0ba2..ccdd746e100a 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -108,6 +108,9 @@ type ReportActionsListProps = { /** Should enable auto scroll to top threshold */ shouldEnableAutoScrollToTopThreshold?: boolean; + + /** The transactions for the report */ + reportTransactions: OnyxTypes.Transaction[]; }; const IS_CLOSE_TO_NEWEST_THRESHOLD = 15; @@ -148,6 +151,7 @@ function ReportActionsList({ listID, shouldEnableAutoScrollToTopThreshold, parentReportActionForTransactionThread, + reportTransactions, }: ReportActionsListProps) { const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const personalDetailsList = usePersonalDetails(); @@ -167,10 +171,6 @@ function ReportActionsList({ const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`, {canBeMissing: true}); const [accountID] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.accountID, canBeMissing: true}); - const [reportTransactions] = useOnyx(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS, { - canBeMissing: true, - selector: (transactions) => transactions?.[report.reportID], - }); const participantsContext = useContext(PersonalDetailsContext); const [isScrollToBottomEnabled, setIsScrollToBottomEnabled] = useState(false); @@ -587,7 +587,7 @@ function ReportActionsList({ index={index} report={report} transactionThreadReport={transactionThreadReport} - transactions={reportTransactions ?? []} + transactions={reportTransactions} linkedReportActionID={linkedReportActionID} displayAsGroup={ !isConsecutiveChronosAutomaticTimerAction(sortedVisibleReportActions, index, chatIncludesChronosWithID(reportAction?.reportID)) && diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx index cc6efaa88fab..cd55d70c0222 100755 --- a/src/pages/home/report/ReportActionsView.tsx +++ b/src/pages/home/report/ReportActionsView.tsx @@ -1,7 +1,7 @@ import {useIsFocused, useRoute} from '@react-navigation/native'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {InteractionManager} 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 ReportActionsSkeletonView from '@components/ReportActionsSkeletonView'; import useCopySelectionHelper from '@hooks/useCopySelectionHelper'; @@ -60,6 +60,9 @@ type ReportActionsViewProps = { /** If the report has older actions to load */ hasOlderActions: boolean; + + /** The transactions for the report */ + reportTransactions: OnyxTypes.Transaction[]; }; let listOldID = Math.round(Math.random() * 100); @@ -72,6 +75,7 @@ function ReportActionsView({ transactionThreadReportID, hasNewerActions, hasOlderActions, + reportTransactions, }: ReportActionsViewProps) { useCopySelectionHelper(); const route = useRoute>(); @@ -94,10 +98,7 @@ function ReportActionsView({ const prevShouldUseNarrowLayoutRef = useRef(shouldUseNarrowLayout); const reportID = report.reportID; const isReportFullyVisible = useMemo((): boolean => getIsReportFullyVisible(isFocused), [isFocused]); - const [reportTransactionIDs] = useOnyx(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS, { - selector: (allTransactions: OnyxCollection) => allTransactions[reportID]?.map((transaction) => transaction.transactionID), - canBeMissing: true, - }); + const reportTransactionIDs = reportTransactions.map((transaction) => transaction.transactionID); useEffect(() => { // When we linked to message - we do not need to wait for initial actions - they already exists @@ -305,6 +306,7 @@ function ReportActionsView({ loadNewerChats={loadNewerChats} listID={listID} shouldEnableAutoScrollToTopThreshold={shouldEnableAutoScroll} + reportTransactions={reportTransactions} /> From ab3d3e5d32d29c00bd0df72bd53dc360335515f4 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Thu, 5 Jun 2025 12:28:29 +0200 Subject: [PATCH 08/30] pass entire reportTransactions down in the tree --- src/pages/home/ReportScreen.tsx | 4 ++-- src/pages/home/report/PureReportActionItem.tsx | 15 ++++++++++----- src/pages/home/report/ReportActionsList.tsx | 10 ++++++---- .../home/report/ReportActionsListItemRenderer.tsx | 10 +++++----- src/pages/home/report/ReportActionsView.tsx | 10 +++++----- 5 files changed, 28 insertions(+), 21 deletions(-) diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 639fa6843866..ec1569c380aa 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -299,7 +299,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) { // OpenReport will be called each time the user scrolls up the report a bit, clicks on report preview, and then goes back." const isLinkedMessagePageReady = isLinkedMessageAvailable && (reportActions.length - indexOfLinkedMessage >= CONST.REPORT.MIN_INITIAL_REPORT_ACTION_COUNT || doesCreatedActionExists()); - const [transactionsByReportID] = useOnyx(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS, { + const [transactionsByReportID = {}] = useOnyx(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS, { canBeMissing: false, }); const reportTransactions = transactionsByReportID?.[reportID ?? CONST.DEFAULT_NUMBER_ID] ?? []; @@ -844,7 +844,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) { hasOlderActions={hasOlderActions} parentReportAction={parentReportAction} transactionThreadReportID={transactionThreadReportID} - reportTransactions={reportTransactions} + transactionsByReportID={transactionsByReportID} /> ) : null} {!!report && shouldDisplayMoneyRequestActionsList ? ( diff --git a/src/pages/home/report/PureReportActionItem.tsx b/src/pages/home/report/PureReportActionItem.tsx index 33a3f61526fc..f7893bcc97ad 100644 --- a/src/pages/home/report/PureReportActionItem.tsx +++ b/src/pages/home/report/PureReportActionItem.tsx @@ -181,8 +181,8 @@ type PureReportActionItemProps = { /** The transaction thread report associated with the report for this action, if any */ transactionThreadReport?: OnyxEntry; - /** The transactions for the report */ - transactions: OnyxTypes.Transaction[]; + /** All transactions grouped by reportID */ + transactionsByReportID: OnyxTypes.ReportTransactionsDerivedValue; /** Array of report actions for the report for this action */ // eslint-disable-next-line react/no-unused-prop-types @@ -359,7 +359,7 @@ function PureReportActionItem({ action, report, transactionThreadReport, - transactions, + transactionsByReportID, linkedReportActionID, displayAsGroup, index, @@ -883,12 +883,14 @@ function PureReportActionItem({ containerStyles={displayAsGroup ? [] : [styles.mt2]} checkIfContextMenuActive={toggleContextMenuFromActiveReportAction} shouldDisplayContextMenu={shouldDisplayContextMenu} - transactions={transactions} + transactions={[]} /> ); } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW && isClosedExpenseReportWithNoExpenses) { children = ${translate('parentReportAction.deletedReport')}`} />; } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW && isBetaEnabled(CONST.BETAS.TABLE_REPORT_VIEW)) { + const iouReportID = getIOUReportIDFromReportActionPreview(action) ?? CONST.DEFAULT_NUMBER_ID; + const transactions = transactionsByReportID[iouReportID] ?? []; children = ( ); } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW) { + const iouReportID = getIOUReportIDFromReportActionPreview(action) ?? CONST.DEFAULT_NUMBER_ID; + const transactions = transactionsByReportID[iouReportID] ?? []; children = ( setIsPaymentMethodPopoverActive(false)} isWhisper={isWhisper} shouldDisplayContextMenu={shouldDisplayContextMenu} + transactions={transactions} /> ); } else if (isTaskAction(action)) { @@ -1339,7 +1344,7 @@ function PureReportActionItem({ return ( ); } diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index ccdd746e100a..ed0766e5123e 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -109,8 +109,8 @@ type ReportActionsListProps = { /** Should enable auto scroll to top threshold */ shouldEnableAutoScrollToTopThreshold?: boolean; - /** The transactions for the report */ - reportTransactions: OnyxTypes.Transaction[]; + /** All transactions grouped by reportID */ + transactionsByReportID: OnyxTypes.ReportTransactionsDerivedValue; }; const IS_CLOSE_TO_NEWEST_THRESHOLD = 15; @@ -151,7 +151,7 @@ function ReportActionsList({ listID, shouldEnableAutoScrollToTopThreshold, parentReportActionForTransactionThread, - reportTransactions, + transactionsByReportID, }: ReportActionsListProps) { const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const personalDetailsList = usePersonalDetails(); @@ -173,6 +173,8 @@ function ReportActionsList({ const [accountID] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.accountID, canBeMissing: true}); const participantsContext = useContext(PersonalDetailsContext); + const reportTransactions = transactionsByReportID[report.reportID]; + const [isScrollToBottomEnabled, setIsScrollToBottomEnabled] = useState(false); useEffect(() => { @@ -587,7 +589,7 @@ function ReportActionsList({ index={index} report={report} transactionThreadReport={transactionThreadReport} - transactions={reportTransactions} + transactionsByReportID={transactionsByReportID} linkedReportActionID={linkedReportActionID} displayAsGroup={ !isConsecutiveChronosAutomaticTimerAction(sortedVisibleReportActions, index, chatIncludesChronosWithID(reportAction?.reportID)) && diff --git a/src/pages/home/report/ReportActionsListItemRenderer.tsx b/src/pages/home/report/ReportActionsListItemRenderer.tsx index 89a8ac15a88c..a1cf7d333d0b 100644 --- a/src/pages/home/report/ReportActionsListItemRenderer.tsx +++ b/src/pages/home/report/ReportActionsListItemRenderer.tsx @@ -3,7 +3,7 @@ import type {OnyxEntry} from 'react-native-onyx'; import {getOriginalMessage, isSentMoneyReportAction, isTransactionThread} from '@libs/ReportActionsUtils'; import {isChatThread, isInvoiceRoom, isPolicyExpenseChat} from '@libs/ReportUtils'; import CONST from '@src/CONST'; -import type {Report, ReportAction, Transaction} from '@src/types/onyx'; +import type {Report, ReportAction, ReportTransactionsDerivedValue} from '@src/types/onyx'; import ReportActionItem from './ReportActionItem'; import ReportActionItemParentAction from './ReportActionItemParentAction'; @@ -29,8 +29,8 @@ type ReportActionsListItemRendererProps = { /** The transaction thread report associated with the report for this action, if any */ transactionThreadReport: OnyxEntry; - /** The transactions for the report */ - transactions: Transaction[]; + /** All transactions grouped by reportID */ + transactionsByReportID: ReportTransactionsDerivedValue; /** Should the comment have the appearance of being grouped with the previous comment? */ displayAsGroup: boolean; @@ -64,7 +64,7 @@ function ReportActionsListItemRenderer({ index, report, transactionThreadReport, - transactions, + transactionsByReportID, displayAsGroup, mostRecentIOUReportActionID = '', shouldHideThreadDividerLine, @@ -191,7 +191,7 @@ function ReportActionsListItemRenderer({ index={index} isFirstVisibleReportAction={isFirstVisibleReportAction} shouldUseThreadDividerLine={shouldUseThreadDividerLine} - transactions={transactions} + transactionsByReportID={transactionsByReportID} /> ); } diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx index cd55d70c0222..44ebe1c15223 100755 --- a/src/pages/home/report/ReportActionsView.tsx +++ b/src/pages/home/report/ReportActionsView.tsx @@ -61,8 +61,8 @@ type ReportActionsViewProps = { /** If the report has older actions to load */ hasOlderActions: boolean; - /** The transactions for the report */ - reportTransactions: OnyxTypes.Transaction[]; + /** All transactions grouped by reportID */ + transactionsByReportID: OnyxTypes.ReportTransactionsDerivedValue; }; let listOldID = Math.round(Math.random() * 100); @@ -75,7 +75,7 @@ function ReportActionsView({ transactionThreadReportID, hasNewerActions, hasOlderActions, - reportTransactions, + transactionsByReportID, }: ReportActionsViewProps) { useCopySelectionHelper(); const route = useRoute>(); @@ -98,7 +98,7 @@ function ReportActionsView({ const prevShouldUseNarrowLayoutRef = useRef(shouldUseNarrowLayout); const reportID = report.reportID; const isReportFullyVisible = useMemo((): boolean => getIsReportFullyVisible(isFocused), [isFocused]); - const reportTransactionIDs = reportTransactions.map((transaction) => transaction.transactionID); + const reportTransactionIDs = transactionsByReportID[reportID]?.map((transaction) => transaction.transactionID); useEffect(() => { // When we linked to message - we do not need to wait for initial actions - they already exists @@ -306,7 +306,7 @@ function ReportActionsView({ loadNewerChats={loadNewerChats} listID={listID} shouldEnableAutoScrollToTopThreshold={shouldEnableAutoScroll} - reportTransactions={reportTransactions} + transactionsByReportID={transactionsByReportID} /> From 4e45d8829244b0909c0c7ad40e27dd01550007c6 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Thu, 5 Jun 2025 13:42:43 +0200 Subject: [PATCH 09/30] fix passing transactions down in report screen --- .../MoneyRequestReportActionsList.tsx | 17 +++++++++-------- .../MoneyRequestReportView.tsx | 12 ++++++------ .../MoneyRequestReportPreviewContent.tsx | 3 ++- .../MoneyRequestReportPreview/index.tsx | 7 ++++--- .../MoneyRequestReportPreview/types.ts | 7 +++---- .../ReportActionItem/ReportPreview.tsx | 9 +++++---- .../ReportActionItem/TripRoomPreview.tsx | 8 ++++---- src/hooks/useReportTransactionViolations.ts | 5 +++-- src/hooks/useTripTransactions.ts | 7 ++++--- .../Search/SearchMoneyRequestReportPage.tsx | 9 +++------ src/pages/home/ReportScreen.tsx | 2 +- src/pages/home/report/PureReportActionItem.tsx | 12 ++++-------- .../report/ReportActionItemParentAction.tsx | 5 +++++ .../report/ReportActionsListItemRenderer.tsx | 1 + src/pages/home/report/TripSummary.tsx | 8 ++++---- 15 files changed, 58 insertions(+), 54 deletions(-) diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx index 01f68cdbfefb..471f3e6a9f3d 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx @@ -82,8 +82,8 @@ type MoneyRequestReportListProps = { /** Array of report actions for this report */ reportActions?: OnyxTypes.ReportAction[]; - /** List of transactions belonging to this report */ - reportTransactions?: OnyxTypes.Transaction[]; + /** All transactions grouped by reportID */ + transactionsByReportID: OnyxTypes.ReportTransactionsDerivedValue; /** List of transactions that arrived when the report was open */ newTransactions: OnyxTypes.Transaction[]; @@ -109,7 +109,7 @@ function MoneyRequestReportActionsList({ report, policy, reportActions = [], - reportTransactions = [], + transactionsByReportID, newTransactions, hasNewerActions, hasOlderActions, @@ -124,7 +124,8 @@ function MoneyRequestReportActionsList({ const [isVisible, setIsVisible] = useState(Visibility.isVisible); const isFocused = useIsFocused(); const route = useRoute>(); - const reportTransactionIDs = reportTransactions.map((transaction) => transaction.transactionID); + const reportTransactions = transactionsByReportID?.[report.reportID] ?? []; + const reportTransactionIDs = useMemo(() => reportTransactions.map((transaction) => transaction.transactionID), [reportTransactions]); const reportID = report?.reportID; const linkedReportActionID = route?.params?.reportActionID; @@ -480,7 +481,7 @@ function MoneyRequestReportActionsList({ isFirstVisibleReportAction={firstVisibleReportActionID === reportAction.reportActionID} shouldHideThreadDividerLine linkedReportActionID={linkedReportActionID} - transactions={reportTransactions ?? []} + transactionsByReportID={transactionsByReportID} /> ); }, @@ -494,7 +495,7 @@ function MoneyRequestReportActionsList({ unreadMarkerReportActionID, firstVisibleReportActionID, linkedReportActionID, - reportTransactions, + transactionsByReportID, ], ); @@ -547,8 +548,8 @@ function MoneyRequestReportActionsList({ 0 && selectedTransactionIDs.length !== reportTransactions.length} + isChecked={selectedTransactionIDs.length === reportTransactionIDs.length} + isIndeterminate={selectedTransactionIDs.length > 0 && selectedTransactionIDs.length !== reportTransactionIDs.length} onPress={() => { if (selectedTransactionIDs.length !== 0) { clearSelectedTransactions(true); diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx index 3c5d29e44152..1e4371294b57 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx @@ -49,8 +49,8 @@ type MoneyRequestReportViewProps = { /** The `backTo` route that should be used when clicking back button */ backToRoute: Route | undefined; - /** The reportTransactions for the report */ - reportTransactions: OnyxTypes.Transaction[]; + /** All transactions grouped by reportID */ + transactionsByReportID: OnyxTypes.ReportTransactionsDerivedValue; }; function goBackFromSearchMoneyRequest() { @@ -88,7 +88,7 @@ function getParentReportAction(parentReportActions: OnyxEntry { @@ -229,7 +229,7 @@ function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayRe )} {shouldDisplayReportFooter ? ( diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx index 2c8e5d7782ee..c4fae597e7c3 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx @@ -100,7 +100,7 @@ function MoneyRequestReportPreviewContent({ chatReport, invoiceReceiverPolicy, iouReport, - transactions, + transactionsByReportID, violations, policy, invoiceReceiverPersonalDetail, @@ -116,6 +116,7 @@ function MoneyRequestReportPreviewContent({ shouldShowBorder = false, onPress, }: MoneyRequestReportPreviewContentProps) { + const transactions = useMemo(() => transactionsByReportID[iouReportID ?? CONST.DEFAULT_NUMBER_ID] ?? [], [transactionsByReportID, iouReportID]); const lastTransaction = transactions?.at(0); const shouldShowEmptyPlaceholder = transactions.length === 0; const theme = useTheme(); diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx index 7a529d63bc44..4164308bda81 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx @@ -37,7 +37,7 @@ function MoneyRequestReportPreview({ shouldDisplayContextMenu = true, isInvoice = false, shouldShowBorder, - transactions, + transactionsByReportID, }: MoneyRequestReportPreviewProps) { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -52,8 +52,9 @@ function MoneyRequestReportPreview({ personalDetails?.[chatReport?.invoiceReceiver && 'accountID' in chatReport.invoiceReceiver ? chatReport.invoiceReceiver.accountID : CONST.DEFAULT_NUMBER_ID], canBeMissing: true, }); - const [iouReport, violations] = useReportTransactionViolations(iouReportID, transactions); + const [iouReport, violations] = useReportTransactionViolations(iouReportID, transactionsByReportID); const policy = usePolicy(policyID); + const transactions = useMemo(() => transactionsByReportID[iouReportID ?? CONST.DEFAULT_NUMBER_ID] ?? [], [transactionsByReportID, iouReportID]); const lastTransaction = transactions?.at(0); const lastTransactionViolations = useTransactionViolations(lastTransaction?.transactionID); const {isDelegateAccessRestricted} = useDelegateUserDetails(); @@ -121,7 +122,7 @@ function MoneyRequestReportPreview({ checkIfContextMenuActive={checkIfContextMenuActive} onPaymentOptionsShow={onPaymentOptionsShow} onPaymentOptionsHide={onPaymentOptionsHide} - transactions={transactions} + transactionsByReportID={transactionsByReportID} violations={violations} policy={policy} invoiceReceiverPersonalDetail={invoiceReceiverPersonalDetail} diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/types.ts b/src/components/ReportActionItem/MoneyRequestReportPreview/types.ts index 98704214682a..c494e8d49300 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/types.ts +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/types.ts @@ -2,7 +2,7 @@ import type {LayoutChangeEvent, ListRenderItem, StyleProp, ViewStyle} from 'reac import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import type {TransactionPreviewStyleType} from '@components/ReportActionItem/TransactionPreview/types'; import type {ContextMenuAnchor} from '@pages/home/report/ContextMenu/ReportActionContextMenu'; -import type {PersonalDetails, Policy, Report, ReportAction, Transaction, TransactionViolation, TransactionViolations} from '@src/types/onyx'; +import type {PersonalDetails, Policy, Report, ReportAction, ReportTransactionsDerivedValue, Transaction, TransactionViolation, TransactionViolations} from '@src/types/onyx'; type TransactionPreviewStyle = { [key in keyof TransactionPreviewStyleType]: number; @@ -60,15 +60,14 @@ type MoneyRequestReportPreviewProps = { /** Whether to show a border to separate Reports Chat Item and Money Request Report Preview */ shouldShowBorder?: boolean; - /** The transactions for the report */ - transactions: Transaction[]; + /** All transactions grouped by reportID */ + transactionsByReportID: ReportTransactionsDerivedValue; }; type MoneyRequestReportPreviewContentOnyxProps = { chatReport: OnyxEntry; invoiceReceiverPolicy: OnyxEntry; iouReport: OnyxEntry; - transactions: Transaction[]; violations: OnyxCollection; policy: OnyxEntry; invoiceReceiverPersonalDetail: OnyxEntry; diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index d5723e6ef5ad..722822280152 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -95,7 +95,7 @@ import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {ReportAction} from '@src/types/onyx'; +import type {ReportAction, ReportTransactionsDerivedValue} from '@src/types/onyx'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import ExportWithDropdownMenu from './ExportWithDropdownMenu'; import type {PendingMessageProps} from './MoneyRequestPreview/types'; @@ -142,7 +142,7 @@ type ReportPreviewProps = { shouldDisplayContextMenu?: boolean; /** The transactions for the report */ - transactions: Transaction[]; + transactionsByReportID: ReportTransactionsDerivedValue; }; function ReportPreview({ @@ -159,11 +159,12 @@ function ReportPreview({ onPaymentOptionsHide, onShowContextMenu = () => {}, shouldDisplayContextMenu = true, - transactions, + transactionsByReportID, }: ReportPreviewProps) { const policy = usePolicy(policyID); const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`, {canBeMissing: false}); - const [iouReport, violations] = useReportTransactionViolations(iouReportID, transactions); + const transactions = useMemo(() => transactionsByReportID[iouReportID ?? CONST.DEFAULT_NUMBER_ID] ?? [], [transactionsByReportID, iouReportID]); + const [iouReport, violations] = useReportTransactionViolations(iouReportID, transactionsByReportID); const isIouReportArchived = useReportIsArchived(iouReportID); const lastTransaction = transactions?.at(0); const transactionIDList = transactions?.map((reportTransaction) => reportTransaction.transactionID) ?? []; diff --git a/src/components/ReportActionItem/TripRoomPreview.tsx b/src/components/ReportActionItem/TripRoomPreview.tsx index b53bc5aa8de3..1616bc1721e5 100644 --- a/src/components/ReportActionItem/TripRoomPreview.tsx +++ b/src/components/ReportActionItem/TripRoomPreview.tsx @@ -29,7 +29,7 @@ import * as Expensicons from '@src/components/Icon/Expensicons'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {ReportAction, Transaction} from '@src/types/onyx'; +import type {ReportAction, ReportTransactionsDerivedValue} from '@src/types/onyx'; import type {Reservation} from '@src/types/onyx/Transaction'; type TripRoomPreviewProps = { @@ -55,7 +55,7 @@ type TripRoomPreviewProps = { shouldDisplayContextMenu?: boolean; /** The transactions for the report */ - transactions: Transaction[]; + transactionsByReportID: ReportTransactionsDerivedValue; }; type ReservationViewProps = { @@ -128,13 +128,13 @@ function TripRoomPreview({ isHovered = false, checkIfContextMenuActive = () => {}, shouldDisplayContextMenu = true, - transactions, + transactionsByReportID, }: TripRoomPreviewProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`, {canBeMissing: true}); const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReport?.iouReportID}`, {canBeMissing: true}); - const tripTransactions = useTripTransactions(chatReportID, transactions); + const tripTransactions = useTripTransactions(chatReportID, transactionsByReportID); const reservationsData: ReservationData[] = getReservationsFromTripTransactions(tripTransactions); const dateInfo = diff --git a/src/hooks/useReportTransactionViolations.ts b/src/hooks/useReportTransactionViolations.ts index 55908a0d83ce..4418b35f8ee9 100644 --- a/src/hooks/useReportTransactionViolations.ts +++ b/src/hooks/useReportTransactionViolations.ts @@ -1,12 +1,13 @@ import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Report, Transaction, TransactionViolation} from '@src/types/onyx'; +import type {Report, ReportTransactionsDerivedValue, TransactionViolation} from '@src/types/onyx'; import useOnyx from './useOnyx'; const DEFAULT_VIOLATIONS: Record = {}; -function useReportTransactionViolations(reportID?: string, reportTransactions?: Transaction[]): [OnyxEntry, OnyxCollection] { +function useReportTransactionViolations(reportID?: string, transactionsByReportID?: ReportTransactionsDerivedValue): [OnyxEntry, OnyxCollection] { + const reportTransactions = transactionsByReportID?.[reportID ?? CONST.DEFAULT_NUMBER_ID] ?? []; const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID ?? CONST.DEFAULT_NUMBER_ID}`, {canBeMissing: true}); const [violations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, { selector: (allViolations) => diff --git a/src/hooks/useTripTransactions.ts b/src/hooks/useTripTransactions.ts index 0babd18dfa2e..6d5b1f05e96f 100644 --- a/src/hooks/useTripTransactions.ts +++ b/src/hooks/useTripTransactions.ts @@ -1,6 +1,7 @@ import {useOnyx} from 'react-native-onyx'; +import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Transaction} from '@src/types/onyx'; +import type {ReportTransactionsDerivedValue, Transaction} from '@src/types/onyx'; /** * Hook to fetch transactions associated with a specific `tripRoom` report. @@ -12,7 +13,7 @@ import type {Transaction} from '@src/types/onyx'; * @param reportID - The trip room's reportID. * @returns Transactions linked to the specified trip room. */ -function useTripTransactions(reportID: string | undefined, reportTransactions: Transaction[]): Transaction[] { +function useTripTransactions(reportID: string | undefined, transactionsByReportID: ReportTransactionsDerivedValue): Transaction[] { const [tripTransactionReportIDs = []] = useOnyx(ONYXKEYS.COLLECTION.REPORT, { selector: (reports) => Object.values(reports ?? {}) @@ -20,7 +21,7 @@ function useTripTransactions(reportID: string | undefined, reportTransactions: T .map((report) => report?.reportID), }); - const tripTransactions = tripTransactionReportIDs.flatMap((transactionReportID) => reportTransactions[transactionReportID ?? 0] ?? []); + const tripTransactions = tripTransactionReportIDs.flatMap((transactionReportID) => transactionsByReportID[transactionReportID ?? CONST.DEFAULT_NUMBER_ID]); return tripTransactions; } diff --git a/src/pages/Search/SearchMoneyRequestReportPage.tsx b/src/pages/Search/SearchMoneyRequestReportPage.tsx index c3a99d718165..86f00c1b27f6 100644 --- a/src/pages/Search/SearchMoneyRequestReportPage.tsx +++ b/src/pages/Search/SearchMoneyRequestReportPage.tsx @@ -49,10 +49,7 @@ function SearchMoneyRequestReportPage({route}: SearchMoneyRequestPageProps) { const policy = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`]; const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP, {canBeMissing: true}); const [currentUserEmail] = useOnyx(ONYXKEYS.SESSION, {selector: (value) => value?.email, canBeMissing: false}); - const [reportTransactions = []] = useOnyx(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS, { - canBeMissing: true, - selector: (transactions) => transactions?.[reportIDFromRoute ?? CONST.DEFAULT_NUMBER_ID], - }); + const [transactionsByReportID = {}] = useOnyx(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS, {canBeMissing: true}); const {reportActions: reportActionsWithDeletedExpenses} = usePaginatedReportActions(reportIDFromRoute); const reportActions = reportActionsWithDeletedExpenses.filter((value) => !isDeletedParentAction(value)); @@ -134,7 +131,7 @@ function SearchMoneyRequestReportPage({route}: SearchMoneyRequestPageProps) { policy={policy} shouldDisplayReportFooter={isCurrentReportLoadedFromOnyx} backToRoute={route.params.backTo} - reportTransactions={reportTransactions} + transactionsByReportID={transactionsByReportID} /> @@ -169,7 +166,7 @@ function SearchMoneyRequestReportPage({route}: SearchMoneyRequestPageProps) { policy={policy} shouldDisplayReportFooter={isCurrentReportLoadedFromOnyx} backToRoute={route.params.backTo} - reportTransactions={reportTransactions} + transactionsByReportID={transactionsByReportID} /> diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index ec1569c380aa..ef6d69b7f97a 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -852,7 +852,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) { report={report} policy={policy} reportActions={reportActions} - reportTransactions={reportTransactions} + transactionsByReportID={transactionsByReportID} newTransactions={newTransactions} hasOlderActions={hasOlderActions} hasNewerActions={hasNewerActions} diff --git a/src/pages/home/report/PureReportActionItem.tsx b/src/pages/home/report/PureReportActionItem.tsx index f7893bcc97ad..f60cb19d6d23 100644 --- a/src/pages/home/report/PureReportActionItem.tsx +++ b/src/pages/home/report/PureReportActionItem.tsx @@ -883,14 +883,12 @@ function PureReportActionItem({ containerStyles={displayAsGroup ? [] : [styles.mt2]} checkIfContextMenuActive={toggleContextMenuFromActiveReportAction} shouldDisplayContextMenu={shouldDisplayContextMenu} - transactions={[]} + transactionsByReportID={transactionsByReportID} /> ); } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW && isClosedExpenseReportWithNoExpenses) { children = ${translate('parentReportAction.deletedReport')}`} />; } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW && isBetaEnabled(CONST.BETAS.TABLE_REPORT_VIEW)) { - const iouReportID = getIOUReportIDFromReportActionPreview(action) ?? CONST.DEFAULT_NUMBER_ID; - const transactions = transactionsByReportID[iouReportID] ?? []; children = ( setIsPaymentMethodPopoverActive(false)} shouldDisplayContextMenu={shouldDisplayContextMenu} shouldShowBorder={shouldShowBorder} - transactions={transactions} + transactionsByReportID={transactionsByReportID} /> ); } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW) { - const iouReportID = getIOUReportIDFromReportActionPreview(action) ?? CONST.DEFAULT_NUMBER_ID; - const transactions = transactionsByReportID[iouReportID] ?? []; children = ( setIsPaymentMethodPopoverActive(false)} isWhisper={isWhisper} shouldDisplayContextMenu={shouldDisplayContextMenu} - transactions={transactions} + transactionsByReportID={transactionsByReportID} /> ); } else if (isTaskAction(action)) { @@ -1344,7 +1340,7 @@ function PureReportActionItem({ return ( ); } diff --git a/src/pages/home/report/ReportActionItemParentAction.tsx b/src/pages/home/report/ReportActionItemParentAction.tsx index fe3292c27a3a..d2229c101139 100644 --- a/src/pages/home/report/ReportActionItemParentAction.tsx +++ b/src/pages/home/report/ReportActionItemParentAction.tsx @@ -55,6 +55,9 @@ type ReportActionItemParentActionProps = { /** If the thread divider line will be used */ shouldUseThreadDividerLine?: boolean; + + /** All transactions grouped by reportID */ + transactionsByReportID: OnyxTypes.ReportTransactionsDerivedValue; }; function ReportActionItemParentAction({ @@ -67,6 +70,7 @@ function ReportActionItemParentAction({ shouldDisplayReplyDivider, isFirstVisibleReportAction = false, shouldUseThreadDividerLine = false, + transactionsByReportID, }: ReportActionItemParentActionProps) { const styles = useThemeStyles(); const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); @@ -152,6 +156,7 @@ function ReportActionItemParentAction({ isFirstVisibleReportAction={isFirstVisibleReportAction} shouldUseThreadDividerLine={shouldUseThreadDividerLine} isThreadReportParentAction + transactionsByReportID={transactionsByReportID} /> ); diff --git a/src/pages/home/report/ReportActionsListItemRenderer.tsx b/src/pages/home/report/ReportActionsListItemRenderer.tsx index a1cf7d333d0b..6f2658c51423 100644 --- a/src/pages/home/report/ReportActionsListItemRenderer.tsx +++ b/src/pages/home/report/ReportActionsListItemRenderer.tsx @@ -161,6 +161,7 @@ function ReportActionsListItemRenderer({ index={index} isFirstVisibleReportAction={isFirstVisibleReportAction} shouldUseThreadDividerLine={shouldUseThreadDividerLine} + transactionsByReportID={transactionsByReportID} /> ); } diff --git a/src/pages/home/report/TripSummary.tsx b/src/pages/home/report/TripSummary.tsx index cdca287f6fe5..a4474bb218d0 100644 --- a/src/pages/home/report/TripSummary.tsx +++ b/src/pages/home/report/TripSummary.tsx @@ -5,19 +5,19 @@ import TripDetailsView from '@components/ReportActionItem/TripDetailsView'; import useTripTransactions from '@hooks/useTripTransactions'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Transaction} from '@src/types/onyx'; +import type {ReportTransactionsDerivedValue} from '@src/types/onyx'; type TripSummaryProps = { /** The report ID */ reportID: string | undefined; /** The transactions for the report */ - transactions: Transaction[]; + transactionsByReportID: ReportTransactionsDerivedValue; }; -function TripSummary({reportID, transactions}: TripSummaryProps) { +function TripSummary({reportID, transactionsByReportID}: TripSummaryProps) { const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID ?? CONST.DEFAULT_NUMBER_ID}`); - const tripTransactions = useTripTransactions(reportID, transactions); + const tripTransactions = useTripTransactions(reportID, transactionsByReportID); if (!reportID || tripTransactions.length === 0) { return null; From be3ca615a81c363aceb7df11046ca28a16c66a40 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Thu, 5 Jun 2025 13:56:33 +0200 Subject: [PATCH 10/30] pass empty object as default --- src/libs/actions/OnyxDerived/index.ts | 2 ++ src/pages/home/report/PureReportActionItem.tsx | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/OnyxDerived/index.ts b/src/libs/actions/OnyxDerived/index.ts index abbcd8a20048..ffa19ddce831 100644 --- a/src/libs/actions/OnyxDerived/index.ts +++ b/src/libs/actions/OnyxDerived/index.ts @@ -37,6 +37,7 @@ function init() { sourceValues: undefined, areAllConnectionsSet: false, }; + // @ts-expect-error TypeScript can't confirm the shape of dependencyValues matches the compute function's parameters derivedValue = compute(dependencyValues, initialContext); dependencyValues = values; Onyx.set(key, derivedValue ?? null); @@ -77,6 +78,7 @@ function init() { [sourceKey]: sourceValue, }; } + // @ts-expect-error TypeScript can't confirm the shape of dependencyValues matches the compute function's parameters const newDerivedValue = compute(dependencyValues, context); Log.info(`[OnyxDerived] updating value for ${key} in Onyx`); derivedValue = newDerivedValue; diff --git a/src/pages/home/report/PureReportActionItem.tsx b/src/pages/home/report/PureReportActionItem.tsx index f60cb19d6d23..b61aea61376c 100644 --- a/src/pages/home/report/PureReportActionItem.tsx +++ b/src/pages/home/report/PureReportActionItem.tsx @@ -182,7 +182,7 @@ type PureReportActionItemProps = { transactionThreadReport?: OnyxEntry; /** All transactions grouped by reportID */ - transactionsByReportID: OnyxTypes.ReportTransactionsDerivedValue; + transactionsByReportID?: OnyxTypes.ReportTransactionsDerivedValue; /** Array of report actions for the report for this action */ // eslint-disable-next-line react/no-unused-prop-types @@ -359,7 +359,7 @@ function PureReportActionItem({ action, report, transactionThreadReport, - transactionsByReportID, + transactionsByReportID = {}, linkedReportActionID, displayAsGroup, index, From 341636becc0b7d368fe0f3501d6951482159bb97 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Thu, 5 Jun 2025 14:10:59 +0200 Subject: [PATCH 11/30] reduce diff --- .../MoneyRequestReportActionsList.tsx | 18 +++++++-------- .../MoneyRequestReportView.tsx | 22 +++++++++---------- .../ReportActionItem/ReportPreview.tsx | 2 +- src/pages/home/report/ReportActionsList.tsx | 4 +--- 4 files changed, 22 insertions(+), 24 deletions(-) diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx index 471f3e6a9f3d..5ad5a5f4f434 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx @@ -124,8 +124,8 @@ function MoneyRequestReportActionsList({ const [isVisible, setIsVisible] = useState(Visibility.isVisible); const isFocused = useIsFocused(); const route = useRoute>(); - const reportTransactions = transactionsByReportID?.[report.reportID] ?? []; - const reportTransactionIDs = useMemo(() => reportTransactions.map((transaction) => transaction.transactionID), [reportTransactions]); + const transactions = useMemo(() => transactionsByReportID?.[report.reportID] ?? [], [transactionsByReportID, report.reportID]); + const reportTransactionIDs = useMemo(() => transactions.map((transaction) => transaction.transactionID), [transactions]); const reportID = report?.reportID; const linkedReportActionID = route?.params?.reportActionID; @@ -157,7 +157,7 @@ function MoneyRequestReportActionsList({ handleDeleteTransactions, isDeleteModalVisible, hideDeleteModal, - } = useSelectedTransactionsActions({report, reportActions, allTransactionsLength: reportTransactions.length, session, onExportFailed: () => setIsDownloadErrorModalVisible(true)}); + } = useSelectedTransactionsActions({report, reportActions, allTransactionsLength: transactions.length, session, onExportFailed: () => setIsDownloadErrorModalVisible(true)}); // We are reversing actions because in this View we are starting at the top and don't use Inverted list const visibleReportActions = useMemo(() => { @@ -554,22 +554,22 @@ function MoneyRequestReportActionsList({ if (selectedTransactionIDs.length !== 0) { clearSelectedTransactions(true); } else { - setSelectedTransactions(reportTransactions.filter((t) => !isTransactionPendingDelete(t)).map((t) => t.transactionID)); + setSelectedTransactions(transactions.filter((t) => !isTransactionPendingDelete(t)).map((t) => t.transactionID)); } }} /> { - if (selectedTransactionIDs.length === reportTransactions.length) { + if (selectedTransactionIDs.length === transactions.length) { clearSelectedTransactions(true); } else { - setSelectedTransactions(reportTransactions.filter((t) => !isTransactionPendingDelete(t)).map((t) => t.transactionID)); + setSelectedTransactions(transactions.filter((t) => !isTransactionPendingDelete(t)).map((t) => t.transactionID)); } }} accessibilityLabel={translate('workspace.people.selectAll')} role="button" - accessibilityState={{checked: selectedTransactionIDs.length === reportTransactions.length}} + accessibilityState={{checked: selectedTransactionIDs.length === transactions.length}} dataSet={{[CONST.SELECTION_SCRAPER_HIDDEN_ELEMENT]: true}} > {translate('workspace.people.selectAll')} @@ -593,7 +593,7 @@ function MoneyRequestReportActionsList({ isActive={isFloatingMessageCounterVisible} onClick={scrollToBottomAndMarkReportAsRead} /> - {isEmpty(visibleReportActions) && isEmpty(reportTransactions) && !isLoadingInitialReportActions ? ( + {isEmpty(visibleReportActions) && isEmpty(transactions) && !isLoadingInitialReportActions ? ( <> { - if (!prevTransactions || !reportTransactions || reportTransactions.length <= prevTransactions.length) { + if (!prevTransactions || !transactions || transactions.length <= prevTransactions.length) { return CONST.EMPTY_ARRAY as unknown as OnyxTypes.Transaction[]; } - return reportTransactions.filter((transaction) => !prevTransactions?.some((prevTransaction) => prevTransaction.transactionID === transaction.transactionID)); - // Depending only on reportTransactions is enough because prevTransactions is a helper object. + return transactions.filter((transaction) => !prevTransactions?.some((prevTransaction) => prevTransaction.transactionID === transaction.transactionID)); + // Depending only on transactions is enough because prevTransactions is a helper object. // eslint-disable-next-line react-compiler/react-compiler // eslint-disable-next-line react-hooks/exhaustive-deps - }, [reportTransactions]); + }, [transactions]); const [parentReportAction] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getNonEmptyStringOnyxID(report?.parentReportID)}`, { canEvict: false, @@ -133,16 +133,16 @@ function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayRe const isTransactionThreadView = isReportTransactionThread(report); // Prevent flash by ensuring transaction data is fully loaded before deciding which view to render - // We need to wait for both the selector to finish AND ensure we're not in a loading state where reportTransactions could still populate - const isTransactionDataReady = reportTransactions !== undefined; + // We need to wait for both the selector to finish AND ensure we're not in a loading state where transactions could still populate + const isTransactionDataReady = transactions !== undefined; const isStillLoadingData = !!isLoadingInitialReportActions || !!reportMetadata?.isLoadingOlderReportActions || !!reportMetadata?.isLoadingNewerReportActions; const shouldWaitForData = - (!isTransactionDataReady || (isStillLoadingData && reportTransactions?.length === 0)) && + (!isTransactionDataReady || (isStillLoadingData && transactions?.length === 0)) && !isTransactionThreadView && report?.pendingFields?.createReport !== CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD; - const isEmptyTransactionReport = reportTransactions && reportTransactions.length === 0 && transactionThreadReportID === undefined; - const shouldDisplayMoneyRequestActionsList = !!isEmptyTransactionReport || shouldDisplayReportTableView(report, reportTransactions ?? []); + const isEmptyTransactionReport = transactions && transactions.length === 0 && transactionThreadReportID === undefined; + const shouldDisplayMoneyRequestActionsList = !!isEmptyTransactionReport || shouldDisplayReportTableView(report, transactions ?? []); const reportHeaderView = useMemo( () => diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 722822280152..6a9a13ff08c3 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -141,7 +141,7 @@ type ReportPreviewProps = { /** Whether context menu should be shown on press */ shouldDisplayContextMenu?: boolean; - /** The transactions for the report */ + /** All transactions grouped by reportID */ transactionsByReportID: ReportTransactionsDerivedValue; }; diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index ed0766e5123e..48efe46173a0 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -173,8 +173,6 @@ function ReportActionsList({ const [accountID] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.accountID, canBeMissing: true}); const participantsContext = useContext(PersonalDetailsContext); - const reportTransactions = transactionsByReportID[report.reportID]; - const [isScrollToBottomEnabled, setIsScrollToBottomEnabled] = useState(false); useEffect(() => { @@ -610,6 +608,7 @@ function ReportActionsList({ parentReportActionForTransactionThread, report, transactionThreadReport, + transactionsByReportID, linkedReportActionID, sortedVisibleReportActions, mostRecentIOUReportActionID, @@ -617,7 +616,6 @@ function ReportActionsList({ unreadMarkerReportActionID, firstVisibleReportActionID, shouldUseThreadDividerLine, - reportTransactions, ], ); From 7eac09a8e32a06a3e06f94dc984b387978ed2332 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Thu, 5 Jun 2025 17:12:23 +0200 Subject: [PATCH 12/30] use getAllNonDeletedTransactions --- src/libs/MoneyRequestReportUtils.ts | 24 +++++++++++++++++++-- src/pages/home/ReportScreen.tsx | 4 ++-- src/pages/home/report/ReportActionsView.tsx | 4 +++- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/libs/MoneyRequestReportUtils.ts b/src/libs/MoneyRequestReportUtils.ts index 2c43d26f33c1..18e5cc303210 100644 --- a/src/libs/MoneyRequestReportUtils.ts +++ b/src/libs/MoneyRequestReportUtils.ts @@ -3,7 +3,7 @@ import type {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; import type {OriginalMessageIOU, Policy, Report, ReportAction, Transaction} from '@src/types/onyx'; import {convertToDisplayString} from './CurrencyUtils'; -import {getIOUActionForTransactionID, getOriginalMessage, isMoneyRequestAction} from './ReportActionsUtils'; +import {getIOUActionForTransactionID, getOriginalMessage, isDeletedParentAction, isMoneyRequestAction} from './ReportActionsUtils'; import { getMoneyRequestSpendBreakdown, getNonHeldAndFullAmount, @@ -49,6 +49,19 @@ function getThreadReportIDsForTransactions(reportActions: ReportAction[], transa .filter((reportID): reportID is string => !!reportID); } +/** + * Filters all available transactions and returns the ones that belong to not removed parent action. + */ +function getAllNonDeletedTransactions(transactions: Transaction[], reportActions: ReportAction[]) { + return transactions.filter((transaction): transaction is Transaction => { + if (!transaction) { + return false; + } + const action = getIOUActionForTransactionID(reportActions, transaction.transactionID); + return !isDeletedParentAction(action); + }); +} + /** * Given a list of transaction, this function checks if a given report has exactly one transaction * @@ -109,4 +122,11 @@ const getTotalAmountForIOUReportPreviewButton = (report: OnyxEntry, poli return convertToDisplayString(totalDisplaySpend, report?.currency); }; -export {isActionVisibleOnMoneyRequestReport, getThreadReportIDsForTransactions, getTotalAmountForIOUReportPreviewButton, isSingleTransactionReport, shouldDisplayReportTableView}; +export { + isActionVisibleOnMoneyRequestReport, + getThreadReportIDsForTransactions, + getTotalAmountForIOUReportPreviewButton, + getAllNonDeletedTransactions, + isSingleTransactionReport, + shouldDisplayReportTableView, +}; diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 667e57048952..33f9bbf7beac 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -31,7 +31,7 @@ import useViewportOffsetTop from '@hooks/useViewportOffsetTop'; import {hideEmojiPicker} from '@libs/actions/EmojiPickerAction'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import Log from '@libs/Log'; -import {shouldDisplayReportTableView} from '@libs/MoneyRequestReportUtils'; +import {getAllNonDeletedTransactions, shouldDisplayReportTableView} from '@libs/MoneyRequestReportUtils'; import Navigation, {navigationRef} from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import clearReportNotifications from '@libs/Notification/clearReportNotifications'; @@ -296,7 +296,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) { const [transactionsByReportID = {}] = useOnyx(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS, { canBeMissing: false, }); - const reportTransactions = transactionsByReportID?.[reportID ?? CONST.DEFAULT_NUMBER_ID] ?? []; + const reportTransactions = getAllNonDeletedTransactions(transactionsByReportID?.[reportID ?? CONST.DEFAULT_NUMBER_ID] ?? [], reportActions); const reportTransactionIDs = reportTransactions?.map((transaction) => transaction.transactionID); const transactionThreadReportID = getOneTransactionThreadReportID(reportID, reportActions ?? [], isOffline, reportTransactionIDs); const [transactionThreadReportActions = {}] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReportID}`, {canBeMissing: true}); diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx index 44ebe1c15223..eea146ca859a 100755 --- a/src/pages/home/report/ReportActionsView.tsx +++ b/src/pages/home/report/ReportActionsView.tsx @@ -13,6 +13,7 @@ import {updateLoadingInitialReportAction} from '@libs/actions/Report'; import Timing from '@libs/actions/Timing'; import DateUtils from '@libs/DateUtils'; import getIsReportFullyVisible from '@libs/getIsReportFullyVisible'; +import {getAllNonDeletedTransactions} from '@libs/MoneyRequestReportUtils'; import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; import type {ReportsSplitNavigatorParamList} from '@libs/Navigation/types'; import {generateNewRandomInt, rand64} from '@libs/NumberUtils'; @@ -98,7 +99,8 @@ function ReportActionsView({ const prevShouldUseNarrowLayoutRef = useRef(shouldUseNarrowLayout); const reportID = report.reportID; const isReportFullyVisible = useMemo((): boolean => getIsReportFullyVisible(isFocused), [isFocused]); - const reportTransactionIDs = transactionsByReportID[reportID]?.map((transaction) => transaction.transactionID); + const reportTransactions = getAllNonDeletedTransactions(transactionsByReportID?.[reportID ?? CONST.DEFAULT_NUMBER_ID] ?? [], allReportActions ?? []); + const reportTransactionIDs = reportTransactions?.map((transaction) => transaction.transactionID); useEffect(() => { // When we linked to message - we do not need to wait for initial actions - they already exists From 0ed45202eba3eb35745e0ebcd5831ed31b4ac666 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Fri, 6 Jun 2025 13:23:41 +0200 Subject: [PATCH 13/30] pass violations through derived value --- .../MoneyRequestReportActionsList.tsx | 2 +- .../MoneyRequestReportView.tsx | 2 +- .../MoneyRequestReportPreviewContent.tsx | 2 +- .../MoneyRequestReportPreview/index.tsx | 6 ++--- .../ReportActionItem/ReportPreview.tsx | 6 ++--- src/hooks/useReportTransactionViolations.ts | 23 ------------------- src/hooks/useTripTransactions.ts | 2 +- src/libs/ReportUtils.ts | 2 +- .../OnyxDerived/configs/reportTransactions.ts | 17 ++++++++++---- src/pages/home/ReportScreen.tsx | 3 ++- src/pages/home/report/ReportActionsView.tsx | 2 +- src/types/onyx/DerivedValues.ts | 19 +++++++++++++-- 12 files changed, 43 insertions(+), 43 deletions(-) delete mode 100644 src/hooks/useReportTransactionViolations.ts diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx index 5ad5a5f4f434..1ce07655c674 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx @@ -124,7 +124,7 @@ function MoneyRequestReportActionsList({ const [isVisible, setIsVisible] = useState(Visibility.isVisible); const isFocused = useIsFocused(); const route = useRoute>(); - const transactions = useMemo(() => transactionsByReportID?.[report.reportID] ?? [], [transactionsByReportID, report.reportID]); + const transactions = useMemo(() => transactionsByReportID?.[report.reportID]?.transactions ?? [], [transactionsByReportID, report.reportID]); const reportTransactionIDs = useMemo(() => transactions.map((transaction) => transaction.transactionID), [transactions]); const reportID = report?.reportID; diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx index e8fa4ce6c26f..df3e111771f1 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx @@ -100,7 +100,7 @@ function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayRe const {reportActions: unfilteredReportActions, hasNewerActions, hasOlderActions} = usePaginatedReportActions(reportID); const reportActions = getFilteredReportActionsForReportView(unfilteredReportActions); - const transactions = transactionsByReportID[reportID ?? CONST.DEFAULT_NUMBER_ID] ?? []; + const transactions = transactionsByReportID[reportID ?? CONST.DEFAULT_NUMBER_ID].transactions ?? []; const reportTransactionIDs = transactions?.map((transaction) => transaction.transactionID); const transactionThreadReportID = getOneTransactionThreadReportID(reportID, reportActions ?? [], isOffline, reportTransactionIDs); const prevTransactions = usePrevious(transactions); diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx index 9997383a1c1f..cf58ad62f569 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx @@ -116,7 +116,7 @@ function MoneyRequestReportPreviewContent({ shouldShowBorder = false, onPress, }: MoneyRequestReportPreviewContentProps) { - const transactions = useMemo(() => transactionsByReportID[iouReportID ?? CONST.DEFAULT_NUMBER_ID] ?? [], [transactionsByReportID, iouReportID]); + const transactions = useMemo(() => transactionsByReportID[iouReportID ?? CONST.DEFAULT_NUMBER_ID]?.transactions ?? [], [transactionsByReportID, iouReportID]); const lastTransaction = transactions?.at(0); const shouldShowEmptyPlaceholder = transactions.length === 0; const theme = useTheme(); diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx index 9c29cdf19828..0c936d5ca411 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx @@ -4,7 +4,6 @@ import {useOnyx} from 'react-native-onyx'; import TransactionPreview from '@components/ReportActionItem/TransactionPreview'; import useDelegateUserDetails from '@hooks/useDelegateUserDetails'; import usePolicy from '@hooks/usePolicy'; -import useReportTransactionViolations from '@hooks/useReportTransactionViolations'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -51,9 +50,10 @@ function MoneyRequestReportPreview({ personalDetails?.[chatReport?.invoiceReceiver && 'accountID' in chatReport.invoiceReceiver ? chatReport.invoiceReceiver.accountID : CONST.DEFAULT_NUMBER_ID], canBeMissing: true, }); - const [iouReport, violations] = useReportTransactionViolations(iouReportID, transactionsByReportID); + const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${iouReportID ?? CONST.DEFAULT_NUMBER_ID}`, {canBeMissing: true}); + const violations = transactionsByReportID[iouReportID ?? CONST.DEFAULT_NUMBER_ID]?.violations ?? {}; const policy = usePolicy(policyID); - const transactions = useMemo(() => transactionsByReportID[iouReportID ?? CONST.DEFAULT_NUMBER_ID] ?? [], [transactionsByReportID, iouReportID]); + const transactions = useMemo(() => transactionsByReportID[iouReportID ?? CONST.DEFAULT_NUMBER_ID]?.transactions ?? [], [transactionsByReportID, iouReportID]); const lastTransaction = transactions?.at(0); const lastTransactionViolations = useTransactionViolations(lastTransaction?.transactionID); const {isDelegateAccessRestricted} = useDelegateUserDetails(); diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 6a9a13ff08c3..bede657d0d00 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -25,7 +25,6 @@ import useNetwork from '@hooks/useNetwork'; import usePaymentAnimations from '@hooks/usePaymentAnimations'; import usePolicy from '@hooks/usePolicy'; import useReportIsArchived from '@hooks/useReportIsArchived'; -import useReportTransactionViolations from '@hooks/useReportTransactionViolations'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useTransactionViolations from '@hooks/useTransactionViolations'; @@ -163,8 +162,9 @@ function ReportPreview({ }: ReportPreviewProps) { const policy = usePolicy(policyID); const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`, {canBeMissing: false}); - const transactions = useMemo(() => transactionsByReportID[iouReportID ?? CONST.DEFAULT_NUMBER_ID] ?? [], [transactionsByReportID, iouReportID]); - const [iouReport, violations] = useReportTransactionViolations(iouReportID, transactionsByReportID); + const transactions = useMemo(() => transactionsByReportID[iouReportID ?? CONST.DEFAULT_NUMBER_ID]?.transactions ?? [], [transactionsByReportID, iouReportID]); + const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${iouReportID ?? CONST.DEFAULT_NUMBER_ID}`, {canBeMissing: true}); + const violations = useMemo(() => transactionsByReportID[iouReportID ?? CONST.DEFAULT_NUMBER_ID]?.violations ?? {}, [transactionsByReportID, iouReportID]); const isIouReportArchived = useReportIsArchived(iouReportID); const lastTransaction = transactions?.at(0); const transactionIDList = transactions?.map((reportTransaction) => reportTransaction.transactionID) ?? []; diff --git a/src/hooks/useReportTransactionViolations.ts b/src/hooks/useReportTransactionViolations.ts deleted file mode 100644 index 4418b35f8ee9..000000000000 --- a/src/hooks/useReportTransactionViolations.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import type {Report, ReportTransactionsDerivedValue, TransactionViolation} from '@src/types/onyx'; -import useOnyx from './useOnyx'; - -const DEFAULT_VIOLATIONS: Record = {}; - -function useReportTransactionViolations(reportID?: string, transactionsByReportID?: ReportTransactionsDerivedValue): [OnyxEntry, OnyxCollection] { - const reportTransactions = transactionsByReportID?.[reportID ?? CONST.DEFAULT_NUMBER_ID] ?? []; - const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID ?? CONST.DEFAULT_NUMBER_ID}`, {canBeMissing: true}); - const [violations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, { - selector: (allViolations) => - Object.fromEntries( - Object.entries(allViolations ?? {}).filter(([key]) => - reportTransactions?.some((transaction) => transaction.transactionID === key.replace(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, '')), - ), - ), - }); - return [report, violations ?? DEFAULT_VIOLATIONS]; -} - -export default useReportTransactionViolations; diff --git a/src/hooks/useTripTransactions.ts b/src/hooks/useTripTransactions.ts index 6d5b1f05e96f..92a8ef7e9c05 100644 --- a/src/hooks/useTripTransactions.ts +++ b/src/hooks/useTripTransactions.ts @@ -21,7 +21,7 @@ function useTripTransactions(reportID: string | undefined, transactionsByReportI .map((report) => report?.reportID), }); - const tripTransactions = tripTransactionReportIDs.flatMap((transactionReportID) => transactionsByReportID[transactionReportID ?? CONST.DEFAULT_NUMBER_ID]); + const tripTransactions = tripTransactionReportIDs.flatMap((transactionReportID) => transactionsByReportID[transactionReportID ?? CONST.DEFAULT_NUMBER_ID].transactions ?? []); return tripTransactions; } diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 39574e7d3a6e..3107af7a430a 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -968,7 +968,7 @@ Onyx.connect({ if (!value) { return; } - reportsTransactions = value; + reportsTransactions = Object.fromEntries(Object.entries(value).map(([reportID, transactionsAndViolations]) => [reportID, transactionsAndViolations.transactions])); }, }); diff --git a/src/libs/actions/OnyxDerived/configs/reportTransactions.ts b/src/libs/actions/OnyxDerived/configs/reportTransactions.ts index 4aad60829014..07548b9f7324 100644 --- a/src/libs/actions/OnyxDerived/configs/reportTransactions.ts +++ b/src/libs/actions/OnyxDerived/configs/reportTransactions.ts @@ -4,8 +4,8 @@ import type {ReportTransactionsDerivedValue} from '@src/types/onyx'; export default createOnyxDerivedValueConfig({ key: ONYXKEYS.DERIVED.REPORT_TRANSACTIONS, - dependencies: [ONYXKEYS.COLLECTION.TRANSACTION], - compute: ([transactions]) => { + dependencies: [ONYXKEYS.COLLECTION.TRANSACTION, ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS], + compute: ([transactions, violations]) => { if (!transactions) { return {}; } @@ -17,10 +17,17 @@ export default createOnyxDerivedValueConfig({ } if (!acc[reportID]) { - acc[reportID] = []; + acc[reportID] = { + transactions: [], + violations: {}, + }; } - - acc[reportID].push(transaction); + const transactionID = transaction.transactionID; + const transactionViolations = violations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`]; + if (transactionViolations && transactionViolations.length > 0) { + acc[reportID].violations[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`] = transactionViolations; + } + acc[reportID].transactions.push(transaction); return acc; }, {}); diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index d70e4e6c676e..448b99dff945 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -296,7 +296,8 @@ function ReportScreen({route, navigation}: ReportScreenProps) { const [transactionsByReportID = {}] = useOnyx(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS, { canBeMissing: false, }); - const reportTransactions = getAllNonDeletedTransactions(transactionsByReportID?.[reportID ?? CONST.DEFAULT_NUMBER_ID] ?? [], reportActions); + + const reportTransactions = getAllNonDeletedTransactions(transactionsByReportID?.[reportID ?? CONST.DEFAULT_NUMBER_ID]?.transactions ?? [], reportActions); const reportTransactionIDs = reportTransactions?.map((transaction) => transaction.transactionID); const transactionThreadReportID = getOneTransactionThreadReportID(reportID, reportActions ?? [], isOffline, reportTransactionIDs); const [transactionThreadReportActions = {}] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReportID}`, {canBeMissing: true}); diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx index eea146ca859a..576d8257d945 100755 --- a/src/pages/home/report/ReportActionsView.tsx +++ b/src/pages/home/report/ReportActionsView.tsx @@ -99,7 +99,7 @@ function ReportActionsView({ const prevShouldUseNarrowLayoutRef = useRef(shouldUseNarrowLayout); const reportID = report.reportID; const isReportFullyVisible = useMemo((): boolean => getIsReportFullyVisible(isFocused), [isFocused]); - const reportTransactions = getAllNonDeletedTransactions(transactionsByReportID?.[reportID ?? CONST.DEFAULT_NUMBER_ID] ?? [], allReportActions ?? []); + const reportTransactions = getAllNonDeletedTransactions(transactionsByReportID?.[reportID ?? CONST.DEFAULT_NUMBER_ID]?.transactions ?? [], allReportActions ?? []); const reportTransactionIDs = reportTransactions?.map((transaction) => transaction.transactionID); useEffect(() => { diff --git a/src/types/onyx/DerivedValues.ts b/src/types/onyx/DerivedValues.ts index 8a6c1b69e41e..046760d7ccb8 100644 --- a/src/types/onyx/DerivedValues.ts +++ b/src/types/onyx/DerivedValues.ts @@ -2,6 +2,7 @@ import type {ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; import type {Errors} from './OnyxCommon'; import type Transaction from './Transaction'; +import type TransactionViolations from './TransactionViolation'; /** * The attributes of a report. @@ -43,10 +44,24 @@ type ReportAttributesDerivedValue = { locale: string | null; }; +/** + * + */ +type ReportTransactionsAndViolations = { + /** + * The transactions of the report. + */ + transactions: Transaction[]; + /** + * The violations of the report. + */ + violations: Record; +}; + /** * The derived value for report transactions. */ -type ReportTransactionsDerivedValue = Record; +type ReportTransactionsDerivedValue = Record; export default ReportAttributesDerivedValue; -export type {ReportAttributes, ReportAttributesDerivedValue, ReportTransactionsDerivedValue}; +export type {ReportAttributes, ReportAttributesDerivedValue, ReportTransactionsDerivedValue, ReportTransactionsAndViolations}; From 50bb61da256ae55b0d6efa3eab5bda52784a55bc Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Fri, 6 Jun 2025 13:39:34 +0200 Subject: [PATCH 14/30] rename derived value --- src/ONYXKEYS.ts | 4 ++-- .../MoneyRequestReportActionsList.tsx | 10 +++++----- .../MoneyRequestReportView.tsx | 10 +++++----- .../MoneyRequestReportPreviewContent.tsx | 4 ++-- .../MoneyRequestReportPreview/index.tsx | 8 ++++---- .../MoneyRequestReportPreview/types.ts | 4 ++-- src/components/ReportActionItem/ReportPreview.tsx | 10 +++++----- src/components/ReportActionItem/TripRoomPreview.tsx | 8 ++++---- src/hooks/useTripTransactions.ts | 6 +++--- src/libs/ReportUtils.ts | 2 +- src/libs/actions/OnyxDerived/ONYX_DERIVED_VALUES.ts | 4 ++-- ...actions.ts => reportTransactionsAndViolations.ts} | 11 +++++++---- src/pages/Search/SearchMoneyRequestReportPage.tsx | 6 +++--- src/pages/home/ReportScreen.tsx | 8 ++++---- src/pages/home/report/PureReportActionItem.tsx | 12 ++++++------ .../home/report/ReportActionItemParentAction.tsx | 6 +++--- src/pages/home/report/ReportActionsList.tsx | 8 ++++---- .../home/report/ReportActionsListItemRenderer.tsx | 10 +++++----- src/pages/home/report/ReportActionsView.tsx | 8 ++++---- src/pages/home/report/TripSummary.tsx | 8 ++++---- src/types/onyx/DerivedValues.ts | 7 ++++--- src/types/onyx/index.ts | 4 ++-- 22 files changed, 81 insertions(+), 77 deletions(-) rename src/libs/actions/OnyxDerived/configs/{reportTransactions.ts => reportTransactionsAndViolations.ts} (72%) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index e99146598a7c..707b69f9ff65 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -830,7 +830,7 @@ const ONYXKEYS = { }, DERIVED: { REPORT_ATTRIBUTES: 'reportAttributes', - REPORT_TRANSACTIONS: 'reportTransactions', + REPORT_TRANSACTIONS_AND_VIOLATIONS: 'reportTransactionsAndViolations', }, } as const; @@ -1179,7 +1179,7 @@ type OnyxValuesMapping = { type OnyxDerivedValuesMapping = { [ONYXKEYS.DERIVED.REPORT_ATTRIBUTES]: OnyxTypes.ReportAttributesDerivedValue; - [ONYXKEYS.DERIVED.REPORT_TRANSACTIONS]: OnyxTypes.ReportTransactionsDerivedValue; + [ONYXKEYS.DERIVED.REPORT_TRANSACTIONS_AND_VIOLATIONS]: OnyxTypes.ReportTransactionsAndViolationsDerivedValue; }; type OnyxValues = OnyxValuesMapping & OnyxCollectionValuesMapping & OnyxFormValuesMapping & OnyxFormDraftValuesMapping & OnyxDerivedValuesMapping; diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx index 1ce07655c674..a3834c42f9a6 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx @@ -83,7 +83,7 @@ type MoneyRequestReportListProps = { reportActions?: OnyxTypes.ReportAction[]; /** All transactions grouped by reportID */ - transactionsByReportID: OnyxTypes.ReportTransactionsDerivedValue; + transactionsAndViolationsByReport: OnyxTypes.ReportTransactionsAndViolationsDerivedValue; /** List of transactions that arrived when the report was open */ newTransactions: OnyxTypes.Transaction[]; @@ -109,7 +109,7 @@ function MoneyRequestReportActionsList({ report, policy, reportActions = [], - transactionsByReportID, + transactionsAndViolationsByReport, newTransactions, hasNewerActions, hasOlderActions, @@ -124,7 +124,7 @@ function MoneyRequestReportActionsList({ const [isVisible, setIsVisible] = useState(Visibility.isVisible); const isFocused = useIsFocused(); const route = useRoute>(); - const transactions = useMemo(() => transactionsByReportID?.[report.reportID]?.transactions ?? [], [transactionsByReportID, report.reportID]); + const transactions = useMemo(() => transactionsAndViolationsByReport?.[report.reportID]?.transactions ?? [], [transactionsAndViolationsByReport, report.reportID]); const reportTransactionIDs = useMemo(() => transactions.map((transaction) => transaction.transactionID), [transactions]); const reportID = report?.reportID; @@ -481,7 +481,7 @@ function MoneyRequestReportActionsList({ isFirstVisibleReportAction={firstVisibleReportActionID === reportAction.reportActionID} shouldHideThreadDividerLine linkedReportActionID={linkedReportActionID} - transactionsByReportID={transactionsByReportID} + transactionsAndViolationsByReport={transactionsAndViolationsByReport} /> ); }, @@ -495,7 +495,7 @@ function MoneyRequestReportActionsList({ unreadMarkerReportActionID, firstVisibleReportActionID, linkedReportActionID, - transactionsByReportID, + transactionsAndViolationsByReport, ], ); diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx index df3e111771f1..c9fc43cc74b1 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx @@ -50,7 +50,7 @@ type MoneyRequestReportViewProps = { backToRoute: Route | undefined; /** All transactions grouped by reportID */ - transactionsByReportID: OnyxTypes.ReportTransactionsDerivedValue; + transactionsAndViolationsByReport: OnyxTypes.ReportTransactionsAndViolationsDerivedValue; }; function goBackFromSearchMoneyRequest() { @@ -88,7 +88,7 @@ function getParentReportAction(parentReportActions: OnyxEntry transaction.transactionID); const transactionThreadReportID = getOneTransactionThreadReportID(reportID, reportActions ?? [], isOffline, reportTransactionIDs); const prevTransactions = usePrevious(transactions); @@ -230,7 +230,7 @@ function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayRe )} {shouldDisplayReportFooter ? ( diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx index cf58ad62f569..26f3599f777c 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx @@ -100,7 +100,7 @@ function MoneyRequestReportPreviewContent({ chatReport, invoiceReceiverPolicy, iouReport, - transactionsByReportID, + transactionsAndViolationsByReport, violations, policy, invoiceReceiverPersonalDetail, @@ -116,7 +116,7 @@ function MoneyRequestReportPreviewContent({ shouldShowBorder = false, onPress, }: MoneyRequestReportPreviewContentProps) { - const transactions = useMemo(() => transactionsByReportID[iouReportID ?? CONST.DEFAULT_NUMBER_ID]?.transactions ?? [], [transactionsByReportID, iouReportID]); + const transactions = useMemo(() => transactionsAndViolationsByReport[iouReportID ?? CONST.DEFAULT_NUMBER_ID]?.transactions ?? [], [transactionsAndViolationsByReport, iouReportID]); const lastTransaction = transactions?.at(0); const shouldShowEmptyPlaceholder = transactions.length === 0; const theme = useTheme(); diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx index 0c936d5ca411..d0c909c28ea6 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx @@ -35,7 +35,7 @@ function MoneyRequestReportPreview({ shouldDisplayContextMenu = true, isInvoice = false, shouldShowBorder, - transactionsByReportID, + transactionsAndViolationsByReport, }: MoneyRequestReportPreviewProps) { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -51,9 +51,9 @@ function MoneyRequestReportPreview({ canBeMissing: true, }); const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${iouReportID ?? CONST.DEFAULT_NUMBER_ID}`, {canBeMissing: true}); - const violations = transactionsByReportID[iouReportID ?? CONST.DEFAULT_NUMBER_ID]?.violations ?? {}; + const violations = transactionsAndViolationsByReport[iouReportID ?? CONST.DEFAULT_NUMBER_ID]?.violations ?? {}; const policy = usePolicy(policyID); - const transactions = useMemo(() => transactionsByReportID[iouReportID ?? CONST.DEFAULT_NUMBER_ID]?.transactions ?? [], [transactionsByReportID, iouReportID]); + const transactions = useMemo(() => transactionsAndViolationsByReport[iouReportID ?? CONST.DEFAULT_NUMBER_ID]?.transactions ?? [], [transactionsAndViolationsByReport, iouReportID]); const lastTransaction = transactions?.at(0); const lastTransactionViolations = useTransactionViolations(lastTransaction?.transactionID); const {isDelegateAccessRestricted} = useDelegateUserDetails(); @@ -121,7 +121,7 @@ function MoneyRequestReportPreview({ checkIfContextMenuActive={checkIfContextMenuActive} onPaymentOptionsShow={onPaymentOptionsShow} onPaymentOptionsHide={onPaymentOptionsHide} - transactionsByReportID={transactionsByReportID} + transactionsAndViolationsByReport={transactionsAndViolationsByReport} violations={violations} policy={policy} invoiceReceiverPersonalDetail={invoiceReceiverPersonalDetail} diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/types.ts b/src/components/ReportActionItem/MoneyRequestReportPreview/types.ts index 959dc363958c..fb2fa940b979 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/types.ts +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/types.ts @@ -2,7 +2,7 @@ import type {LayoutChangeEvent, ListRenderItem, StyleProp, ViewStyle} from 'reac import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import type {TransactionPreviewStyleType} from '@components/ReportActionItem/TransactionPreview/types'; import type {ContextMenuAnchor} from '@pages/home/report/ContextMenu/ReportActionContextMenu'; -import type {PersonalDetails, Policy, Report, ReportAction, ReportTransactionsDerivedValue, Transaction, TransactionViolation, TransactionViolations} from '@src/types/onyx'; +import type {PersonalDetails, Policy, Report, ReportAction, ReportTransactionsAndViolationsDerivedValue, Transaction, TransactionViolation, TransactionViolations} from '@src/types/onyx'; type TransactionPreviewStyle = { [key in keyof TransactionPreviewStyleType]: number; @@ -58,7 +58,7 @@ type MoneyRequestReportPreviewProps = { shouldShowBorder?: boolean; /** All transactions grouped by reportID */ - transactionsByReportID: ReportTransactionsDerivedValue; + transactionsAndViolationsByReport: ReportTransactionsAndViolationsDerivedValue; }; type MoneyRequestReportPreviewContentOnyxProps = { diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index bede657d0d00..9f2bfdb14db9 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -94,7 +94,7 @@ import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {ReportAction, ReportTransactionsDerivedValue} from '@src/types/onyx'; +import type {ReportAction, ReportTransactionsAndViolationsDerivedValue} from '@src/types/onyx'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import ExportWithDropdownMenu from './ExportWithDropdownMenu'; import type {PendingMessageProps} from './MoneyRequestPreview/types'; @@ -141,7 +141,7 @@ type ReportPreviewProps = { shouldDisplayContextMenu?: boolean; /** All transactions grouped by reportID */ - transactionsByReportID: ReportTransactionsDerivedValue; + transactionsAndViolationsByReport: ReportTransactionsAndViolationsDerivedValue; }; function ReportPreview({ @@ -158,13 +158,13 @@ function ReportPreview({ onPaymentOptionsHide, onShowContextMenu = () => {}, shouldDisplayContextMenu = true, - transactionsByReportID, + transactionsAndViolationsByReport, }: ReportPreviewProps) { const policy = usePolicy(policyID); const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`, {canBeMissing: false}); - const transactions = useMemo(() => transactionsByReportID[iouReportID ?? CONST.DEFAULT_NUMBER_ID]?.transactions ?? [], [transactionsByReportID, iouReportID]); + const transactions = useMemo(() => transactionsAndViolationsByReport[iouReportID ?? CONST.DEFAULT_NUMBER_ID]?.transactions ?? [], [transactionsAndViolationsByReport, iouReportID]); const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${iouReportID ?? CONST.DEFAULT_NUMBER_ID}`, {canBeMissing: true}); - const violations = useMemo(() => transactionsByReportID[iouReportID ?? CONST.DEFAULT_NUMBER_ID]?.violations ?? {}, [transactionsByReportID, iouReportID]); + const violations = useMemo(() => transactionsAndViolationsByReport[iouReportID ?? CONST.DEFAULT_NUMBER_ID]?.violations ?? {}, [transactionsAndViolationsByReport, iouReportID]); const isIouReportArchived = useReportIsArchived(iouReportID); const lastTransaction = transactions?.at(0); const transactionIDList = transactions?.map((reportTransaction) => reportTransaction.transactionID) ?? []; diff --git a/src/components/ReportActionItem/TripRoomPreview.tsx b/src/components/ReportActionItem/TripRoomPreview.tsx index 1616bc1721e5..dc51a7c596ca 100644 --- a/src/components/ReportActionItem/TripRoomPreview.tsx +++ b/src/components/ReportActionItem/TripRoomPreview.tsx @@ -29,7 +29,7 @@ import * as Expensicons from '@src/components/Icon/Expensicons'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {ReportAction, ReportTransactionsDerivedValue} from '@src/types/onyx'; +import type {ReportAction, ReportTransactionsAndViolationsDerivedValue} from '@src/types/onyx'; import type {Reservation} from '@src/types/onyx/Transaction'; type TripRoomPreviewProps = { @@ -55,7 +55,7 @@ type TripRoomPreviewProps = { shouldDisplayContextMenu?: boolean; /** The transactions for the report */ - transactionsByReportID: ReportTransactionsDerivedValue; + transactionsAndViolationsByReport: ReportTransactionsAndViolationsDerivedValue; }; type ReservationViewProps = { @@ -128,13 +128,13 @@ function TripRoomPreview({ isHovered = false, checkIfContextMenuActive = () => {}, shouldDisplayContextMenu = true, - transactionsByReportID, + transactionsAndViolationsByReport, }: TripRoomPreviewProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`, {canBeMissing: true}); const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReport?.iouReportID}`, {canBeMissing: true}); - const tripTransactions = useTripTransactions(chatReportID, transactionsByReportID); + const tripTransactions = useTripTransactions(chatReportID, transactionsAndViolationsByReport); const reservationsData: ReservationData[] = getReservationsFromTripTransactions(tripTransactions); const dateInfo = diff --git a/src/hooks/useTripTransactions.ts b/src/hooks/useTripTransactions.ts index 92a8ef7e9c05..97f519a71f5e 100644 --- a/src/hooks/useTripTransactions.ts +++ b/src/hooks/useTripTransactions.ts @@ -1,7 +1,7 @@ import {useOnyx} from 'react-native-onyx'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {ReportTransactionsDerivedValue, Transaction} from '@src/types/onyx'; +import type {ReportTransactionsAndViolationsDerivedValue, Transaction} from '@src/types/onyx'; /** * Hook to fetch transactions associated with a specific `tripRoom` report. @@ -13,7 +13,7 @@ import type {ReportTransactionsDerivedValue, Transaction} from '@src/types/onyx' * @param reportID - The trip room's reportID. * @returns Transactions linked to the specified trip room. */ -function useTripTransactions(reportID: string | undefined, transactionsByReportID: ReportTransactionsDerivedValue): Transaction[] { +function useTripTransactions(reportID: string | undefined, transactionsAndViolationsByReport: ReportTransactionsAndViolationsDerivedValue): Transaction[] { const [tripTransactionReportIDs = []] = useOnyx(ONYXKEYS.COLLECTION.REPORT, { selector: (reports) => Object.values(reports ?? {}) @@ -21,7 +21,7 @@ function useTripTransactions(reportID: string | undefined, transactionsByReportI .map((report) => report?.reportID), }); - const tripTransactions = tripTransactionReportIDs.flatMap((transactionReportID) => transactionsByReportID[transactionReportID ?? CONST.DEFAULT_NUMBER_ID].transactions ?? []); + const tripTransactions = tripTransactionReportIDs.flatMap((transactionReportID) => transactionsAndViolationsByReport[transactionReportID ?? CONST.DEFAULT_NUMBER_ID].transactions ?? []); return tripTransactions; } diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 3107af7a430a..628d0c403233 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -963,7 +963,7 @@ Onyx.connect({ let reportsTransactions: Record = {}; Onyx.connect({ - key: ONYXKEYS.DERIVED.REPORT_TRANSACTIONS, + key: ONYXKEYS.DERIVED.REPORT_TRANSACTIONS_AND_VIOLATIONS, callback: (value) => { if (!value) { return; diff --git a/src/libs/actions/OnyxDerived/ONYX_DERIVED_VALUES.ts b/src/libs/actions/OnyxDerived/ONYX_DERIVED_VALUES.ts index 8e311bf09099..6869c09dfad5 100644 --- a/src/libs/actions/OnyxDerived/ONYX_DERIVED_VALUES.ts +++ b/src/libs/actions/OnyxDerived/ONYX_DERIVED_VALUES.ts @@ -1,7 +1,7 @@ import type {ValueOf} from 'type-fest'; import ONYXKEYS from '@src/ONYXKEYS'; import reportAttributesConfig from './configs/reportAttributes'; -import reportTransactionsConfig from './configs/reportTransactions'; +import reportTransactionsAndViolationsConfig from './configs/reportTransactionsAndViolations'; import type {OnyxDerivedValueConfig} from './types'; /** @@ -10,7 +10,7 @@ import type {OnyxDerivedValueConfig} from './types'; */ const ONYX_DERIVED_VALUES = { [ONYXKEYS.DERIVED.REPORT_ATTRIBUTES]: reportAttributesConfig, - [ONYXKEYS.DERIVED.REPORT_TRANSACTIONS]: reportTransactionsConfig, + [ONYXKEYS.DERIVED.REPORT_TRANSACTIONS_AND_VIOLATIONS]: reportTransactionsAndViolationsConfig, } as const satisfies { // eslint-disable-next-line @typescript-eslint/no-explicit-any [Key in ValueOf]: OnyxDerivedValueConfig; diff --git a/src/libs/actions/OnyxDerived/configs/reportTransactions.ts b/src/libs/actions/OnyxDerived/configs/reportTransactionsAndViolations.ts similarity index 72% rename from src/libs/actions/OnyxDerived/configs/reportTransactions.ts rename to src/libs/actions/OnyxDerived/configs/reportTransactionsAndViolations.ts index 07548b9f7324..b7a67f1d0a16 100644 --- a/src/libs/actions/OnyxDerived/configs/reportTransactions.ts +++ b/src/libs/actions/OnyxDerived/configs/reportTransactionsAndViolations.ts @@ -1,16 +1,16 @@ import createOnyxDerivedValueConfig from '@userActions/OnyxDerived/createOnyxDerivedValueConfig'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {ReportTransactionsDerivedValue} from '@src/types/onyx'; +import type {ReportTransactionsAndViolationsDerivedValue} from '@src/types/onyx'; export default createOnyxDerivedValueConfig({ - key: ONYXKEYS.DERIVED.REPORT_TRANSACTIONS, + key: ONYXKEYS.DERIVED.REPORT_TRANSACTIONS_AND_VIOLATIONS, dependencies: [ONYXKEYS.COLLECTION.TRANSACTION, ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS], compute: ([transactions, violations]) => { if (!transactions) { return {}; } - return Object.values(transactions).reduce((acc, transaction) => { + return Object.values(transactions).reduce((acc, transaction) => { const reportID = transaction?.reportID; if (!reportID) { return acc; @@ -25,7 +25,10 @@ export default createOnyxDerivedValueConfig({ const transactionID = transaction.transactionID; const transactionViolations = violations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`]; if (transactionViolations && transactionViolations.length > 0) { - acc[reportID].violations[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`] = transactionViolations; + acc[reportID].violations = { + ...acc[reportID].violations, + [`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`]: transactionViolations, + }; } acc[reportID].transactions.push(transaction); diff --git a/src/pages/Search/SearchMoneyRequestReportPage.tsx b/src/pages/Search/SearchMoneyRequestReportPage.tsx index 8215aaea140c..faede3170b37 100644 --- a/src/pages/Search/SearchMoneyRequestReportPage.tsx +++ b/src/pages/Search/SearchMoneyRequestReportPage.tsx @@ -43,7 +43,7 @@ function SearchMoneyRequestReportPage({route}: SearchMoneyRequestPageProps) { const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {allowStaleData: true, initialValue: {}, canBeMissing: false}); const policy = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`]; const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP, {canBeMissing: true}); - const [transactionsByReportID = {}] = useOnyx(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS, {canBeMissing: true}); + const [transactionsAndViolationsByReport = {}] = useOnyx(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS_AND_VIOLATIONS, {canBeMissing: true}); const {isEditingDisabled, isCurrentReportLoadedFromOnyx} = useIsReportReadyToDisplay(report, reportIDFromRoute); @@ -100,7 +100,7 @@ function SearchMoneyRequestReportPage({route}: SearchMoneyRequestPageProps) { policy={policy} shouldDisplayReportFooter={isCurrentReportLoadedFromOnyx} backToRoute={route.params.backTo} - transactionsByReportID={transactionsByReportID} + transactionsAndViolationsByReport={transactionsAndViolationsByReport} /> @@ -135,7 +135,7 @@ function SearchMoneyRequestReportPage({route}: SearchMoneyRequestPageProps) { policy={policy} shouldDisplayReportFooter={isCurrentReportLoadedFromOnyx} backToRoute={route.params.backTo} - transactionsByReportID={transactionsByReportID} + transactionsAndViolationsByReport={transactionsAndViolationsByReport} /> diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 448b99dff945..bad756888a4f 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -293,11 +293,11 @@ function ReportScreen({route, navigation}: ReportScreenProps) { // OpenReport will be called each time the user scrolls up the report a bit, clicks on report preview, and then goes back." const isLinkedMessagePageReady = isLinkedMessageAvailable && (reportActions.length - indexOfLinkedMessage >= CONST.REPORT.MIN_INITIAL_REPORT_ACTION_COUNT || doesCreatedActionExists()); - const [transactionsByReportID = {}] = useOnyx(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS, { + const [transactionsAndViolationsByReport = {}] = useOnyx(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS_AND_VIOLATIONS, { canBeMissing: false, }); - const reportTransactions = getAllNonDeletedTransactions(transactionsByReportID?.[reportID ?? CONST.DEFAULT_NUMBER_ID]?.transactions ?? [], reportActions); + const reportTransactions = getAllNonDeletedTransactions(transactionsAndViolationsByReport?.[reportID ?? CONST.DEFAULT_NUMBER_ID]?.transactions ?? [], reportActions); const reportTransactionIDs = reportTransactions?.map((transaction) => transaction.transactionID); const transactionThreadReportID = getOneTransactionThreadReportID(reportID, reportActions ?? [], isOffline, reportTransactionIDs); const [transactionThreadReportActions = {}] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReportID}`, {canBeMissing: true}); @@ -823,7 +823,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) { hasOlderActions={hasOlderActions} parentReportAction={parentReportAction} transactionThreadReportID={transactionThreadReportID} - transactionsByReportID={transactionsByReportID} + transactionsAndViolationsByReport={transactionsAndViolationsByReport} /> ) : null} {!!report && shouldDisplayMoneyRequestActionsList ? ( @@ -831,7 +831,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) { report={report} policy={policy} reportActions={reportActions} - transactionsByReportID={transactionsByReportID} + transactionsAndViolationsByReport={transactionsAndViolationsByReport} newTransactions={newTransactions} hasOlderActions={hasOlderActions} hasNewerActions={hasNewerActions} diff --git a/src/pages/home/report/PureReportActionItem.tsx b/src/pages/home/report/PureReportActionItem.tsx index 3972945c6606..45eb0a9af34c 100644 --- a/src/pages/home/report/PureReportActionItem.tsx +++ b/src/pages/home/report/PureReportActionItem.tsx @@ -181,7 +181,7 @@ type PureReportActionItemProps = { transactionThreadReport?: OnyxEntry; /** All transactions grouped by reportID */ - transactionsByReportID?: OnyxTypes.ReportTransactionsDerivedValue; + transactionsAndViolationsByReport?: OnyxTypes.ReportTransactionsAndViolationsDerivedValue; /** Array of report actions for the report for this action */ // eslint-disable-next-line react/no-unused-prop-types @@ -355,7 +355,7 @@ function PureReportActionItem({ action, report, transactionThreadReport, - transactionsByReportID = {}, + transactionsAndViolationsByReport = {}, linkedReportActionID, displayAsGroup, index, @@ -878,7 +878,7 @@ function PureReportActionItem({ containerStyles={displayAsGroup ? [] : [styles.mt2]} checkIfContextMenuActive={toggleContextMenuFromActiveReportAction} shouldDisplayContextMenu={shouldDisplayContextMenu} - transactionsByReportID={transactionsByReportID} + transactionsAndViolationsByReport={transactionsAndViolationsByReport} /> ); } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW && isClosedExpenseReportWithNoExpenses) { @@ -899,7 +899,7 @@ function PureReportActionItem({ onPaymentOptionsHide={() => setIsPaymentMethodPopoverActive(false)} shouldDisplayContextMenu={shouldDisplayContextMenu} shouldShowBorder={shouldShowBorder} - transactionsByReportID={transactionsByReportID} + transactionsAndViolationsByReport={transactionsAndViolationsByReport} /> ); } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW) { @@ -918,7 +918,7 @@ function PureReportActionItem({ onPaymentOptionsHide={() => setIsPaymentMethodPopoverActive(false)} isWhisper={isWhisper} shouldDisplayContextMenu={shouldDisplayContextMenu} - transactionsByReportID={transactionsByReportID} + transactionsAndViolationsByReport={transactionsAndViolationsByReport} /> ); } else if (isTaskAction(action)) { @@ -1335,7 +1335,7 @@ function PureReportActionItem({ return ( ); } diff --git a/src/pages/home/report/ReportActionItemParentAction.tsx b/src/pages/home/report/ReportActionItemParentAction.tsx index d2229c101139..069090fe1769 100644 --- a/src/pages/home/report/ReportActionItemParentAction.tsx +++ b/src/pages/home/report/ReportActionItemParentAction.tsx @@ -57,7 +57,7 @@ type ReportActionItemParentActionProps = { shouldUseThreadDividerLine?: boolean; /** All transactions grouped by reportID */ - transactionsByReportID: OnyxTypes.ReportTransactionsDerivedValue; + transactionsAndViolationsByReport: OnyxTypes.ReportTransactionsAndViolationsDerivedValue; }; function ReportActionItemParentAction({ @@ -70,7 +70,7 @@ function ReportActionItemParentAction({ shouldDisplayReplyDivider, isFirstVisibleReportAction = false, shouldUseThreadDividerLine = false, - transactionsByReportID, + transactionsAndViolationsByReport, }: ReportActionItemParentActionProps) { const styles = useThemeStyles(); const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); @@ -156,7 +156,7 @@ function ReportActionItemParentAction({ isFirstVisibleReportAction={isFirstVisibleReportAction} shouldUseThreadDividerLine={shouldUseThreadDividerLine} isThreadReportParentAction - transactionsByReportID={transactionsByReportID} + transactionsAndViolationsByReport={transactionsAndViolationsByReport} /> ); diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index 48efe46173a0..1eaa4e6732b2 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -110,7 +110,7 @@ type ReportActionsListProps = { shouldEnableAutoScrollToTopThreshold?: boolean; /** All transactions grouped by reportID */ - transactionsByReportID: OnyxTypes.ReportTransactionsDerivedValue; + transactionsAndViolationsByReport: OnyxTypes.ReportTransactionsAndViolationsDerivedValue; }; const IS_CLOSE_TO_NEWEST_THRESHOLD = 15; @@ -151,7 +151,7 @@ function ReportActionsList({ listID, shouldEnableAutoScrollToTopThreshold, parentReportActionForTransactionThread, - transactionsByReportID, + transactionsAndViolationsByReport, }: ReportActionsListProps) { const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const personalDetailsList = usePersonalDetails(); @@ -587,7 +587,7 @@ function ReportActionsList({ index={index} report={report} transactionThreadReport={transactionThreadReport} - transactionsByReportID={transactionsByReportID} + transactionsAndViolationsByReport={transactionsAndViolationsByReport} linkedReportActionID={linkedReportActionID} displayAsGroup={ !isConsecutiveChronosAutomaticTimerAction(sortedVisibleReportActions, index, chatIncludesChronosWithID(reportAction?.reportID)) && @@ -608,7 +608,7 @@ function ReportActionsList({ parentReportActionForTransactionThread, report, transactionThreadReport, - transactionsByReportID, + transactionsAndViolationsByReport, linkedReportActionID, sortedVisibleReportActions, mostRecentIOUReportActionID, diff --git a/src/pages/home/report/ReportActionsListItemRenderer.tsx b/src/pages/home/report/ReportActionsListItemRenderer.tsx index 6f2658c51423..2c79930d15c1 100644 --- a/src/pages/home/report/ReportActionsListItemRenderer.tsx +++ b/src/pages/home/report/ReportActionsListItemRenderer.tsx @@ -3,7 +3,7 @@ import type {OnyxEntry} from 'react-native-onyx'; import {getOriginalMessage, isSentMoneyReportAction, isTransactionThread} from '@libs/ReportActionsUtils'; import {isChatThread, isInvoiceRoom, isPolicyExpenseChat} from '@libs/ReportUtils'; import CONST from '@src/CONST'; -import type {Report, ReportAction, ReportTransactionsDerivedValue} from '@src/types/onyx'; +import type {Report, ReportAction, ReportTransactionsAndViolationsDerivedValue} from '@src/types/onyx'; import ReportActionItem from './ReportActionItem'; import ReportActionItemParentAction from './ReportActionItemParentAction'; @@ -30,7 +30,7 @@ type ReportActionsListItemRendererProps = { transactionThreadReport: OnyxEntry; /** All transactions grouped by reportID */ - transactionsByReportID: ReportTransactionsDerivedValue; + transactionsAndViolationsByReport: ReportTransactionsAndViolationsDerivedValue; /** Should the comment have the appearance of being grouped with the previous comment? */ displayAsGroup: boolean; @@ -64,7 +64,7 @@ function ReportActionsListItemRenderer({ index, report, transactionThreadReport, - transactionsByReportID, + transactionsAndViolationsByReport, displayAsGroup, mostRecentIOUReportActionID = '', shouldHideThreadDividerLine, @@ -161,7 +161,7 @@ function ReportActionsListItemRenderer({ index={index} isFirstVisibleReportAction={isFirstVisibleReportAction} shouldUseThreadDividerLine={shouldUseThreadDividerLine} - transactionsByReportID={transactionsByReportID} + transactionsAndViolationsByReport={transactionsAndViolationsByReport} /> ); } @@ -192,7 +192,7 @@ function ReportActionsListItemRenderer({ index={index} isFirstVisibleReportAction={isFirstVisibleReportAction} shouldUseThreadDividerLine={shouldUseThreadDividerLine} - transactionsByReportID={transactionsByReportID} + transactionsAndViolationsByReport={transactionsAndViolationsByReport} /> ); } diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx index 576d8257d945..862351cd2302 100755 --- a/src/pages/home/report/ReportActionsView.tsx +++ b/src/pages/home/report/ReportActionsView.tsx @@ -63,7 +63,7 @@ type ReportActionsViewProps = { hasOlderActions: boolean; /** All transactions grouped by reportID */ - transactionsByReportID: OnyxTypes.ReportTransactionsDerivedValue; + transactionsAndViolationsByReport: OnyxTypes.ReportTransactionsAndViolationsDerivedValue; }; let listOldID = Math.round(Math.random() * 100); @@ -76,7 +76,7 @@ function ReportActionsView({ transactionThreadReportID, hasNewerActions, hasOlderActions, - transactionsByReportID, + transactionsAndViolationsByReport, }: ReportActionsViewProps) { useCopySelectionHelper(); const route = useRoute>(); @@ -99,7 +99,7 @@ function ReportActionsView({ const prevShouldUseNarrowLayoutRef = useRef(shouldUseNarrowLayout); const reportID = report.reportID; const isReportFullyVisible = useMemo((): boolean => getIsReportFullyVisible(isFocused), [isFocused]); - const reportTransactions = getAllNonDeletedTransactions(transactionsByReportID?.[reportID ?? CONST.DEFAULT_NUMBER_ID]?.transactions ?? [], allReportActions ?? []); + const reportTransactions = getAllNonDeletedTransactions(transactionsAndViolationsByReport?.[reportID ?? CONST.DEFAULT_NUMBER_ID]?.transactions ?? [], allReportActions ?? []); const reportTransactionIDs = reportTransactions?.map((transaction) => transaction.transactionID); useEffect(() => { @@ -308,7 +308,7 @@ function ReportActionsView({ loadNewerChats={loadNewerChats} listID={listID} shouldEnableAutoScrollToTopThreshold={shouldEnableAutoScroll} - transactionsByReportID={transactionsByReportID} + transactionsAndViolationsByReport={transactionsAndViolationsByReport} /> diff --git a/src/pages/home/report/TripSummary.tsx b/src/pages/home/report/TripSummary.tsx index a4474bb218d0..9fc434a8da8a 100644 --- a/src/pages/home/report/TripSummary.tsx +++ b/src/pages/home/report/TripSummary.tsx @@ -5,19 +5,19 @@ import TripDetailsView from '@components/ReportActionItem/TripDetailsView'; import useTripTransactions from '@hooks/useTripTransactions'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {ReportTransactionsDerivedValue} from '@src/types/onyx'; +import type {ReportTransactionsAndViolationsDerivedValue} from '@src/types/onyx'; type TripSummaryProps = { /** The report ID */ reportID: string | undefined; /** The transactions for the report */ - transactionsByReportID: ReportTransactionsDerivedValue; + transactionsAndViolationsByReport: ReportTransactionsAndViolationsDerivedValue; }; -function TripSummary({reportID, transactionsByReportID}: TripSummaryProps) { +function TripSummary({reportID, transactionsAndViolationsByReport}: TripSummaryProps) { const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID ?? CONST.DEFAULT_NUMBER_ID}`); - const tripTransactions = useTripTransactions(reportID, transactionsByReportID); + const tripTransactions = useTripTransactions(reportID, transactionsAndViolationsByReport); if (!reportID || tripTransactions.length === 0) { return null; diff --git a/src/types/onyx/DerivedValues.ts b/src/types/onyx/DerivedValues.ts index 046760d7ccb8..c9179fad9f1a 100644 --- a/src/types/onyx/DerivedValues.ts +++ b/src/types/onyx/DerivedValues.ts @@ -1,3 +1,4 @@ +import type {OnyxCollection} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; import type {Errors} from './OnyxCommon'; @@ -55,13 +56,13 @@ type ReportTransactionsAndViolations = { /** * The violations of the report. */ - violations: Record; + violations: OnyxCollection; }; /** * The derived value for report transactions. */ -type ReportTransactionsDerivedValue = Record; +type ReportTransactionsAndViolationsDerivedValue = Record; export default ReportAttributesDerivedValue; -export type {ReportAttributes, ReportAttributesDerivedValue, ReportTransactionsDerivedValue, ReportTransactionsAndViolations}; +export type {ReportAttributes, ReportAttributesDerivedValue, ReportTransactionsAndViolationsDerivedValue, ReportTransactionsAndViolations}; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 765fc6ce8f29..9f0332065d46 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -23,7 +23,7 @@ import type Credentials from './Credentials'; import type Currency from './Currency'; import type {CurrencyList} from './Currency'; import type CustomStatusDraft from './CustomStatusDraft'; -import type {ReportAttributesDerivedValue, ReportTransactionsDerivedValue} from './DerivedValues'; +import type {ReportAttributesDerivedValue, ReportTransactionsAndViolationsDerivedValue} from './DerivedValues'; import type DismissedProductTraining from './DismissedProductTraining'; import type DismissedReferralBanners from './DismissedReferralBanners'; import type Download from './Download'; @@ -259,7 +259,7 @@ export type { SidePanel, LastPaymentMethodType, ReportAttributesDerivedValue, - ReportTransactionsDerivedValue, + ReportTransactionsAndViolationsDerivedValue, TalkToAISales, ScheduleCallDraft, ValidateUserAndGetAccessiblePolicies, From 2388d488780b132c06477dcd342dab46bda1540b Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Fri, 6 Jun 2025 14:12:59 +0200 Subject: [PATCH 15/30] unify format of holding transactions and transactions violations --- .../MoneyRequestReportActionsList.tsx | 3 ++- .../MoneyRequestReportView/MoneyRequestReportView.tsx | 3 ++- .../MoneyRequestReportPreviewContent.tsx | 3 ++- .../ReportActionItem/MoneyRequestReportPreview/index.tsx | 4 ++-- src/components/ReportActionItem/ReportPreview.tsx | 4 ++-- src/hooks/useTripTransactions.ts | 4 ++-- src/libs/ReportUtils.ts | 2 +- .../configs/reportTransactionsAndViolations.ts | 9 ++++++--- src/pages/home/ReportScreen.tsx | 4 ++-- src/pages/home/report/ReportActionsView.tsx | 3 ++- src/types/onyx/DerivedValues.ts | 5 ++--- 11 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx index a3834c42f9a6..8aa1623bb4c4 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx @@ -124,7 +124,8 @@ function MoneyRequestReportActionsList({ const [isVisible, setIsVisible] = useState(Visibility.isVisible); const isFocused = useIsFocused(); const route = useRoute>(); - const transactions = useMemo(() => transactionsAndViolationsByReport?.[report.reportID]?.transactions ?? [], [transactionsAndViolationsByReport, report.reportID]); + const {transactions: reportTransactions} = transactionsAndViolationsByReport[report.reportID] ?? {}; + const transactions = useMemo(() => Object.values(reportTransactions ?? {}) ?? [], [reportTransactions]); const reportTransactionIDs = useMemo(() => transactions.map((transaction) => transaction.transactionID), [transactions]); const reportID = report?.reportID; diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx index c9fc43cc74b1..f11bc53eb7d2 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx @@ -100,7 +100,8 @@ function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayRe const {reportActions: unfilteredReportActions, hasNewerActions, hasOlderActions} = usePaginatedReportActions(reportID); const reportActions = getFilteredReportActionsForReportView(unfilteredReportActions); - const transactions = transactionsAndViolationsByReport[reportID ?? CONST.DEFAULT_NUMBER_ID].transactions ?? []; + const {transactions: reportTransactions} = transactionsAndViolationsByReport[reportID ?? CONST.DEFAULT_NUMBER_ID] ?? {}; + const transactions = useMemo(() => Object.values(reportTransactions ?? {}) ?? [], [reportTransactions]); const reportTransactionIDs = transactions?.map((transaction) => transaction.transactionID); const transactionThreadReportID = getOneTransactionThreadReportID(reportID, reportActions ?? [], isOffline, reportTransactionIDs); const prevTransactions = usePrevious(transactions); diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx index 26f3599f777c..978d72e6b7f1 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx @@ -116,7 +116,8 @@ function MoneyRequestReportPreviewContent({ shouldShowBorder = false, onPress, }: MoneyRequestReportPreviewContentProps) { - const transactions = useMemo(() => transactionsAndViolationsByReport[iouReportID ?? CONST.DEFAULT_NUMBER_ID]?.transactions ?? [], [transactionsAndViolationsByReport, iouReportID]); + const {transactions: reportTransactions} = transactionsAndViolationsByReport[iouReportID ?? CONST.DEFAULT_NUMBER_ID] ?? {}; + const transactions = useMemo(() => Object.values(reportTransactions ?? {}) ?? [], [reportTransactions]); const lastTransaction = transactions?.at(0); const shouldShowEmptyPlaceholder = transactions.length === 0; const theme = useTheme(); diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx index d0c909c28ea6..a299ef72abbb 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx @@ -51,9 +51,9 @@ function MoneyRequestReportPreview({ canBeMissing: true, }); const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${iouReportID ?? CONST.DEFAULT_NUMBER_ID}`, {canBeMissing: true}); - const violations = transactionsAndViolationsByReport[iouReportID ?? CONST.DEFAULT_NUMBER_ID]?.violations ?? {}; + const {transactions: reportTransactions, violations} = transactionsAndViolationsByReport[iouReportID ?? CONST.DEFAULT_NUMBER_ID] ?? {}; const policy = usePolicy(policyID); - const transactions = useMemo(() => transactionsAndViolationsByReport[iouReportID ?? CONST.DEFAULT_NUMBER_ID]?.transactions ?? [], [transactionsAndViolationsByReport, iouReportID]); + const transactions = useMemo(() => Object.values(reportTransactions ?? {}) ?? [], [reportTransactions]); const lastTransaction = transactions?.at(0); const lastTransactionViolations = useTransactionViolations(lastTransaction?.transactionID); const {isDelegateAccessRestricted} = useDelegateUserDetails(); diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index 9f2bfdb14db9..aee47b49654f 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -162,9 +162,9 @@ function ReportPreview({ }: ReportPreviewProps) { const policy = usePolicy(policyID); const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`, {canBeMissing: false}); - const transactions = useMemo(() => transactionsAndViolationsByReport[iouReportID ?? CONST.DEFAULT_NUMBER_ID]?.transactions ?? [], [transactionsAndViolationsByReport, iouReportID]); + const {transactions: reportTransactions, violations} = transactionsAndViolationsByReport[iouReportID ?? CONST.DEFAULT_NUMBER_ID] ?? {}; const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${iouReportID ?? CONST.DEFAULT_NUMBER_ID}`, {canBeMissing: true}); - const violations = useMemo(() => transactionsAndViolationsByReport[iouReportID ?? CONST.DEFAULT_NUMBER_ID]?.violations ?? {}, [transactionsAndViolationsByReport, iouReportID]); + const transactions = useMemo(() => Object.values(reportTransactions ?? {}) ?? [], [reportTransactions]); const isIouReportArchived = useReportIsArchived(iouReportID); const lastTransaction = transactions?.at(0); const transactionIDList = transactions?.map((reportTransaction) => reportTransaction.transactionID) ?? []; diff --git a/src/hooks/useTripTransactions.ts b/src/hooks/useTripTransactions.ts index 97f519a71f5e..dafbcb006926 100644 --- a/src/hooks/useTripTransactions.ts +++ b/src/hooks/useTripTransactions.ts @@ -20,8 +20,8 @@ function useTripTransactions(reportID: string | undefined, transactionsAndViolat .filter((report) => report && report.chatReportID === reportID) .map((report) => report?.reportID), }); - - const tripTransactions = tripTransactionReportIDs.flatMap((transactionReportID) => transactionsAndViolationsByReport[transactionReportID ?? CONST.DEFAULT_NUMBER_ID].transactions ?? []); + const {transactions} = transactionsAndViolationsByReport[reportID ?? CONST.DEFAULT_NUMBER_ID] ?? {}; + const tripTransactions = tripTransactionReportIDs.flatMap((transactionReportID) => transactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionReportID}`] ?? []); return tripTransactions; } diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 628d0c403233..c3d92761fd68 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -968,7 +968,7 @@ Onyx.connect({ if (!value) { return; } - reportsTransactions = Object.fromEntries(Object.entries(value).map(([reportID, transactionsAndViolations]) => [reportID, transactionsAndViolations.transactions])); + reportsTransactions = Object.fromEntries(Object.entries(value).map(([reportID, {transactions}]) => [reportID, Object.values(transactions ?? {})])); }, }); diff --git a/src/libs/actions/OnyxDerived/configs/reportTransactionsAndViolations.ts b/src/libs/actions/OnyxDerived/configs/reportTransactionsAndViolations.ts index b7a67f1d0a16..c673792d0b0a 100644 --- a/src/libs/actions/OnyxDerived/configs/reportTransactionsAndViolations.ts +++ b/src/libs/actions/OnyxDerived/configs/reportTransactionsAndViolations.ts @@ -10,7 +10,7 @@ export default createOnyxDerivedValueConfig({ return {}; } - return Object.values(transactions).reduce((acc, transaction) => { + return Object.entries(transactions).reduce((acc, [transactionKey, transaction]) => { const reportID = transaction?.reportID; if (!reportID) { return acc; @@ -18,7 +18,7 @@ export default createOnyxDerivedValueConfig({ if (!acc[reportID]) { acc[reportID] = { - transactions: [], + transactions: {}, violations: {}, }; } @@ -30,7 +30,10 @@ export default createOnyxDerivedValueConfig({ [`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`]: transactionViolations, }; } - acc[reportID].transactions.push(transaction); + acc[reportID].transactions = { + ...acc[reportID].transactions, + [transactionKey]: transaction, + }; return acc; }, {}); diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index bad756888a4f..070fc9432eb5 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -296,8 +296,8 @@ function ReportScreen({route, navigation}: ReportScreenProps) { const [transactionsAndViolationsByReport = {}] = useOnyx(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS_AND_VIOLATIONS, { canBeMissing: false, }); - - const reportTransactions = getAllNonDeletedTransactions(transactionsAndViolationsByReport?.[reportID ?? CONST.DEFAULT_NUMBER_ID]?.transactions ?? [], reportActions); + const {transactions} = transactionsAndViolationsByReport[reportID ?? CONST.DEFAULT_NUMBER_ID] ?? {}; + const reportTransactions = getAllNonDeletedTransactions(Object.values(transactions ?? {}) ?? [], reportActions); const reportTransactionIDs = reportTransactions?.map((transaction) => transaction.transactionID); const transactionThreadReportID = getOneTransactionThreadReportID(reportID, reportActions ?? [], isOffline, reportTransactionIDs); const [transactionThreadReportActions = {}] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReportID}`, {canBeMissing: true}); diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx index 862351cd2302..a31aff22d2ac 100755 --- a/src/pages/home/report/ReportActionsView.tsx +++ b/src/pages/home/report/ReportActionsView.tsx @@ -99,7 +99,8 @@ function ReportActionsView({ const prevShouldUseNarrowLayoutRef = useRef(shouldUseNarrowLayout); const reportID = report.reportID; const isReportFullyVisible = useMemo((): boolean => getIsReportFullyVisible(isFocused), [isFocused]); - const reportTransactions = getAllNonDeletedTransactions(transactionsAndViolationsByReport?.[reportID ?? CONST.DEFAULT_NUMBER_ID]?.transactions ?? [], allReportActions ?? []); + const {transactions} = transactionsAndViolationsByReport[reportID ?? CONST.DEFAULT_NUMBER_ID] ?? {}; + const reportTransactions = getAllNonDeletedTransactions(Object.values(transactions ?? {}) ?? [], allReportActions ?? []); const reportTransactionIDs = reportTransactions?.map((transaction) => transaction.transactionID); useEffect(() => { diff --git a/src/types/onyx/DerivedValues.ts b/src/types/onyx/DerivedValues.ts index c9179fad9f1a..6afd8bb66eb0 100644 --- a/src/types/onyx/DerivedValues.ts +++ b/src/types/onyx/DerivedValues.ts @@ -1,4 +1,3 @@ -import type {OnyxCollection} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; import type {Errors} from './OnyxCommon'; @@ -52,11 +51,11 @@ type ReportTransactionsAndViolations = { /** * The transactions of the report. */ - transactions: Transaction[]; + transactions: Record; /** * The violations of the report. */ - violations: OnyxCollection; + violations: Record; }; /** From ed435b4592bef9c9a15614f13fea51484c05c22d Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Fri, 6 Jun 2025 14:55:40 +0200 Subject: [PATCH 16/30] atomic updates on derived value --- .../reportTransactionsAndViolations.ts | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/libs/actions/OnyxDerived/configs/reportTransactionsAndViolations.ts b/src/libs/actions/OnyxDerived/configs/reportTransactionsAndViolations.ts index c673792d0b0a..01e655a9ede3 100644 --- a/src/libs/actions/OnyxDerived/configs/reportTransactionsAndViolations.ts +++ b/src/libs/actions/OnyxDerived/configs/reportTransactionsAndViolations.ts @@ -5,12 +5,24 @@ import type {ReportTransactionsAndViolationsDerivedValue} from '@src/types/onyx' export default createOnyxDerivedValueConfig({ key: ONYXKEYS.DERIVED.REPORT_TRANSACTIONS_AND_VIOLATIONS, dependencies: [ONYXKEYS.COLLECTION.TRANSACTION, ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS], - compute: ([transactions, violations]) => { + compute: ([transactions, violations], {sourceValues, currentValue}) => { if (!transactions) { return {}; } - return Object.entries(transactions).reduce((acc, [transactionKey, transaction]) => { + const transactionsUpdates = sourceValues?.[ONYXKEYS.COLLECTION.TRANSACTION]; + const transactionViolationsUpdates = sourceValues?.[ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS]; + let transactionsToProcess = Object.keys(transactions); + if (transactionsUpdates) { + transactionsToProcess = Object.keys(transactionsUpdates); + } else if (transactionViolationsUpdates) { + transactionsToProcess = Object.keys(transactionViolationsUpdates).map((transactionViolation) => + transactionViolation.replace(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, ONYXKEYS.COLLECTION.TRANSACTION), + ); + } + + return transactionsToProcess.reduce((acc, transactionKey) => { + const transaction = transactions[transactionKey]; const reportID = transaction?.reportID; if (!reportID) { return acc; @@ -24,18 +36,14 @@ export default createOnyxDerivedValueConfig({ } const transactionID = transaction.transactionID; const transactionViolations = violations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`]; + if (transactionViolations && transactionViolations.length > 0) { - acc[reportID].violations = { - ...acc[reportID].violations, - [`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`]: transactionViolations, - }; + acc[reportID].violations[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`] = transactionViolations; } - acc[reportID].transactions = { - ...acc[reportID].transactions, - [transactionKey]: transaction, - }; + + acc[reportID].transactions[transactionKey] = transaction; return acc; - }, {}); + }, currentValue ?? {}); }, }); From 6e56c167d9b48b23a5408af49bcd6d408110e641 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Mon, 9 Jun 2025 08:34:39 +0200 Subject: [PATCH 17/30] fix typechecks in tests --- tests/perf-test/ReportActionsList.perf-test.tsx | 1 + tests/ui/MoneyRequestReportPreview.test.tsx | 1 + tests/ui/components/ReportPreviewTest.tsx | 1 + 3 files changed, 3 insertions(+) diff --git a/tests/perf-test/ReportActionsList.perf-test.tsx b/tests/perf-test/ReportActionsList.perf-test.tsx index 42026e63f6a8..d3546621a654 100644 --- a/tests/perf-test/ReportActionsList.perf-test.tsx +++ b/tests/perf-test/ReportActionsList.perf-test.tsx @@ -121,6 +121,7 @@ function ReportActionsListWrapper() { loadOlderChats={mockLoadChats} loadNewerChats={mockLoadChats} transactionThreadReport={report} + transactionsAndViolationsByReport={{}} /> diff --git a/tests/ui/MoneyRequestReportPreview.test.tsx b/tests/ui/MoneyRequestReportPreview.test.tsx index d837d2063b7d..531d253658c6 100644 --- a/tests/ui/MoneyRequestReportPreview.test.tsx +++ b/tests/ui/MoneyRequestReportPreview.test.tsx @@ -77,6 +77,7 @@ const renderPage = ({isWhisper = false, isHovered = false, contextMenuAnchor = n onPaymentOptionsHide={() => {}} isHovered={isHovered} isWhisper={isWhisper} + transactionsAndViolationsByReport={{}} /> diff --git a/tests/ui/components/ReportPreviewTest.tsx b/tests/ui/components/ReportPreviewTest.tsx index f83314540479..9744ef3c8ef4 100644 --- a/tests/ui/components/ReportPreviewTest.tsx +++ b/tests/ui/components/ReportPreviewTest.tsx @@ -79,6 +79,7 @@ describe('ReportPreview', () => { policyID="" checkIfContextMenuActive={emptyFunction} onShowContextMenu={emptyFunction} + transactionsAndViolationsByReport={{}} /> , From 8d43b4f1806373d38e306f28b772afad1d9c5c58 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Mon, 9 Jun 2025 09:33:16 +0200 Subject: [PATCH 18/30] fix IOUTest --- tests/actions/IOUTest.ts | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index f81d40bef8f0..872af526744f 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -1,9 +1,8 @@ -import {renderHook} from '@testing-library/react-native'; import {format} from 'date-fns'; import isEqual from 'lodash/isEqual'; import type {OnyxCollection, OnyxEntry, OnyxInputValue} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; -import useReportWithTransactionsAndViolations from '@hooks/useReportWithTransactionsAndViolations'; +import OnyxUtils from 'react-native-onyx/dist/OnyxUtils'; import { addSplitExpenseField, calculateDiffAmount, @@ -55,6 +54,7 @@ import { import {buildOptimisticIOUReport, buildOptimisticIOUReportAction, buildTransactionThread, createDraftTransactionAndNavigateToParticipantSelector, isIOUReport} from '@libs/ReportUtils'; import type {OptimisticChatReport} from '@libs/ReportUtils'; import {buildOptimisticTransaction, getValidWaypoints, isDistanceRequest as isDistanceRequestUtil} from '@libs/TransactionUtils'; +import initOnyxDerivedValues from '@userActions/OnyxDerived'; import CONST from '@src/CONST'; import type {IOUAction} from '@src/CONST'; import OnyxUpdateManager from '@src/libs/actions/OnyxUpdateManager'; @@ -145,6 +145,7 @@ describe('actions/IOU', () => { [ONYXKEYS.PERSONAL_DETAILS_LIST]: {[RORY_ACCOUNT_ID]: {accountID: RORY_ACCOUNT_ID, login: RORY_EMAIL}}, }, }); + initOnyxDerivedValues(); }); let mockFetch: MockFetch; @@ -5125,9 +5126,6 @@ describe('actions/IOU', () => { await waitForBatchedUpdates(); expect(canApproveIOU(fakeReport, fakePolicy)).toBeFalsy(); - // Then should return false when passing transactions directly as the third parameter instead of relying on Onyx data - const {result} = renderHook(() => useReportWithTransactionsAndViolations(reportID)); - expect(canApproveIOU(result.current.at(0) as Report, fakePolicy, result.current.at(1) as Transaction[])).toBeFalsy(); }); it('should return false if we have only scan failure transactions', async () => { const policyID = '2'; @@ -5180,9 +5178,6 @@ describe('actions/IOU', () => { await waitForBatchedUpdates(); expect(canApproveIOU(fakeReport, fakePolicy)).toBeFalsy(); - // Then should return false when passing transactions directly as the third parameter instead of relying on Onyx data - const {result} = renderHook(() => useReportWithTransactionsAndViolations(reportID)); - expect(canApproveIOU(result.current.at(0) as Report, fakePolicy, result.current.at(1) as Transaction[])).toBeFalsy(); }); it('should return false if all transactions are pending card or scan failure transaction', async () => { const policyID = '2'; @@ -5226,9 +5221,10 @@ describe('actions/IOU', () => { await waitForBatchedUpdates(); expect(canApproveIOU(fakeReport, fakePolicy)).toBeFalsy(); - // Then should return false when passing transactions directly as the third parameter instead of relying on Onyx data - const {result} = renderHook(() => useReportWithTransactionsAndViolations(reportID)); - expect(canApproveIOU(result.current.at(0) as Report, fakePolicy, result.current.at(1) as Transaction[])).toBeFalsy(); + const transactionsAndViolations = await OnyxUtils.get(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS_AND_VIOLATIONS); + const report = await OnyxUtils.get(`${ONYXKEYS.COLLECTION.REPORT}${fakeReport.reportID}`); + const reportTransactionsAndViolations = transactionsAndViolations?.[fakeReport.reportID]; + expect(canApproveIOU(report, fakePolicy, Object.values(reportTransactionsAndViolations?.transactions ?? {}))).toBeFalsy(); }); it('should return true if at least one transactions is not pending card or scan failure transaction', async () => { const policyID = '2'; @@ -5277,9 +5273,6 @@ describe('actions/IOU', () => { await waitForBatchedUpdates(); expect(canApproveIOU(fakeReport, fakePolicy)).toBeTruthy(); - // Then should return true when passing transactions directly as the third parameter instead of relying on Onyx data - const {result} = renderHook(() => useReportWithTransactionsAndViolations(reportID)); - expect(canApproveIOU(result.current.at(0) as Report, fakePolicy, result.current.at(1) as Transaction[])).toBeTruthy(); }); it('should return false if the report is closed', async () => { From 070ec83e1a04ad57d572885edab06dc913c5528b Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Mon, 9 Jun 2025 09:57:22 +0200 Subject: [PATCH 19/30] cleanup MoneyRequestReportPreviewContentProps --- .../MoneyRequestReportPreviewContent.tsx | 4 +--- .../ReportActionItem/MoneyRequestReportPreview/index.tsx | 2 +- .../ReportActionItem/MoneyRequestReportPreview/types.ts | 3 ++- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx index 978d72e6b7f1..d29c94f7097b 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx @@ -100,7 +100,7 @@ function MoneyRequestReportPreviewContent({ chatReport, invoiceReceiverPolicy, iouReport, - transactionsAndViolationsByReport, + transactions, violations, policy, invoiceReceiverPersonalDetail, @@ -116,8 +116,6 @@ function MoneyRequestReportPreviewContent({ shouldShowBorder = false, onPress, }: MoneyRequestReportPreviewContentProps) { - const {transactions: reportTransactions} = transactionsAndViolationsByReport[iouReportID ?? CONST.DEFAULT_NUMBER_ID] ?? {}; - const transactions = useMemo(() => Object.values(reportTransactions ?? {}) ?? [], [reportTransactions]); const lastTransaction = transactions?.at(0); const shouldShowEmptyPlaceholder = transactions.length === 0; const theme = useTheme(); diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx index a299ef72abbb..fb7806e1b930 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx @@ -121,7 +121,7 @@ function MoneyRequestReportPreview({ checkIfContextMenuActive={checkIfContextMenuActive} onPaymentOptionsShow={onPaymentOptionsShow} onPaymentOptionsHide={onPaymentOptionsHide} - transactionsAndViolationsByReport={transactionsAndViolationsByReport} + transactions={transactions} violations={violations} policy={policy} invoiceReceiverPersonalDetail={invoiceReceiverPersonalDetail} diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/types.ts b/src/components/ReportActionItem/MoneyRequestReportPreview/types.ts index fb2fa940b979..f61d9a87a2c0 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/types.ts +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/types.ts @@ -65,6 +65,7 @@ type MoneyRequestReportPreviewContentOnyxProps = { chatReport: OnyxEntry; invoiceReceiverPolicy: OnyxEntry; iouReport: OnyxEntry; + transactions: Transaction[]; violations: OnyxCollection; policy: OnyxEntry; invoiceReceiverPersonalDetail: OnyxEntry; @@ -73,7 +74,7 @@ type MoneyRequestReportPreviewContentOnyxProps = { }; type MoneyRequestReportPreviewContentProps = MoneyRequestReportPreviewContentOnyxProps & - Omit & { + Omit & { /** Extra styles passed used by MoneyRequestReportPreviewContent */ reportPreviewStyles: MoneyRequestReportPreviewStyleType; From 24a54b888c6948f741f0731311529df3508f583e Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Mon, 9 Jun 2025 10:12:59 +0200 Subject: [PATCH 20/30] bring back report utils change --- src/libs/ReportUtils.ts | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index c3d92761fd68..c07da3e53973 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -953,22 +953,29 @@ Onyx.connect({ }); let allTransactions: OnyxCollection = {}; +let reportsTransactions: Record = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.TRANSACTION, waitForCollectionCallback: true, - callback: (value) => { - allTransactions = Object.fromEntries(Object.entries(value).filter(([, transaction]) => transaction)); - }, -}); - -let reportsTransactions: Record = {}; -Onyx.connect({ - key: ONYXKEYS.DERIVED.REPORT_TRANSACTIONS_AND_VIOLATIONS, callback: (value) => { if (!value) { return; } - reportsTransactions = Object.fromEntries(Object.entries(value).map(([reportID, {transactions}]) => [reportID, Object.values(transactions ?? {})])); + allTransactions = Object.fromEntries(Object.entries(value).filter(([, transaction]) => transaction)); + + reportsTransactions = Object.values(value).reduce>((all, transaction) => { + const reportsMap = all; + if (!transaction?.reportID) { + return reportsMap; + } + + if (!reportsMap[transaction.reportID]) { + reportsMap[transaction.reportID] = []; + } + reportsMap[transaction.reportID].push(transaction); + + return all; + }, {}); }, }); From 28765333f326b2849eb8b09e5ad887d303d3450c Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Mon, 9 Jun 2025 10:18:48 +0200 Subject: [PATCH 21/30] update IOUTest --- tests/actions/IOUTest.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/actions/IOUTest.ts b/tests/actions/IOUTest.ts index 872af526744f..c741f5fae317 100644 --- a/tests/actions/IOUTest.ts +++ b/tests/actions/IOUTest.ts @@ -5126,6 +5126,10 @@ describe('actions/IOU', () => { await waitForBatchedUpdates(); expect(canApproveIOU(fakeReport, fakePolicy)).toBeFalsy(); + const transactionsAndViolations = await OnyxUtils.get(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS_AND_VIOLATIONS); + const report = await OnyxUtils.get(`${ONYXKEYS.COLLECTION.REPORT}${fakeReport.reportID}`); + const reportTransactionsAndViolations = transactionsAndViolations?.[fakeReport.reportID]; + expect(canApproveIOU(report, fakePolicy, Object.values(reportTransactionsAndViolations?.transactions ?? {}))).toBeFalsy(); }); it('should return false if we have only scan failure transactions', async () => { const policyID = '2'; @@ -5178,6 +5182,10 @@ describe('actions/IOU', () => { await waitForBatchedUpdates(); expect(canApproveIOU(fakeReport, fakePolicy)).toBeFalsy(); + const transactionsAndViolations = await OnyxUtils.get(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS_AND_VIOLATIONS); + const report = await OnyxUtils.get(`${ONYXKEYS.COLLECTION.REPORT}${fakeReport.reportID}`); + const reportTransactionsAndViolations = transactionsAndViolations?.[fakeReport.reportID]; + expect(canApproveIOU(report, fakePolicy, Object.values(reportTransactionsAndViolations?.transactions ?? {}))).toBeFalsy(); }); it('should return false if all transactions are pending card or scan failure transaction', async () => { const policyID = '2'; @@ -5273,6 +5281,10 @@ describe('actions/IOU', () => { await waitForBatchedUpdates(); expect(canApproveIOU(fakeReport, fakePolicy)).toBeTruthy(); + const transactionsAndViolations = await OnyxUtils.get(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS_AND_VIOLATIONS); + const report = await OnyxUtils.get(`${ONYXKEYS.COLLECTION.REPORT}${fakeReport.reportID}`); + const reportTransactionsAndViolations = transactionsAndViolations?.[fakeReport.reportID]; + expect(canApproveIOU(report, fakePolicy, Object.values(reportTransactionsAndViolations?.transactions ?? {}))).toBeTruthy(); }); it('should return false if the report is closed', async () => { From 8c14b1083f80b86d18c8a67a4690e04552c56554 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Mon, 9 Jun 2025 10:25:39 +0200 Subject: [PATCH 22/30] fix lint changed --- .../ReportActionItem/MoneyRequestReportPreview/index.tsx | 2 +- src/components/ReportActionItem/ReportPreview.tsx | 2 +- src/hooks/useTripTransactions.ts | 1 + src/pages/home/report/ReportActionItemParentAction.tsx | 2 +- src/pages/home/report/TripSummary.tsx | 3 +-- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx index fb7806e1b930..30bf67f10758 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx @@ -50,7 +50,7 @@ function MoneyRequestReportPreview({ personalDetails?.[chatReport?.invoiceReceiver && 'accountID' in chatReport.invoiceReceiver ? chatReport.invoiceReceiver.accountID : CONST.DEFAULT_NUMBER_ID], canBeMissing: true, }); - const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${iouReportID ?? CONST.DEFAULT_NUMBER_ID}`, {canBeMissing: true}); + const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`, {canBeMissing: true}); const {transactions: reportTransactions, violations} = transactionsAndViolationsByReport[iouReportID ?? CONST.DEFAULT_NUMBER_ID] ?? {}; const policy = usePolicy(policyID); const transactions = useMemo(() => Object.values(reportTransactions ?? {}) ?? [], [reportTransactions]); diff --git a/src/components/ReportActionItem/ReportPreview.tsx b/src/components/ReportActionItem/ReportPreview.tsx index aee47b49654f..54b56036bdca 100644 --- a/src/components/ReportActionItem/ReportPreview.tsx +++ b/src/components/ReportActionItem/ReportPreview.tsx @@ -163,7 +163,7 @@ function ReportPreview({ const policy = usePolicy(policyID); const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`, {canBeMissing: false}); const {transactions: reportTransactions, violations} = transactionsAndViolationsByReport[iouReportID ?? CONST.DEFAULT_NUMBER_ID] ?? {}; - const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${iouReportID ?? CONST.DEFAULT_NUMBER_ID}`, {canBeMissing: true}); + const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${iouReportID}`, {canBeMissing: true}); const transactions = useMemo(() => Object.values(reportTransactions ?? {}) ?? [], [reportTransactions]); const isIouReportArchived = useReportIsArchived(iouReportID); const lastTransaction = transactions?.at(0); diff --git a/src/hooks/useTripTransactions.ts b/src/hooks/useTripTransactions.ts index dafbcb006926..7751a2177434 100644 --- a/src/hooks/useTripTransactions.ts +++ b/src/hooks/useTripTransactions.ts @@ -19,6 +19,7 @@ function useTripTransactions(reportID: string | undefined, transactionsAndViolat Object.values(reports ?? {}) .filter((report) => report && report.chatReportID === reportID) .map((report) => report?.reportID), + canBeMissing: true, }); const {transactions} = transactionsAndViolationsByReport[reportID ?? CONST.DEFAULT_NUMBER_ID] ?? {}; const tripTransactions = tripTransactionReportIDs.flatMap((transactionReportID) => transactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionReportID}`] ?? []); diff --git a/src/pages/home/report/ReportActionItemParentAction.tsx b/src/pages/home/report/ReportActionItemParentAction.tsx index 069090fe1769..2e358004c76d 100644 --- a/src/pages/home/report/ReportActionItemParentAction.tsx +++ b/src/pages/home/report/ReportActionItemParentAction.tsx @@ -73,7 +73,7 @@ function ReportActionItemParentAction({ transactionsAndViolationsByReport, }: ReportActionItemParentActionProps) { const styles = useThemeStyles(); - const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); + const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: true}); const ancestorIDs = useRef(getAllAncestorReportActionIDs(report)); const ancestorReports = useRef>>({}); const [allAncestors, setAllAncestors] = useState([]); diff --git a/src/pages/home/report/TripSummary.tsx b/src/pages/home/report/TripSummary.tsx index 9fc434a8da8a..c51f3d621619 100644 --- a/src/pages/home/report/TripSummary.tsx +++ b/src/pages/home/report/TripSummary.tsx @@ -3,7 +3,6 @@ import {useOnyx} from 'react-native-onyx'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import TripDetailsView from '@components/ReportActionItem/TripDetailsView'; import useTripTransactions from '@hooks/useTripTransactions'; -import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {ReportTransactionsAndViolationsDerivedValue} from '@src/types/onyx'; @@ -16,7 +15,7 @@ type TripSummaryProps = { }; function TripSummary({reportID, transactionsAndViolationsByReport}: TripSummaryProps) { - const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID ?? CONST.DEFAULT_NUMBER_ID}`); + const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {canBeMissing: true}); const tripTransactions = useTripTransactions(reportID, transactionsAndViolationsByReport); if (!reportID || tripTransactions.length === 0) { From 48a49fa9f8517aabb4d3d58f81bee43aaf278bda Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Fri, 13 Jun 2025 09:56:15 +0200 Subject: [PATCH 23/30] pass transactions by report in MoneyRequestReportPreview tests --- tests/ui/MoneyRequestReportPreview.test.tsx | 42 +++++++++++++-------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/tests/ui/MoneyRequestReportPreview.test.tsx b/tests/ui/MoneyRequestReportPreview.test.tsx index 531d253658c6..e889bd73564f 100644 --- a/tests/ui/MoneyRequestReportPreview.test.tsx +++ b/tests/ui/MoneyRequestReportPreview.test.tsx @@ -1,7 +1,7 @@ import {PortalProvider} from '@gorhom/portal'; import * as NativeNavigation from '@react-navigation/native'; import {fireEvent, render, screen} from '@testing-library/react-native'; -import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; +import type {OnyxCollection} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import ComposeProviders from '@components/ComposeProviders'; import {LocaleContextProvider} from '@components/LocaleContextProvider'; @@ -19,9 +19,10 @@ import CONST from '@src/CONST'; import * as ReportActionUtils from '@src/libs/ReportActionsUtils'; import * as ReportUtils from '@src/libs/ReportUtils'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Report, Transaction, TransactionViolation, TransactionViolations} from '@src/types/onyx'; +import type {Transaction, TransactionViolation, TransactionViolations} from '@src/types/onyx'; +import type {ReportTransactionsAndViolationsDerivedValue} from '@src/types/onyx/DerivedValues'; import {actionR14932 as mockAction} from '../../__mocks__/reportData/actions'; -import {chatReportR14932 as mockChatReport, iouReportR14932 as mockIOUReport} from '../../__mocks__/reportData/reports'; +import {chatReportR14932 as mockChatReport} from '../../__mocks__/reportData/reports'; import {transactionR14932 as mockTransaction} from '../../__mocks__/reportData/transactions'; import {violationsR14932 as mockViolations} from '../../__mocks__/reportData/violations'; import * as TestHelper from '../utils/TestHelper'; @@ -44,12 +45,6 @@ jest.mock('@react-native-community/geolocation', () => ({ setRNConfiguration: jest.fn(), })); -jest.mock('@src/hooks/useReportWithTransactionsAndViolations', () => - jest.fn((): [OnyxEntry, Transaction[], OnyxCollection] => { - return [mockChatReport, [mockTransaction, {...mockTransaction, transactionID: mockSecondTransactionID}], {violations: mockViolations}]; - }), -); - const getIOUActionForReportID = (reportID: string | undefined, transactionID: string | undefined) => { if (!reportID || !transactionID) { return undefined; @@ -60,7 +55,7 @@ const getIOUActionForReportID = (reportID: string | undefined, transactionID: st const hasViolations = (reportID: string | undefined, transactionViolations: OnyxCollection, shouldShowInReview?: boolean) => (shouldShowInReview === undefined || shouldShowInReview) && Object.values(transactionViolations ?? {}).length > 0; -const renderPage = ({isWhisper = false, isHovered = false, contextMenuAnchor = null}: Partial) => { +const renderPage = ({isWhisper = false, isHovered = false, contextMenuAnchor = null, transactionsAndViolationsByReport = {}}: Partial) => { return render( @@ -69,7 +64,7 @@ const renderPage = ({isWhisper = false, isHovered = false, contextMenuAnchor = n {}} @@ -77,7 +72,7 @@ const renderPage = ({isWhisper = false, isHovered = false, contextMenuAnchor = n onPaymentOptionsHide={() => {}} isHovered={isHovered} isWhisper={isWhisper} - transactionsAndViolationsByReport={{}} + transactionsAndViolationsByReport={transactionsAndViolationsByReport} /> @@ -120,6 +115,19 @@ const mockOnyxViolations: Record<`${typeof ONYXKEYS.COLLECTION.TRANSACTION_VIOLA const arrayOfTransactions = Object.values(mockOnyxTransactions); +const buildTransactionsAndViolationsByReport = (): ReportTransactionsAndViolationsDerivedValue => { + const reportID = mockChatReport.iouReportID; + return { + [String(reportID)]: { + transactions: { + [`${ONYXKEYS.COLLECTION.TRANSACTION}${mockTransaction.transactionID}`]: mockTransaction, + [`${ONYXKEYS.COLLECTION.TRANSACTION}${mockSecondTransaction.transactionID}`]: mockSecondTransaction, + }, + violations: {}, + }, + }; +}; + TestHelper.setupApp(); TestHelper.setupGlobalFetchMock(); @@ -140,12 +148,14 @@ describe('MoneyRequestReportPreview', () => { }); it('renders transaction details and associated report name correctly', async () => { - renderPage({}); + const transactionsAndViolationsByReport = buildTransactionsAndViolationsByReport(); + renderPage({transactionsAndViolationsByReport}); await waitForBatchedUpdatesWithAct(); setCurrentWidth(); await Onyx.mergeCollection(ONYXKEYS.COLLECTION.TRANSACTION, mockOnyxTransactions).then(waitForBatchedUpdates); const {reportName: moneyRequestReportPreviewName = ''} = mockChatReport; - for (const transaction of arrayOfTransactions) { + const reportTransactions = transactionsAndViolationsByReport[mockChatReport?.iouReportID ?? CONST.DEFAULT_NUMBER_ID]?.transactions; + for (const transaction of Object.values(reportTransactions)) { const {transactionDisplayAmount, transactionHeaderText} = getTransactionDisplayAmountAndHeaderText(transaction); expect(screen.getByText(moneyRequestReportPreviewName)).toBeOnTheScreen(); @@ -156,7 +166,7 @@ describe('MoneyRequestReportPreview', () => { }); it('renders RBR for every transaction with violations', async () => { - renderPage({}); + renderPage({transactionsAndViolationsByReport: buildTransactionsAndViolationsByReport()}); await waitForBatchedUpdatesWithAct(); setCurrentWidth(); await Onyx.multiSet({...mockOnyxTransactions, ...mockOnyxViolations}); @@ -164,7 +174,7 @@ describe('MoneyRequestReportPreview', () => { }); it('renders a skeleton if the transaction is empty', async () => { - renderPage({}); + renderPage({transactionsAndViolationsByReport: buildTransactionsAndViolationsByReport()}); await waitForBatchedUpdatesWithAct(); setCurrentWidth(); expect(screen.getAllByTestId(TransactionPreviewSkeletonView.displayName)).toHaveLength(2); From 88736c94effcfff3a727ced65e19b4aebbce0e52 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Fri, 13 Jun 2025 10:14:57 +0200 Subject: [PATCH 24/30] iterate through array of transactions --- tests/ui/MoneyRequestReportPreview.test.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/ui/MoneyRequestReportPreview.test.tsx b/tests/ui/MoneyRequestReportPreview.test.tsx index e889bd73564f..ebd33767d0a3 100644 --- a/tests/ui/MoneyRequestReportPreview.test.tsx +++ b/tests/ui/MoneyRequestReportPreview.test.tsx @@ -148,14 +148,12 @@ describe('MoneyRequestReportPreview', () => { }); it('renders transaction details and associated report name correctly', async () => { - const transactionsAndViolationsByReport = buildTransactionsAndViolationsByReport(); - renderPage({transactionsAndViolationsByReport}); + renderPage({transactionsAndViolationsByReport: buildTransactionsAndViolationsByReport()}); await waitForBatchedUpdatesWithAct(); setCurrentWidth(); await Onyx.mergeCollection(ONYXKEYS.COLLECTION.TRANSACTION, mockOnyxTransactions).then(waitForBatchedUpdates); const {reportName: moneyRequestReportPreviewName = ''} = mockChatReport; - const reportTransactions = transactionsAndViolationsByReport[mockChatReport?.iouReportID ?? CONST.DEFAULT_NUMBER_ID]?.transactions; - for (const transaction of Object.values(reportTransactions)) { + for (const transaction of arrayOfTransactions) { const {transactionDisplayAmount, transactionHeaderText} = getTransactionDisplayAmountAndHeaderText(transaction); expect(screen.getByText(moneyRequestReportPreviewName)).toBeOnTheScreen(); From 014f7451bd9717ca0dd91fccbb796953ba298d25 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Fri, 13 Jun 2025 11:16:56 +0200 Subject: [PATCH 25/30] fix MoneyRequestReportPreview test --- tests/ui/MoneyRequestReportPreview.test.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/ui/MoneyRequestReportPreview.test.tsx b/tests/ui/MoneyRequestReportPreview.test.tsx index ebd33767d0a3..0c82e630c957 100644 --- a/tests/ui/MoneyRequestReportPreview.test.tsx +++ b/tests/ui/MoneyRequestReportPreview.test.tsx @@ -119,11 +119,8 @@ const buildTransactionsAndViolationsByReport = (): ReportTransactionsAndViolatio const reportID = mockChatReport.iouReportID; return { [String(reportID)]: { - transactions: { - [`${ONYXKEYS.COLLECTION.TRANSACTION}${mockTransaction.transactionID}`]: mockTransaction, - [`${ONYXKEYS.COLLECTION.TRANSACTION}${mockSecondTransaction.transactionID}`]: mockSecondTransaction, - }, - violations: {}, + transactions: mockOnyxTransactions, + violations: mockOnyxViolations, }, }; }; @@ -152,6 +149,7 @@ describe('MoneyRequestReportPreview', () => { await waitForBatchedUpdatesWithAct(); setCurrentWidth(); await Onyx.mergeCollection(ONYXKEYS.COLLECTION.TRANSACTION, mockOnyxTransactions).then(waitForBatchedUpdates); + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${mockChatReport.iouReportID}`, mockChatReport).then(waitForBatchedUpdates); const {reportName: moneyRequestReportPreviewName = ''} = mockChatReport; for (const transaction of arrayOfTransactions) { const {transactionDisplayAmount, transactionHeaderText} = getTransactionDisplayAmountAndHeaderText(transaction); From 80a22f9d45b7e96292ea2c797c6db26a79d907ab Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Fri, 13 Jun 2025 11:22:23 +0200 Subject: [PATCH 26/30] fix ReportPreviewTest --- tests/ui/components/ReportPreviewTest.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/ui/components/ReportPreviewTest.tsx b/tests/ui/components/ReportPreviewTest.tsx index 9744ef3c8ef4..dc1c7f835a04 100644 --- a/tests/ui/components/ReportPreviewTest.tsx +++ b/tests/ui/components/ReportPreviewTest.tsx @@ -79,7 +79,12 @@ describe('ReportPreview', () => { policyID="" checkIfContextMenuActive={emptyFunction} onShowContextMenu={emptyFunction} - transactionsAndViolationsByReport={{}} + transactionsAndViolationsByReport={{ + [chatReportID]: { + transactions: {}, + violations: {}, + }, + }} /> , From db7e699f22dfb6b19ea3913d3e9a38b877bab997 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Fri, 13 Jun 2025 11:45:46 +0200 Subject: [PATCH 27/30] remove defaults in useOnyx --- .../MoneyRequestReportView/MoneyRequestReportView.tsx | 4 ++-- src/pages/Search/SearchMoneyRequestReportPage.tsx | 6 +++--- src/pages/home/ReportScreen.tsx | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx index f9380ab1d720..2bd1b31b16a7 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx @@ -88,7 +88,7 @@ function getParentReportAction(parentReportActions: OnyxEntry Object.values(reportTransactions ?? {}) ?? [], [reportTransactions]); const reportTransactionIDs = transactions?.map((transaction) => transaction.transactionID); const transactionThreadReportID = getOneTransactionThreadReportID(report, chatReport, reportActions ?? [], isOffline, reportTransactionIDs); diff --git a/src/pages/Search/SearchMoneyRequestReportPage.tsx b/src/pages/Search/SearchMoneyRequestReportPage.tsx index 01b1b2d56655..3fbbfcddf6ea 100644 --- a/src/pages/Search/SearchMoneyRequestReportPage.tsx +++ b/src/pages/Search/SearchMoneyRequestReportPage.tsx @@ -43,7 +43,7 @@ function SearchMoneyRequestReportPage({route}: SearchMoneyRequestPageProps) { const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {allowStaleData: true, initialValue: {}, canBeMissing: false}); const policy = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`]; const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP, {canBeMissing: true}); - const [transactionsAndViolationsByReport = {}] = useOnyx(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS_AND_VIOLATIONS, {canBeMissing: true}); + const [transactionsAndViolationsByReport] = useOnyx(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS_AND_VIOLATIONS, {canBeMissing: true}); const {isEditingDisabled, isCurrentReportLoadedFromOnyx} = useIsReportReadyToDisplay(report, reportIDFromRoute); @@ -101,7 +101,7 @@ function SearchMoneyRequestReportPage({route}: SearchMoneyRequestPageProps) { policy={policy} shouldDisplayReportFooter={isCurrentReportLoadedFromOnyx} backToRoute={route.params.backTo} - transactionsAndViolationsByReport={transactionsAndViolationsByReport} + transactionsAndViolationsByReport={transactionsAndViolationsByReport ?? {}} /> @@ -137,7 +137,7 @@ function SearchMoneyRequestReportPage({route}: SearchMoneyRequestPageProps) { policy={policy} shouldDisplayReportFooter={isCurrentReportLoadedFromOnyx} backToRoute={route.params.backTo} - transactionsAndViolationsByReport={transactionsAndViolationsByReport} + transactionsAndViolationsByReport={transactionsAndViolationsByReport ?? {}} /> diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 742c9b619a83..8b5b6105320a 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -296,10 +296,10 @@ function ReportScreen({route, navigation}: ReportScreenProps) { // OpenReport will be called each time the user scrolls up the report a bit, clicks on report preview, and then goes back. const isLinkedMessagePageReady = isLinkedMessageAvailable && (reportActions.length - indexOfLinkedMessage >= CONST.REPORT.MIN_INITIAL_REPORT_ACTION_COUNT || doesCreatedActionExists()); - const [transactionsAndViolationsByReport = {}] = useOnyx(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS_AND_VIOLATIONS, { + const [transactionsAndViolationsByReport] = useOnyx(ONYXKEYS.DERIVED.REPORT_TRANSACTIONS_AND_VIOLATIONS, { canBeMissing: false, }); - const {transactions} = transactionsAndViolationsByReport[reportID ?? CONST.DEFAULT_NUMBER_ID] ?? {}; + const {transactions} = transactionsAndViolationsByReport?.[reportID ?? CONST.DEFAULT_NUMBER_ID] ?? {}; const reportTransactions = getAllNonDeletedTransactions(Object.values(transactions ?? {}) ?? [], reportActions); const reportTransactionIDs = reportTransactions?.map((transaction) => transaction.transactionID); const transactionThreadReportID = getOneTransactionThreadReportID(report, chatReport, reportActions ?? [], isOffline, reportTransactionIDs); @@ -829,7 +829,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) { hasOlderActions={hasOlderActions} parentReportAction={parentReportAction} transactionThreadReportID={transactionThreadReportID} - transactionsAndViolationsByReport={transactionsAndViolationsByReport} + transactionsAndViolationsByReport={transactionsAndViolationsByReport ?? {}} /> ) : null} {!!report && shouldDisplayMoneyRequestActionsList && !shouldWaitForTransactions ? ( @@ -837,7 +837,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) { report={report} policy={policy} reportActions={reportActions} - transactionsAndViolationsByReport={transactionsAndViolationsByReport} + transactionsAndViolationsByReport={transactionsAndViolationsByReport ?? {}} newTransactions={newTransactions} hasOlderActions={hasOlderActions} hasNewerActions={hasNewerActions} From 73aba68a88f1c9ecee6e4a641a575d5d902df0ce Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Fri, 13 Jun 2025 15:31:05 +0200 Subject: [PATCH 28/30] fix crash when transactions are undefined --- src/hooks/useTripTransactions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/useTripTransactions.ts b/src/hooks/useTripTransactions.ts index 7751a2177434..470cb3f65da5 100644 --- a/src/hooks/useTripTransactions.ts +++ b/src/hooks/useTripTransactions.ts @@ -22,7 +22,7 @@ function useTripTransactions(reportID: string | undefined, transactionsAndViolat canBeMissing: true, }); const {transactions} = transactionsAndViolationsByReport[reportID ?? CONST.DEFAULT_NUMBER_ID] ?? {}; - const tripTransactions = tripTransactionReportIDs.flatMap((transactionReportID) => transactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionReportID}`] ?? []); + const tripTransactions = tripTransactionReportIDs.flatMap((transactionReportID) => transactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionReportID}`] ?? []); return tripTransactions; } From 079f1d70887d955230ca7d29e639404c4348fc40 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Wed, 18 Jun 2025 09:53:47 +0200 Subject: [PATCH 29/30] replace reduce with for of --- .../reportTransactionsAndViolations.ts | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/libs/actions/OnyxDerived/configs/reportTransactionsAndViolations.ts b/src/libs/actions/OnyxDerived/configs/reportTransactionsAndViolations.ts index 01e655a9ede3..fcd450428316 100644 --- a/src/libs/actions/OnyxDerived/configs/reportTransactionsAndViolations.ts +++ b/src/libs/actions/OnyxDerived/configs/reportTransactionsAndViolations.ts @@ -1,6 +1,5 @@ import createOnyxDerivedValueConfig from '@userActions/OnyxDerived/createOnyxDerivedValueConfig'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {ReportTransactionsAndViolationsDerivedValue} from '@src/types/onyx'; export default createOnyxDerivedValueConfig({ key: ONYXKEYS.DERIVED.REPORT_TRANSACTIONS_AND_VIOLATIONS, @@ -21,29 +20,32 @@ export default createOnyxDerivedValueConfig({ ); } - return transactionsToProcess.reduce((acc, transactionKey) => { + const reportTransactionsAndViolations = currentValue ?? {}; + for (const transactionKey of transactionsToProcess) { const transaction = transactions[transactionKey]; const reportID = transaction?.reportID; if (!reportID) { - return acc; + // eslint-disable-next-line no-continue + continue; } - if (!acc[reportID]) { - acc[reportID] = { + if (!reportTransactionsAndViolations[reportID]) { + reportTransactionsAndViolations[reportID] = { transactions: {}, violations: {}, }; } + const transactionID = transaction.transactionID; const transactionViolations = violations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`]; if (transactionViolations && transactionViolations.length > 0) { - acc[reportID].violations[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`] = transactionViolations; + reportTransactionsAndViolations[reportID].violations[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`] = transactionViolations; } - acc[reportID].transactions[transactionKey] = transaction; + reportTransactionsAndViolations[reportID].transactions[transactionKey] = transaction; + } - return acc; - }, currentValue ?? {}); + return reportTransactionsAndViolations; }, }); From ee77303dd70236543f3c8c1c2cee176734eab2f5 Mon Sep 17 00:00:00 2001 From: Tomasz Misiukiewicz Date: Fri, 20 Jun 2025 08:39:57 +0200 Subject: [PATCH 30/30] fix lint changed --- Mobile-Expensify | 2 +- .../MoneyRequestReportView/MoneyRequestReportView.tsx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index a3d7aba325ea..48fd24f1ca79 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit a3d7aba325ea417507b464418dfdc0c972528edf +Subproject commit 48fd24f1ca793fdb96223a2fc3987a2af9bd263d diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx index ea6b860a9364..26f22069147d 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx @@ -24,6 +24,7 @@ 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';