Skip to content

Commit 6782a29

Browse files
authored
Merge pull request Expensify#67205 from allgandalf/fixSearch.ts
Refactor function in Search.ts to remove usage of Onyx.Connect
2 parents 4121f27 + 76f843e commit 6782a29

5 files changed

Lines changed: 111 additions & 36 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
"test:debug": "TZ=utc NODE_OPTIONS='--inspect-brk --experimental-vm-modules' jest --runInBand",
4747
"perf-test": "NODE_OPTIONS=--experimental-vm-modules npx reassure",
4848
"typecheck": "NODE_OPTIONS=--max_old_space_size=8192 tsc",
49-
"lint": "NODE_OPTIONS=--max_old_space_size=8192 eslint . --max-warnings=329 --cache --cache-location=node_modules/.cache/eslint",
49+
"lint": "NODE_OPTIONS=--max_old_space_size=8192 eslint . --max-warnings=327 --cache --cache-location=node_modules/.cache/eslint",
5050
"lint-changed": "NODE_OPTIONS=--max_old_space_size=8192 ./scripts/lintChanged.sh",
5151
"lint-watch": "npx eslint-watch --watch --changed",
5252
"shellcheck": "./scripts/shellCheck.sh",

src/components/SelectionList/Search/ReportListItemHeader.tsx

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, {useMemo} from 'react';
22
import {View} from 'react-native';
33
import type {ColorValue} from 'react-native';
44
import type {OnyxEntry} from 'react-native-onyx';
@@ -7,14 +7,17 @@ import ReportSearchHeader from '@components/ReportSearchHeader';
77
import {useSearchContext} from '@components/Search/SearchContext';
88
import type {ListItem, TransactionReportGroupListItemType} from '@components/SelectionList/types';
99
import TextWithTooltip from '@components/TextWithTooltip';
10+
import useOnyx from '@hooks/useOnyx';
1011
import useResponsiveLayout from '@hooks/useResponsiveLayout';
1112
import useStyleUtils from '@hooks/useStyleUtils';
1213
import useTheme from '@hooks/useTheme';
1314
import useThemeStyles from '@hooks/useThemeStyles';
1415
import {convertToDisplayString} from '@libs/CurrencyUtils';
1516
import {handleActionButtonPress} from '@userActions/Search';
1617
import CONST from '@src/CONST';
18+
import ONYXKEYS from '@src/ONYXKEYS';
1719
import type * as OnyxTypes from '@src/types/onyx';
20+
import type {SearchPolicy, SearchReport} from '@src/types/onyx/SearchResults';
1821
import ActionCell from './ActionCell';
1922
import UserInfoAndActionButtonRow from './UserInfoAndActionButtonRow';
2023

