Skip to content

Commit 5f9df46

Browse files
authored
Merge pull request Expensify#87242 from samranahm/79929/IOURequestStepScan-phase-6-v2
refactor: `IOURequestStepScan` clean-up, phase 6: move multi-scan state to hook and use `key` for reset (v2)
2 parents 776ef1a + 876fb93 commit 5f9df46

10 files changed

Lines changed: 99 additions & 96 deletions

File tree

src/pages/iou/request/IOURequestStartPage.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ function IOURequestStartPage({
9898
});
9999

100100
const [lastSelectedDistanceRates] = useOnyx(ONYXKEYS.NVP_LAST_SELECTED_DISTANCE_RATES);
101-
const [isMultiScanEnabled, setIsMultiScanEnabled] = useState(false);
102101
const [currentDate] = useOnyx(ONYXKEYS.CURRENT_DATE);
103102
const {isOffline} = useNetwork();
104103
const [hasUserSubmittedExpenseOrScannedReceipt] = useOnyx(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, {selector: isTestReceiptTooltipDismissedSelector});
@@ -188,7 +187,6 @@ function IOURequestStartPage({
188187
if (transaction?.iouRequestType === newIOUType) {
189188
return;
190189
}
191-
setIsMultiScanEnabled(false);
192190
initMoneyRequest({
193191
reportID,
194192
policy,
@@ -347,14 +345,12 @@ function IOURequestStartPage({
347345
{() => (
348346
<TabScreenWithFocusTrapWrapper>
349347
<IOURequestStepScan
348+
key={transactionRequestType}
350349
route={route}
351350
navigation={navigation}
352351
onLayout={(setTestReceiptAndNavigate) => {
353352
setTestReceiptAndNavigateRef.current = setTestReceiptAndNavigate;
354353
}}
355-
isMultiScanEnabled={isMultiScanEnabled}
356-
setIsMultiScanEnabled={setIsMultiScanEnabled}
357-
isStartingScan
358354
/>
359355
</TabScreenWithFocusTrapWrapper>
360356
)}

src/pages/iou/request/step/IOURequestStepScan/components/MobileWebCameraView.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@ type MobileWebCameraViewProps = {
4141
iouType: IOUType;
4242
currentUserPersonalDetails: CurrentUserPersonalDetails;
4343
reportID: string;
44-
isMultiScanEnabled?: boolean;
45-
isStartingScan?: boolean;
44+
isMultiScanEnabled: boolean;
45+
isStartingScan: boolean;
4646
updateScanAndNavigate: (file: FileObject, source: string) => void;
47-
setIsMultiScanEnabled?: (value: boolean) => void;
47+
setIsMultiScanEnabled: (value: boolean) => void;
4848
PDFValidationComponent: React.ReactNode;
4949
shouldAcceptMultipleFiles: boolean;
5050
receiptFiles: ReceiptFile[];
@@ -54,8 +54,8 @@ type MobileWebCameraViewProps = {
5454
navigateToConfirmationStep: (files: ReceiptFile[], locationPermissionGranted?: boolean, isTestTransaction?: boolean) => void;
5555
shouldSkipConfirmation: boolean;
5656
setStartLocationPermissionFlow: (value: boolean) => void;
57-
onLayout?: () => void;
5857
onBackButtonPress: () => void;
58+
onLayout?: () => void;
5959
shouldShowWrapper: boolean;
6060
};
6161

@@ -65,7 +65,7 @@ function MobileWebCameraView({
6565
iouType,
6666
currentUserPersonalDetails,
6767
reportID,
68-
isMultiScanEnabled = false,
68+
isMultiScanEnabled,
6969
isStartingScan,
7070
updateScanAndNavigate,
7171
setIsMultiScanEnabled,
@@ -78,8 +78,8 @@ function MobileWebCameraView({
7878
navigateToConfirmationStep,
7979
shouldSkipConfirmation,
8080
setStartLocationPermissionFlow,
81-
onLayout,
8281
onBackButtonPress,
82+
onLayout,
8383
shouldShowWrapper,
8484
}: MobileWebCameraViewProps) {
8585
const {blinkStyle, canUseMultiScan, shouldShowMultiScanEducationalPopup, showBlink, toggleMultiScan, dismissMultiScanEducationalPopup, submitReceipts, submitMultiScanReceipts} =
@@ -93,6 +93,7 @@ function MobileWebCameraView({
9393
shouldSkipConfirmation,
9494
setStartLocationPermissionFlow,
9595
setIsMultiScanEnabled,
96+
setReceiptFiles,
9697
});
9798
const theme = useTheme();
9899
const styles = useThemeStyles();

src/pages/iou/request/step/IOURequestStepScan/hooks/useMobileReceiptScan.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,14 @@ import {validTransactionDraftIDsSelector} from '@src/selectors/TransactionDraft'
1818
function useMobileReceiptScan({
1919
initialTransaction,
2020
iouType,
21-
isMultiScanEnabled = false,
22-
isStartingScan = false,
21+
isMultiScanEnabled,
22+
isStartingScan,
2323
receiptFiles,
2424
navigateToConfirmationStep,
2525
shouldSkipConfirmation,
2626
setStartLocationPermissionFlow,
2727
setIsMultiScanEnabled,
28+
setReceiptFiles,
2829
}: UseMobileReceiptScanParams) {
2930
const [shouldStartLocationPermissionFlow] = useOnyx(ONYXKEYS.NVP_LAST_LOCATION_PERMISSION_PROMPT, {
3031
selector: shouldStartLocationPermissionFlowSelector,
@@ -78,7 +79,10 @@ function useMobileReceiptScan({
7879
}
7980
removeTransactionReceipt(CONST.IOU.OPTIMISTIC_TRANSACTION_ID);
8081
removeDraftTransactionsByIDs(draftTransactionIDs, true);
81-
setIsMultiScanEnabled?.(!isMultiScanEnabled);
82+
if (isMultiScanEnabled) {
83+
setReceiptFiles([]);
84+
}
85+
setIsMultiScanEnabled(!isMultiScanEnabled);
8286
}
8387

8488
function dismissMultiScanEducationalPopup() {

src/pages/iou/request/step/IOURequestStepScan/hooks/useReceiptScan.ts

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import shouldStartLocationPermissionFlowSelector from '@selectors/LocationPermission';
22
import {hasSeenTourSelector} from '@selectors/Onboarding';
3-
import {useEffect, useMemo, useState} from 'react';
3+
import {useMemo, useState} from 'react';
44
import TestReceipt from '@assets/images/fake-receipt.png';
55
import useDefaultExpensePolicy from '@hooks/useDefaultExpensePolicy';
66
import useFilesValidation from '@hooks/useFilesValidation';
@@ -24,6 +24,7 @@ import {setMoneyRequestReceipt} from '@userActions/IOU/Receipt';
2424
import {buildOptimisticTransactionAndCreateDraft, removeDraftTransactionsByIDs} from '@userActions/TransactionEdit';
2525
import CONST from '@src/CONST';
2626
import ONYXKEYS from '@src/ONYXKEYS';
27+
import SCREENS from '@src/SCREENS';
2728
import {validTransactionDraftsSelector} from '@src/selectors/TransactionDraft';
2829
import type Transaction from '@src/types/onyx/Transaction';
2930
import type {FileObject} from '@src/types/utils/Attachment';
@@ -38,8 +39,7 @@ function useReceiptScan({
3839
currentUserPersonalDetails,
3940
backTo,
4041
backToReport,
41-
isMultiScanEnabled = false,
42-
isStartingScan = false,
42+
routeName,
4343
updateScanAndNavigate,
4444
getSource,
4545
}: UseReceiptScanParams) {
@@ -70,6 +70,8 @@ function useReceiptScan({
7070
const [userBillingGracePeriodEnds] = useOnyx(ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_USER_BILLING_GRACE_PERIOD_END);
7171
const [ownerBillingGracePeriodEnd] = useOnyx(ONYXKEYS.NVP_PRIVATE_OWNER_BILLING_GRACE_PERIOD_END);
7272
const draftTransactionIDs = Object.keys(allTransactionDrafts ?? {});
73+
const [isMultiScanEnabled, setIsMultiScanEnabled] = useState(false);
74+
const isStartingScan = routeName === SCREENS.MONEY_REQUEST.CREATE;
7375

7476
const isEditing = action === CONST.IOU.ACTION.EDIT;
7577
const isReplacingReceipt = (isEditing && hasReceipt(initialTransaction)) || (!!initialTransaction?.receipt && !!backTo);
@@ -90,15 +92,6 @@ function useReceiptScan({
9092
const [startLocationPermissionFlow, setStartLocationPermissionFlow] = useState(false);
9193
const [receiptFiles, setReceiptFiles] = useState<ReceiptFile[]>([]);
9294

93-
// Clear receipt files when multi-scan is disabled
94-
useEffect(() => {
95-
if (isMultiScanEnabled) {
96-
return;
97-
}
98-
// eslint-disable-next-line react-hooks/set-state-in-effect
99-
setReceiptFiles([]);
100-
}, [isMultiScanEnabled]);
101-
10295
const [recentWaypoints] = useOnyx(ONYXKEYS.NVP_RECENT_WAYPOINTS);
10396
const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID);
10497

@@ -233,6 +226,9 @@ function useReceiptScan({
233226
});
234227
return {
235228
transactions,
229+
isMultiScanEnabled,
230+
setIsMultiScanEnabled,
231+
isStartingScan,
236232
isEditing,
237233
isReplacingReceipt,
238234
shouldAcceptMultipleFiles,

src/pages/iou/request/step/IOURequestStepScan/index.native.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,12 @@ import type IOURequestStepScanProps from './types';
5757
function IOURequestStepScan({
5858
report,
5959
route: {
60+
name: routeName,
6061
params: {action, iouType, reportID, transactionID: initialTransactionID, backTo, backToReport},
6162
},
6263
transaction: initialTransaction,
6364
currentUserPersonalDetails,
6465
onLayout,
65-
isMultiScanEnabled = false,
66-
isStartingScan = false,
67-
setIsMultiScanEnabled,
6866
}: IOURequestStepScanProps) {
6967
const theme = useTheme();
7068
const styles = useThemeStyles();
@@ -224,6 +222,9 @@ function IOURequestStepScan({
224222

225223
const {
226224
isEditing,
225+
isMultiScanEnabled,
226+
setIsMultiScanEnabled,
227+
isStartingScan,
227228
shouldAcceptMultipleFiles,
228229
shouldSkipConfirmation,
229230
startLocationPermissionFlow,
@@ -245,8 +246,7 @@ function IOURequestStepScan({
245246
currentUserPersonalDetails,
246247
backTo,
247248
backToReport,
248-
isMultiScanEnabled,
249-
isStartingScan,
249+
routeName,
250250
updateScanAndNavigate,
251251
getSource,
252252
});
@@ -262,6 +262,7 @@ function IOURequestStepScan({
262262
shouldSkipConfirmation,
263263
setStartLocationPermissionFlow,
264264
setIsMultiScanEnabled,
265+
setReceiptFiles,
265266
});
266267

267268
const maybeCancelShutterSpan = useCallback(() => {

src/pages/iou/request/step/IOURequestStepScan/index.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,12 @@ import type IOURequestStepScanProps from './types';
2828
function IOURequestStepScan({
2929
report,
3030
route: {
31+
name: routeName,
3132
params: {action, iouType, reportID, transactionID: initialTransactionID, backTo, backToReport},
3233
},
3334
transaction: initialTransaction,
3435
currentUserPersonalDetails,
3536
onLayout,
36-
isMultiScanEnabled = false,
37-
isStartingScan = false,
38-
setIsMultiScanEnabled,
3937
}: Omit<IOURequestStepScanProps, 'user'>) {
4038
const isMobileWeb = isMobile();
4139
const policy = usePolicy(report?.policyID);
@@ -63,6 +61,9 @@ function IOURequestStepScan({
6361

6462
const {
6563
transactions,
64+
isMultiScanEnabled,
65+
setIsMultiScanEnabled,
66+
isStartingScan,
6667
isEditing,
6768
isReplacingReceipt,
6869
shouldAcceptMultipleFiles,
@@ -86,8 +87,7 @@ function IOURequestStepScan({
8687
currentUserPersonalDetails,
8788
backTo,
8889
backToReport,
89-
isMultiScanEnabled,
90-
isStartingScan,
90+
routeName,
9191
updateScanAndNavigate,
9292
getSource,
9393
});
@@ -128,7 +128,7 @@ function IOURequestStepScan({
128128
if (isAllScanFilesCanBeRead) {
129129
return;
130130
}
131-
setIsMultiScanEnabled?.(false);
131+
setIsMultiScanEnabled(false);
132132
removeTransactionReceipt(CONST.IOU.OPTIMISTIC_TRANSACTION_ID);
133133
removeDraftTransactionsByIDs(draftTransactionIDs, true);
134134
});

src/pages/iou/request/step/IOURequestStepScan/types.ts

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,8 @@ type UseReceiptScanParams = {
3838
/** Report ID to navigate back to */
3939
backToReport: string | undefined;
4040

41-
/** Whether multi-scan is enabled */
42-
isMultiScanEnabled: boolean | undefined;
43-
44-
/** Whether the user is starting a scan request */
45-
isStartingScan: boolean | undefined;
41+
/** The route name to determine if scan is starting */
42+
routeName: IOURequestStepScanProps['route']['name'];
4643

4744
/** Callback to replace receipt and navigate back when editing */
4845
updateScanAndNavigate: (file: FileObject, source: string) => void;
@@ -59,10 +56,10 @@ type UseMobileReceiptScanParams = {
5956
iouType: IOUType;
6057

6158
/** Whether multi-scan is enabled */
62-
isMultiScanEnabled?: boolean;
59+
isMultiScanEnabled: boolean;
6360

6461
/** Whether the user is starting a scan request */
65-
isStartingScan?: boolean;
62+
isStartingScan: boolean;
6663

6764
/** The current receipt files being scanned */
6865
receiptFiles: ReceiptFile[];
@@ -76,8 +73,11 @@ type UseMobileReceiptScanParams = {
7673
/** Callback to start the location permission flow */
7774
setStartLocationPermissionFlow: (value: boolean) => void;
7875

79-
/** Callback to update multi-scan enabled state in parent */
80-
setIsMultiScanEnabled: ((value: boolean) => void) | undefined;
76+
/** Callback to update multi-scan enabled state */
77+
setIsMultiScanEnabled: (value: boolean) => void;
78+
79+
/** Callback to update scanned receipt files */
80+
setReceiptFiles: (value: ReceiptFile[]) => void;
8181
};
8282

8383
type IOURequestStepScanProps = WithCurrentUserPersonalDetailsProps &
@@ -90,15 +90,6 @@ type IOURequestStepScanProps = WithCurrentUserPersonalDetailsProps &
9090
* Receives a function (`setTestReceiptAndNavigate`) as an argument,
9191
*/
9292
onLayout?: (setTestReceiptAndNavigate: () => void) => void;
93-
94-
/** If the receipts preview should be shown */
95-
isMultiScanEnabled?: boolean;
96-
97-
/** Updates isMultiScanEnabled flag */
98-
setIsMultiScanEnabled?: (value: boolean) => void;
99-
100-
/** Indicates whether users start to create scan request */
101-
isStartingScan?: boolean;
10293
};
10394

10495
type ReceiptFile = {

tests/ui/IOURequestStepScanTest.tsx

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {NavigationContainer} from '@react-navigation/native';
2-
import {act, render} from '@testing-library/react-native';
2+
import {act, fireEvent, render, screen} from '@testing-library/react-native';
33
import React from 'react';
44
import Onyx from 'react-native-onyx';
55
import type {OnyxEntry} from 'react-native-onyx';
@@ -172,17 +172,6 @@ describe('IOURequestStepScan', () => {
172172
const POLICY_ID = 'policy-1';
173173
const TRANSACTION_ID_1 = '101';
174174

175-
const transaction1 = createRandomTransaction(1);
176-
transaction1.reportID = REPORT_ID;
177-
transaction1.transactionID = TRANSACTION_ID_1;
178-
transaction1.receipt = {source: 'file://first-receipt.png', state: CONST.IOU.RECEIPT_STATE.OPEN};
179-
180-
await act(async () => {
181-
await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, createMinimalReport(REPORT_ID, POLICY_ID));
182-
await Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${TRANSACTION_ID_1}`, transaction1);
183-
});
184-
await waitForBatchedUpdates();
185-
186175
render(
187176
<OnyxListItemProvider>
188177
<LocaleContextProvider>
@@ -191,27 +180,36 @@ describe('IOURequestStepScan', () => {
191180
route={
192181
{
193182
key: 'StepScan2',
194-
name: SCREENS.MONEY_REQUEST.STEP_SCAN,
183+
name: SCREENS.MONEY_REQUEST.CREATE,
195184
params: {
196185
action: CONST.IOU.ACTION.CREATE,
197186
iouType: CONST.IOU.TYPE.SUBMIT,
198187
reportID: REPORT_ID,
199188
transactionID: TRANSACTION_ID_1,
200189
pageIndex: 0,
201190
},
202-
} as unknown as PlatformStackScreenProps<MoneyRequestNavigatorParamList, typeof SCREENS.MONEY_REQUEST.STEP_SCAN>['route']
191+
} as unknown as PlatformStackScreenProps<MoneyRequestNavigatorParamList, typeof SCREENS.MONEY_REQUEST.CREATE>['route']
203192
}
204193
navigation={{} as never}
205-
isMultiScanEnabled
206-
isStartingScan
207-
setIsMultiScanEnabled={jest.fn()}
208194
/>
209195
</NavigationContainer>
210196
</LocaleContextProvider>
211197
</OnyxListItemProvider>,
212198
);
213199

214200
await waitForBatchedUpdatesWithAct();
201+
fireEvent.press(screen.getByLabelText('multi-scan'));
202+
await waitForBatchedUpdates();
203+
const transaction1 = createRandomTransaction(1);
204+
transaction1.reportID = REPORT_ID;
205+
transaction1.transactionID = TRANSACTION_ID_1;
206+
transaction1.receipt = {source: 'file://first-receipt.png', state: CONST.IOU.RECEIPT_STATE.OPEN};
207+
208+
await act(async () => {
209+
await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, createMinimalReport(REPORT_ID, POLICY_ID));
210+
await Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${TRANSACTION_ID_1}`, transaction1);
211+
});
212+
await waitForBatchedUpdates();
215213

216214
expect(triggerFileSelection).not.toBeNull();
217215

0 commit comments

Comments
 (0)