Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import OfflineWithFeedback from '@components/OfflineWithFeedback';
import ReportActionsSkeletonView from '@components/ReportActionsSkeletonView';
import ReportHeaderSkeletonView from '@components/ReportHeaderSkeletonView';
import useNetwork from '@hooks/useNetwork';
import useNewTransactions from '@hooks/useNewTransactions';
import usePaginatedReportActions from '@hooks/usePaginatedReportActions';
import usePrevious from '@hooks/usePrevious';
import useThemeStyles from '@hooks/useThemeStyles';
import {removeFailedReport} from '@libs/actions/Report';
import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID';
Expand All @@ -24,7 +24,6 @@ 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';
Expand Down Expand Up @@ -106,17 +105,7 @@ function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayRe
const reportTransactionIDs = transactions?.map((transaction) => transaction.transactionID);
const transactionThreadReportID = getOneTransactionThreadReportID(report, chatReport, reportActions ?? [], isOffline, reportTransactionIDs);

const prevTransactions = usePrevious(transactions);

const newTransactions = useMemo(() => {
if (!prevTransactions || !transactions || transactions.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.
// eslint-disable-next-line react-compiler/react-compiler
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [transactions]);
const newTransactions = useNewTransactions(reportMetadata?.hasOnceLoadedReportActions, transactions);

const [parentReportAction] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getNonEmptyStringOnyxID(report?.parentReportID)}`, {
canEvict: false,
Expand Down
49 changes: 49 additions & 0 deletions src/hooks/useNewTransactions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import {useEffect, useMemo, useRef} from 'react';
import CONST from '@src/CONST';
import type {Transaction} from '@src/types/onyx';
import usePrevious from './usePrevious';

/**
* This hook returns new transactions that have been added since the last transactions update.
* This hook should be used only in the context of highlighting the new transactions on the Report table view.
*/
function useNewTransactions(hasOnceLoadedReportActions: boolean | undefined, transactions: Transaction[] | undefined) {
// If we haven't loaded report yet we set previous transactions to undefined.
const prevTransactions = usePrevious(hasOnceLoadedReportActions ? transactions : undefined);

// We need to skip the first transactions change, to avoid highlighting transactions on the first load.
const skipFirstTransactionsChange = useRef(!hasOnceLoadedReportActions);

const newTransactions = useMemo(() => {
if (transactions === undefined || prevTransactions === undefined || transactions.length <= prevTransactions.length) {
return CONST.EMPTY_ARRAY as unknown as Transaction[];
}
if (skipFirstTransactionsChange.current) {
skipFirstTransactionsChange.current = false;
return CONST.EMPTY_ARRAY as unknown as Transaction[];
}
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
}, [transactions]);

// In case when we have loaded the report, but there were no transactions in it, then we need to explicitly set skipFirstTransactionsChange to false, as it will be not set in the useMemo above.
useEffect(() => {
if (!hasOnceLoadedReportActions) {
return;
}
// This is needed to ensure that set we skipFirstTransactionsChange to false only after the Onyx merge is done.
new Promise<void>((resolve) => {
resolve();
}).then(() => {
requestAnimationFrame(() => {
skipFirstTransactionsChange.current = false;
});
});
}, [hasOnceLoadedReportActions]);

return newTransactions;
}

export default useNewTransactions;
13 changes: 2 additions & 11 deletions src/pages/home/ReportScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import useDeepCompareRef from '@hooks/useDeepCompareRef';
import useIsReportReadyToDisplay from '@hooks/useIsReportReadyToDisplay';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import useNewTransactions from '@hooks/useNewTransactions';
import useOnyx from '@hooks/useOnyx';
import usePaginatedReportActions from '@hooks/usePaginatedReportActions';
import usePermissions from '@hooks/usePermissions';
Expand Down Expand Up @@ -314,17 +315,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) {
// 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 shouldWaitForTransactions = shouldWaitForTransactionsUtil(report, reportTransactions, reportMetadata);

const prevTransactions = usePrevious(reportTransactions);

const newTransactions = useMemo(() => {
if (!reportTransactions || !prevTransactions || reportTransactions.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 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]);
const newTransactions = useNewTransactions(reportMetadata?.hasOnceLoadedReportActions, reportTransactions);

useEffect(() => {
if (!prevIsFocused || isFocused) {
Expand Down
Loading