@@ -169,12 +172,28 @@ function ReportListItemHeader<TItem extends ListItem>({
169172
const {isLargeScreenWidth, shouldUseNarrowLayout} = useResponsiveLayout();
170173
const thereIsFromAndTo = !!reportItem?.from && !!reportItem?.to;
171174
const showUserInfo = (reportItem.type === CONST.REPORT.TYPE.IOU && thereIsFromAndTo) || (reportItem.type === CONST.REPORT.TYPE.EXPENSE && !!reportItem?.from);
172-
175+
const [snapshot] = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${currentSearchHash}`, {canBeMissing: true});
176+
const snapshotReport = useMemo(() => {
177+
return (snapshot?.data?.[`${ONYXKEYS.COLLECTION.REPORT}${reportItem.reportID}`] ?? {}) as SearchReport;
178+
}, [snapshot, reportItem.reportID]);
179+
const snapshotPolicy = useMemo(() => {
180+
return (snapshot?.data?.[`${ONYXKEYS.COLLECTION.POLICY}${reportItem.policyID}`] ?? {}) as SearchPolicy;
181+
}, [snapshot, reportItem.policyID]);
182+
const [lastPaymentMethod] = useOnyx(`${ONYXKEYS.NVP_LAST_PAYMENT_METHOD}`, {canBeMissing: true});
173183
const avatarBorderColor =
174184
StyleUtils.getItemBackgroundColorStyle(!!reportItem.isSelected, !!isFocused, !!isDisabled, theme.activeComponentBG, theme.hoverComponentBG)?.backgroundColor ?? theme.highlightBG;
175185

176186
const handleOnButtonPress = () => {
177-
handleActionButtonPress(currentSearchHash, reportItem, () => onSelectRow(reportItem as unknown as TItem), shouldUseNarrowLayout && !!canSelectMultiple, currentSearchKey);
187+
handleActionButtonPress(
188+
currentSearchHash,
189+
reportItem,
190+
() => onSelectRow(reportItem as unknown as TItem),
191+
shouldUseNarrowLayout && !!canSelectMultiple,
192+
snapshotReport,
193+
snapshotPolicy,
194+
lastPaymentMethod,
195+
currentSearchKey,
196+
);
178197
};
179198
return !isLargeScreenWidth ? (
180199
<View>

src/components/SelectionList/Search/TransactionListItem.tsx

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {useSearchContext} from '@components/Search/SearchContext';
88
import type {ListItem, TransactionListItemProps, TransactionListItemType} from '@components/SelectionList/types';
99
import TransactionItemRow from '@components/TransactionItemRow';
1010
import useAnimatedHighlightStyle from '@hooks/useAnimatedHighlightStyle';
11+
import useOnyx from '@hooks/useOnyx';
1112
import useResponsiveLayout from '@hooks/useResponsiveLayout';
1213
import useStyleUtils from '@hooks/useStyleUtils';
1314
import useSyncFocus from '@hooks/useSyncFocus';
@@ -16,6 +17,8 @@ import useThemeStyles from '@hooks/useThemeStyles';
1617
import {handleActionButtonPress as handleActionButtonPressUtil} from '@libs/actions/Search';
1718
import variables from '@styles/variables';
1819
import CONST from '@src/CONST';
20+
import ONYXKEYS from '@src/ONYXKEYS';
21+
import type {SearchPolicy, SearchReport} from '@src/types/onyx/SearchResults';
1922
import UserInfoAndActionButtonRow from './UserInfoAndActionButtonRow';
2023

2124
function TransactionListItem<TItem extends ListItem>({
@@ -38,6 +41,15 @@ function TransactionListItem<TItem extends ListItem>({
3841

3942
const {isLargeScreenWidth, shouldUseNarrowLayout} = useResponsiveLayout();
4043
const {currentSearchHash, currentSearchKey} = useSearchContext();
44+
const [snapshot] = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${currentSearchHash}`, {canBeMissing: true});
45+
const snapshotReport = useMemo(() => {
46+
return (snapshot?.data?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionItem.reportID}`] ?? {}) as SearchReport;
47+
}, [snapshot, transactionItem.reportID]);
48+
49+
const snapshotPolicy = useMemo(() => {
50+
return (snapshot?.data?.[`${ONYXKEYS.COLLECTION.POLICY}${transactionItem.policyID}`] ?? {}) as SearchPolicy;
51+
}, [snapshot, transactionItem.policyID]);
52+
const [lastPaymentMethod] = useOnyx(`${ONYXKEYS.NVP_LAST_PAYMENT_METHOD}`, {canBeMissing: true});
4153

4254
const pressableStyle = [
4355
styles.transactionListItemStyle,
@@ -62,8 +74,17 @@ function TransactionListItem<TItem extends ListItem>({
6274
}, [transactionItem]);
6375

6476
const handleActionButtonPress = useCallback(() => {
65-
handleActionButtonPressUtil(currentSearchHash, transactionItem, () => onSelectRow(item), shouldUseNarrowLayout && !!canSelectMultiple, currentSearchKey);
66-
}, [canSelectMultiple, currentSearchHash, currentSearchKey, item, onSelectRow, shouldUseNarrowLayout, transactionItem]);
77+
handleActionButtonPressUtil(
78+
currentSearchHash,
79+
transactionItem,
80+
() => onSelectRow(item),
81+
shouldUseNarrowLayout && !!canSelectMultiple,
82+
snapshotReport,
83+
snapshotPolicy,
84+
lastPaymentMethod,
85+
currentSearchKey,
86+
);
87+
}, [currentSearchHash, transactionItem, shouldUseNarrowLayout, canSelectMultiple, snapshotReport, snapshotPolicy, lastPaymentMethod, currentSearchKey, onSelectRow, item]);
6788

6889
const handleCheckboxPress = useCallback(() => {
6990
onCheckboxPress?.(item);

src/libs/actions/Search.ts

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import Onyx from 'react-native-onyx';
2-
import type {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx';
2+
import type {OnyxEntry, OnyxUpdate} from 'react-native-onyx';
33
import type {ValueOf} from 'type-fest';
44
import type {FormOnyxValues} from '@components/Form/types';
55
import type {PaymentData, SearchQueryJSON} from '@components/Search/types';
@@ -22,33 +22,19 @@ import CONST from '@src/CONST';
2222
import ONYXKEYS from '@src/ONYXKEYS';
2323
import {FILTER_KEYS} from '@src/types/form/SearchAdvancedFiltersForm';
2424
import type {SearchAdvancedFiltersForm} from '@src/types/form/SearchAdvancedFiltersForm';
25-
import type {LastPaymentMethod, LastPaymentMethodType, Policy, SearchResults} from '@src/types/onyx';
25+
import type {LastPaymentMethod, LastPaymentMethodType, Policy} from '@src/types/onyx';
2626
import type {ConnectionName} from '@src/types/onyx/Policy';
2727
import type {SearchPolicy, SearchReport, SearchTransaction} from '@src/types/onyx/SearchResults';
2828
import type Nullable from '@src/types/utils/Nullable';
2929

30-
let lastPaymentMethod: OnyxEntry<LastPaymentMethod>;
31-
Onyx.connect({
32-
key: ONYXKEYS.NVP_LAST_PAYMENT_METHOD,
33-
callback: (val) => {
34-
lastPaymentMethod = val;
35-
},
36-
});
37-
38-
let allSnapshots: OnyxCollection<SearchResults>;
39-
Onyx.connect({
40-
key: ONYXKEYS.COLLECTION.SNAPSHOT,
41-
callback: (val) => {
42-
allSnapshots = val;
43-
},
44-
waitForCollectionCallback: true,
45-
});
46-
4730
function handleActionButtonPress(
4831
hash: number,
4932
item: TransactionListItemType | TransactionReportGroupListItemType,
5033
goToItem: () => void,
5134
isInMobileSelectionMode: boolean,
35+
snapshotReport: SearchReport,
36+
snapshotPolicy: SearchPolicy,
37+
lastPaymentMethod: OnyxEntry<LastPaymentMethod>,
5238
currentSearchKey?: SearchKey,
5339
) {
5440
// The transactionIDList is needed to handle actions taken on `status:""` where transactions on single expense reports can be approved/paid.
@@ -64,22 +50,21 @@ function handleActionButtonPress(
6450

6551
switch (item.action) {
6652
case CONST.SEARCH.ACTION_TYPES.PAY:
67-
getPayActionCallback(hash, item, goToItem, currentSearchKey);
53+
getPayActionCallback(hash, item, goToItem, snapshotReport, snapshotPolicy, lastPaymentMethod, currentSearchKey);
6854
return;
6955
case CONST.SEARCH.ACTION_TYPES.APPROVE:
7056
approveMoneyRequestOnSearch(hash, [item.reportID], transactionID, currentSearchKey);
7157
return;
7258
case CONST.SEARCH.ACTION_TYPES.SUBMIT: {
73-
const policy = (allSnapshots?.[`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`]?.data?.[`${ONYXKEYS.COLLECTION.POLICY}${item.policyID}`] ?? {}) as SearchPolicy;
74-
submitMoneyRequestOnSearch(hash, [item], [policy], transactionID, currentSearchKey);
59+
submitMoneyRequestOnSearch(hash, [item], [snapshotPolicy], transactionID, currentSearchKey);
7560
return;
7661
}
7762
case CONST.SEARCH.ACTION_TYPES.EXPORT_TO_ACCOUNTING: {
7863
if (!item) {
7964
return;
8065
}
8166

82-
const policy = (allSnapshots?.[`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`]?.data?.[`${ONYXKEYS.COLLECTION.POLICY}${item.policyID}`] ?? {}) as Policy;
67+
const policy = (snapshotPolicy ?? {}) as Policy;
8368
const connectedIntegration = getValidConnectedIntegration(policy);
8469

8570
if (!connectedIntegration) {
@@ -108,24 +93,31 @@ function getLastPolicyPaymentMethod(policyID: string | undefined, lastPaymentMet
10893
return lastPolicyPaymentMethod;
10994
}
11095

111-
function getPayActionCallback(hash: number, item: TransactionListItemType | TransactionReportGroupListItemType, goToItem: () => void, currentSearchKey?: SearchKey) {
96+
function getPayActionCallback(
97+
hash: number,
98+
item: TransactionListItemType | TransactionReportGroupListItemType,
99+
goToItem: () => void,
100+
snapshotReport: SearchReport,
101+
snapshotPolicy: SearchPolicy,
102+
lastPaymentMethod: OnyxEntry<LastPaymentMethod>,
103+
currentSearchKey?: SearchKey,
104+
) {
112105
const lastPolicyPaymentMethod = getLastPolicyPaymentMethod(item.policyID, lastPaymentMethod);
113106

114107
if (!lastPolicyPaymentMethod) {
115108
goToItem();
116109
return;
117110
}
118111

119-
const report = (allSnapshots?.[`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`]?.data?.[`${ONYXKEYS.COLLECTION.REPORT}${item.reportID}`] ?? {}) as SearchReport;
120-
const amount = Math.abs((report?.total ?? 0) - (report?.nonReimbursableTotal ?? 0));
112+
const amount = Math.abs((snapshotReport?.total ?? 0) - (snapshotReport?.nonReimbursableTotal ?? 0));
121113
const transactionID = isTransactionListItemType(item) ? [item.transactionID] : undefined;
122114

123115
if (lastPolicyPaymentMethod === CONST.IOU.PAYMENT_TYPE.ELSEWHERE) {
124116
payMoneyRequestOnSearch(hash, [{reportID: item.reportID, amount, paymentType: lastPolicyPaymentMethod}], transactionID, currentSearchKey);
125117
return;
126118
}
127119

128-
const hasVBBA = !!allSnapshots?.[`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`]?.data?.[`${ONYXKEYS.COLLECTION.POLICY}${item.policyID}`]?.achAccount?.bankAccountID;
120+
const hasVBBA = !!snapshotPolicy?.achAccount?.bankAccountID;
129121
if (hasVBBA) {
130122
payMoneyRequestOnSearch(hash, [{reportID: item.reportID, amount, paymentType: lastPolicyPaymentMethod}], transactionID, currentSearchKey);
131123
return;

tests/unit/Search/handleActionButtonPressTest.ts

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
/* eslint-disable @typescript-eslint/naming-convention */
2+
import type {OnyxEntry} from 'react-native-onyx';
3+
import Onyx from 'react-native-onyx';
24
import type {TransactionReportGroupListItemType} from '@components/SelectionList/types';
35
import {handleActionButtonPress} from '@libs/actions/Search';
6+
import ONYXKEYS from '@src/ONYXKEYS';
7+
import type {LastPaymentMethod, SearchResults} from '@src/types/onyx';
48

59
jest.mock('@src/components/ConfirmedRoute.tsx');
610

@@ -211,23 +215,62 @@ const updatedMockReportItem = {
211215
}),
212216
};
213217

218+
const mockSnapshotForItem: OnyxEntry<SearchResults> = {
219+
// @ts-expect-error: Allow partial record in snapshot update for testing
220+
data: {
221+
[`${ONYXKEYS.COLLECTION.POLICY}${mockReportItemWithHold?.policyID}`]: {
222+
...(mockReportItemWithHold.policyID
223+
? {
224+
[String(mockReportItemWithHold.policyID)]: {
225+
type: 'policy',
226+
id: String(mockReportItemWithHold.policyID),
227+
role: 'admin',
228+
owner: 'apb@apb.com',
229+
...mockReportItemWithHold,
230+
},
231+
}
232+
: {}),
233+
},
234+
},
235+
};
236+
237+
const mockLastPaymentMethod: OnyxEntry<LastPaymentMethod> = {
238+
expense: 'Elsewhere',
239+
lastUsed: 'Elsewhere',
240+
};
241+
214242
describe('handleActionButtonPress', () => {
215243
const searchHash = 1;
244+
beforeAll(() => {
245+
Onyx.merge(
246+
`${ONYXKEYS.COLLECTION.SNAPSHOT}${searchHash}`,
247+
// @ts-expect-error: Allow partial record in snapshot update for testing
248+
mockSnapshotForItem,
249+
);
250+
Onyx.merge(ONYXKEYS.NVP_LAST_PAYMENT_METHOD, mockLastPaymentMethod);
251+
});
252+
253+
const snapshotReport = mockSnapshotForItem?.data?.[`${ONYXKEYS.COLLECTION.REPORT}${mockReportItemWithHold.reportID}`] ?? {};
254+
const snapshotPolicy = mockSnapshotForItem?.data?.[`${ONYXKEYS.COLLECTION.POLICY}${mockReportItemWithHold.policyID}`] ?? {};
255+
216256
test('Should navigate to item when report has one transaction on hold', () => {
217257
const goToItem = jest.fn(() => {});
218-
handleActionButtonPress(searchHash, mockReportItemWithHold, goToItem, false);
258+
// @ts-expect-error: Allow partial record in snapshot update for testing
259+
handleActionButtonPress(searchHash, mockReportItemWithHold, goToItem, false, snapshotReport, snapshotPolicy, mockLastPaymentMethod);
219260
expect(goToItem).toHaveBeenCalledTimes(1);
220261
});
221262

222263
test('Should not navigate to item when the hold is removed', () => {
223264
const goToItem = jest.fn(() => {});
224-
handleActionButtonPress(searchHash, updatedMockReportItem, goToItem, false);
265+
// @ts-expect-error: Allow partial record in snapshot update for testing
266+
handleActionButtonPress(searchHash, updatedMockReportItem, goToItem, false, snapshotReport, snapshotPolicy, mockLastPaymentMethod);
225267
expect(goToItem).toHaveBeenCalledTimes(0);
226268
});
227269

228270
test('Should run goToItem callback when user is in mobile selection mode', () => {
229271
const goToItem = jest.fn(() => {});
230-
handleActionButtonPress(searchHash, updatedMockReportItem, goToItem, true);
272+
// @ts-expect-error: Allow partial record in snapshot update for testing
273+
handleActionButtonPress(searchHash, updatedMockReportItem, goToItem, true, snapshotReport, snapshotPolicy, mockLastPaymentMethod);
231274
expect(goToItem).toHaveBeenCalledTimes(1);
232275
});
233276
});

0 commit comments

Comments
 (0)