Skip to content

Commit d1f6e61

Browse files
Merge branch 'main' into maddylewis-patch-5
2 parents 53c97fe + c0ec500 commit d1f6e61

11 files changed

Lines changed: 395 additions & 77 deletions

File tree

src/components/MoneyReportHeader.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import React, {useCallback, useContext, useEffect, useMemo, useState} from 'reac
33
import {ActivityIndicator, InteractionManager, View} from 'react-native';
44
import type {OnyxEntry} from 'react-native-onyx';
55
import type {ValueOf} from 'type-fest';
6+
import useDuplicateTransactionsAndViolations from '@hooks/useDuplicateTransactionsAndViolations';
67
import useLoadingBarVisibility from '@hooks/useLoadingBarVisibility';
78
import useLocalize from '@hooks/useLocalize';
89
import useMobileSelectionMode from '@hooks/useMobileSelectionMode';
@@ -191,6 +192,7 @@ function MoneyReportHeader({
191192
{canBeMissing: true},
192193
);
193194

195+
const {duplicateTransactions, duplicateTransactionViolations} = useDuplicateTransactionsAndViolations(transactions.map((t) => t.transactionID));
194196
const isExported = useMemo(() => isExportedUtils(reportActions), [reportActions]);
195197
// wrapped in useMemo to improve performance because this is an operation on array
196198
const integrationNameFromExportMessage = useMemo(() => {
@@ -1137,7 +1139,9 @@ function MoneyReportHeader({
11371139
}
11381140
// it's deleting transaction but not the report which leads to bug (that is actually also on staging)
11391141
// Money request should be deleted when interactions are done, to not show the not found page before navigating to goBackRoute
1140-
InteractionManager.runAfterInteractions(() => deleteMoneyRequest(transaction?.transactionID, requestParentReportAction));
1142+
InteractionManager.runAfterInteractions(() =>
1143+
deleteMoneyRequest(transaction?.transactionID, requestParentReportAction, duplicateTransactions, duplicateTransactionViolations),
1144+
);
11411145
goBackRoute = getNavigationUrlOnMoneyRequestDelete(transaction.transactionID, requestParentReportAction, false);
11421146
}
11431147

src/components/MoneyRequestHeader.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import React, {useCallback, useContext, useEffect, useMemo, useState} from 'reac
44
import {View} from 'react-native';
55
import type {OnyxEntry} from 'react-native-onyx';
66
import type {ValueOf} from 'type-fest';
7+
import useDuplicateTransactionsAndViolations from '@hooks/useDuplicateTransactionsAndViolations';
78
import useLoadingBarVisibility from '@hooks/useLoadingBarVisibility';
89
import useLocalize from '@hooks/useLocalize';
910
import useOnyx from '@hooks/useOnyx';
@@ -80,7 +81,7 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre
8081
{canBeMissing: true},
8182
);
8283
const transactionViolations = useTransactionViolations(transaction?.transactionID);
83-
84+
const {duplicateTransactions, duplicateTransactionViolations} = useDuplicateTransactionsAndViolations(transaction?.transactionID ? [transaction.transactionID] : []);
8485
const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false);
8586
const [downloadErrorModalVisible, setDownloadErrorModalVisible] = useState(false);
8687
const [dismissedHoldUseExplanation, dismissedHoldUseExplanationResult] = useOnyx(ONYXKEYS.NVP_DISMISSED_HOLD_USE_EXPLANATION, {canBeMissing: true});
@@ -351,9 +352,9 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre
351352
throw new Error('Data missing');
352353
}
353354
if (isTrackExpenseAction(parentReportAction)) {
354-
deleteTrackExpense(report?.parentReportID, transaction.transactionID, parentReportAction, true);
355+
deleteTrackExpense(report?.parentReportID, transaction.transactionID, parentReportAction, duplicateTransactions, duplicateTransactionViolations, true);
355356
} else {
356-
deleteMoneyRequest(transaction.transactionID, parentReportAction, true);
357+
deleteMoneyRequest(transaction.transactionID, parentReportAction, duplicateTransactions, duplicateTransactionViolations, true);
357358
removeTransaction(transaction.transactionID);
358359
}
359360
onBackButtonPress();
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import {useMemo} from 'react';
2+
import type {OnyxCollection} from 'react-native-onyx';
3+
import CONST from '@src/CONST';
4+
import ONYXKEYS from '@src/ONYXKEYS';
5+
import type {Transaction, TransactionViolations} from '@src/types/onyx';
6+
import useOnyx from './useOnyx';
7+
8+
/**
9+
* Selects violations related to provided transaction IDs and if present, the violations of their duplicates.
10+
* @param transactionIDs - An array of transaction IDs to fetch their violations for.
11+
* @param allTransactionsViolations - A collection of all transaction violations currently in the onyx db.
12+
* @returns - A collection of violations related to the transaction IDs and if present, the violations of their duplicates.
13+
* @private
14+
*/
15+
function selectViolationsWithDuplicates(transactionIDs: string[], allTransactionsViolations: OnyxCollection<TransactionViolations>): OnyxCollection<TransactionViolations> {
16+
if (!allTransactionsViolations || !transactionIDs?.length) {
17+
return {};
18+
}
19+
20+
const result: OnyxCollection<TransactionViolations> = {};
21+
22+
for (const transactionID of transactionIDs) {
23+
const key = `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`;
24+
const transactionViolations = allTransactionsViolations[key];
25+
26+
if (!transactionViolations) {
27+
continue;
28+
}
29+
30+
result[key] = transactionViolations;
31+
32+
transactionViolations
33+
.filter((violations) => violations.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)
34+
.flatMap((violations) => violations?.data?.duplicates ?? [])
35+
.forEach((duplicateID) => {
36+
if (!duplicateID) {
37+
return;
38+
}
39+
40+
const duplicateKey = `${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${duplicateID}`;
41+
const duplicateViolations = allTransactionsViolations[duplicateKey];
42+
43+
if (duplicateViolations) {
44+
result[duplicateKey] = duplicateViolations;
45+
}
46+
});
47+
}
48+
49+
return result;
50+
}
51+
52+
/**
53+
* Selects transactions related to provided transaction IDs and if present, the duplicate transactions.
54+
* @param transactionIDs - An array of transaction IDs to fetch their transactions for.
55+
* @param allTransactions - A collection of all transactions currently in the onyx.
56+
* @param duplicateTransactionViolations - A collection of all duplicate transaction violations currently in the onyx.
57+
* @returns - A collection of transactions related to the transaction IDs and if present, the duplicate transactions.
58+
*/
59+
60+
function selectTransactionsWithDuplicates(
61+
transactionIDs: string[],
62+
allTransactions: OnyxCollection<Transaction>,
63+
duplicateTransactionViolations: OnyxCollection<TransactionViolations>,
64+
): OnyxCollection<Transaction> {
65+
if (!allTransactions) {
66+
return {};
67+
}
68+
69+
const result: OnyxCollection<Transaction> = {};
70+
71+
for (const transactionID of transactionIDs) {
72+
const key = `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`;
73+
const transaction = allTransactions[key];
74+
if (transaction) {
75+
result[key] = transaction;
76+
}
77+
78+
const transactionViolations = duplicateTransactionViolations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`];
79+
80+
if (!transactionViolations) {
81+
continue;
82+
}
83+
84+
transactionViolations
85+
.filter((violations) => violations.name === CONST.VIOLATIONS.DUPLICATED_TRANSACTION)
86+
.flatMap((violations) => violations?.data?.duplicates ?? [])
87+
.forEach((duplicateID) => {
88+
if (!duplicateID) {
89+
return;
90+
}
91+
92+
const duplicateKey = `${ONYXKEYS.COLLECTION.TRANSACTION}${duplicateID}`;
93+
const duplicateTransaction = allTransactions[duplicateKey];
94+
95+
if (duplicateTransaction) {
96+
result[duplicateKey] = duplicateTransaction;
97+
}
98+
});
99+
}
100+
return result;
101+
}
102+
103+
type DuplicateTransactionsAndViolations = {
104+
duplicateTransactions: OnyxCollection<Transaction>;
105+
duplicateTransactionViolations: OnyxCollection<TransactionViolations>;
106+
};
107+
108+
/**
109+
* A hook to fetch transactions, their violations and if present, the duplicate transactions and their violations.
110+
* @param transactionIDs - Array of transaction IDs to check for duplicates.
111+
* @returns - An object containing duplicate transactions and their violations.
112+
*/
113+
function useDuplicateTransactionsAndViolations(transactionIDs: string[]): DuplicateTransactionsAndViolations {
114+
const violationsSelectorMemo = useMemo(() => {
115+
return (allTransactionsViolations: OnyxCollection<TransactionViolations>) => selectViolationsWithDuplicates(transactionIDs, allTransactionsViolations);
116+
}, [transactionIDs]);
117+
118+
const [duplicateTransactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS, {
119+
canBeMissing: true,
120+
selector: violationsSelectorMemo,
121+
});
122+
123+
const transactionSelector = useMemo(() => {
124+
return (allTransactions: OnyxCollection<Transaction>) => selectTransactionsWithDuplicates(transactionIDs, allTransactions, duplicateTransactionViolations);
125+
}, [transactionIDs, duplicateTransactionViolations]);
126+
127+
const [duplicateTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, {
128+
canBeMissing: true,
129+
selector: transactionSelector,
130+
});
131+
132+
return useMemo(
133+
() => ({
134+
duplicateTransactions,
135+
duplicateTransactionViolations,
136+
}),
137+
[duplicateTransactions, duplicateTransactionViolations],
138+
);
139+
}
140+
141+
export default useDuplicateTransactionsAndViolations;

src/hooks/useSelectedTransactionsActions.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import CONST from '@src/CONST';
2121
import ONYXKEYS from '@src/ONYXKEYS';
2222
import ROUTES from '@src/ROUTES';
2323
import type {OriginalMessageIOU, Report, ReportAction, Session, Transaction} from '@src/types/onyx';
24+
import useDuplicateTransactionsAndViolations from './useDuplicateTransactionsAndViolations';
2425
import useLocalize from './useLocalize';
2526
import useOnyx from './useOnyx';
2627
import useReportIsArchived from './useReportIsArchived';
@@ -45,6 +46,7 @@ function useSelectedTransactionsActions({
4546
}) {
4647
const {selectedTransactionIDs, clearSelectedTransactions} = useSearchContext();
4748
const [allTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, {canBeMissing: false});
49+
const {duplicateTransactions, duplicateTransactionViolations} = useDuplicateTransactionsAndViolations(selectedTransactionIDs);
4850
const isReportArchived = useReportIsArchived(report?.reportID);
4951
const selectedTransactions = useMemo(
5052
() =>
@@ -62,6 +64,7 @@ function useSelectedTransactionsActions({
6264
const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false);
6365
const isTrackExpenseThread = isTrackExpenseReport(report);
6466
const isInvoice = isInvoiceReport(report);
67+
6568
let iouType: IOUType = CONST.IOU.TYPE.SUBMIT;
6669

6770
if (isTrackExpenseThread) {
@@ -87,15 +90,15 @@ function useSelectedTransactionsActions({
8790
return;
8891
}
8992

90-
deleteMoneyRequest(transactionID, action, undefined, deletedTransactionIDs);
93+
deleteMoneyRequest(transactionID, action, duplicateTransactions, duplicateTransactionViolations, false, deletedTransactionIDs);
9194
deletedTransactionIDs.push(transactionID);
9295
});
9396
clearSelectedTransactions(true);
9497
if (allTransactionsLength - transactionsWithActions.length <= 1) {
9598
turnOffMobileSelectionMode();
9699
}
97100
setIsDeleteModalVisible(false);
98-
}, [allTransactionsLength, reportActions, selectedTransactionIDs, clearSelectedTransactions]);
101+
}, [duplicateTransactions, duplicateTransactionViolations, allTransactionsLength, reportActions, selectedTransactionIDs, clearSelectedTransactions]);
99102

100103
const showDeleteModal = useCallback(() => {
101104
setIsDeleteModalVisible(true);

0 commit comments

Comments
 (0)