Skip to content

Commit fd58842

Browse files
committed
decompose DecisionModal state into useDecisionModal hook in MoneyReportHeader
1 parent e38712f commit fd58842

4 files changed

Lines changed: 109 additions & 37 deletions

File tree

src/components/DecisionModal.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,5 @@ function DecisionModal({
112112
);
113113
}
114114

115+
export type {DecisionModalProps};
115116
export default DecisionModal;
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import React, {useState} from 'react';
2+
import type {DecisionModalProps} from '@components/DecisionModal';
3+
import DecisionModal from '@components/DecisionModal';
4+
import useResponsiveLayout from '@hooks/useResponsiveLayout';
5+
import {ModalActions} from './ModalContext';
6+
import type {ModalProps} from './ModalContext';
7+
8+
type DecisionModalWrapperProps = ModalProps & Omit<DecisionModalProps, 'onClose' | 'onSecondOptionSubmit' | 'onFirstOptionSubmit' | 'isVisible' | 'isSmallScreenWidth'>;
9+
10+
function DecisionModalWrapper({closeModal, onModalHide, ...props}: DecisionModalWrapperProps) {
11+
const [isVisible, setIsVisible] = useState(true);
12+
const [closeAction, setCloseAction] = useState<typeof ModalActions.CONFIRM | typeof ModalActions.CLOSE>(ModalActions.CLOSE);
13+
// We need to use isSmallScreenWidth here because the DecisionModal breaks in RHP with shouldUseNarrowLayout.
14+
// eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
15+
const {isSmallScreenWidth} = useResponsiveLayout();
16+
17+
const handleFirstOption = () => {
18+
setCloseAction(ModalActions.CONFIRM);
19+
setIsVisible(false);
20+
};
21+
22+
const handleSecondOption = () => {
23+
setCloseAction(ModalActions.CLOSE);
24+
setIsVisible(false);
25+
};
26+
27+
const handleModalHide = () => {
28+
if (isVisible) {
29+
return;
30+
}
31+
closeModal({action: closeAction});
32+
onModalHide?.();
33+
};
34+
35+
return (
36+
<DecisionModal
37+
// eslint-disable-next-line react/jsx-props-no-spreading
38+
{...props}
39+
isVisible={isVisible}
40+
isSmallScreenWidth={isSmallScreenWidth}
41+
onFirstOptionSubmit={handleFirstOption}
42+
onSecondOptionSubmit={handleSecondOption}
43+
onClose={handleSecondOption}
44+
onModalHide={handleModalHide}
45+
/>
46+
);
47+
}
48+
49+
export default DecisionModalWrapper;

src/components/MoneyReportHeader.tsx

Lines changed: 33 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import useConfirmModal from '@hooks/useConfirmModal';
1414
import useConfirmPendingRTERAndProceed from '@hooks/useConfirmPendingRTERAndProceed';
1515
import {useCurrencyListActions} from '@hooks/useCurrencyList';
1616
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
17+
import useDecisionModal from '@hooks/useDecisionModal';
1718
import useDefaultExpensePolicy from '@hooks/useDefaultExpensePolicy';
1819
import useDeleteTransactions from '@hooks/useDeleteTransactions';
1920
import useDuplicateTransactionsAndViolations from '@hooks/useDuplicateTransactionsAndViolations';
@@ -158,7 +159,6 @@ import ActivityIndicator from './ActivityIndicator';
158159
import Button from './Button';
159160
import ButtonWithDropdownMenu from './ButtonWithDropdownMenu';
160161
import type {ButtonWithDropdownMenuRef, DropdownOption} from './ButtonWithDropdownMenu/types';
161-
import DecisionModal from './DecisionModal';
162162
import {useDelegateNoAccessActions, useDelegateNoAccessState} from './DelegateNoAccessModalProvider';
163163
import Header from './Header';
164164
import HeaderLoadingBar from './HeaderLoadingBar';
@@ -363,7 +363,6 @@ function MoneyReportHeader({reportID: reportIDProp, shouldDisplayBackButton = fa
363363
}, [isExported, reportActions]);
364364

365365
const transactionViolations = useTransactionViolations(transaction?.transactionID);
366-
const [downloadErrorModalVisible, setDownloadErrorModalVisible] = useState(false);
367366
const [isPDFModalVisible, setIsPDFModalVisible] = useState(false);
368367
const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED);
369368
const [isTrackIntentUser] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED, {selector: isTrackIntentUserSelector});
@@ -384,6 +383,7 @@ function MoneyReportHeader({reportID: reportIDProp, shouldDisplayBackButton = fa
384383

385384
const [exportModalStatus, setExportModalStatus] = useState<ExportType | null>(null);
386385
const {showConfirmModal} = useConfirmModal();
386+
const {showDecisionModal} = useDecisionModal();
387387
const {isPaidAnimationRunning, isApprovedAnimationRunning, isSubmittingAnimationRunning, startAnimation, stopAnimation, startApprovedAnimation, startSubmittingAnimation} =
388388
usePaymentAnimations();
389389
const styles = useThemeStyles();
@@ -516,7 +516,6 @@ function MoneyReportHeader({reportID: reportIDProp, shouldDisplayBackButton = fa
516516

517517
const [isDuplicateActive, temporarilyDisableDuplicateAction] = useThrottledButtonState(handleDuplicateReset);
518518

519-
const [isDownloadErrorModalVisible, setIsDownloadErrorModalVisible] = useState(false);
520519
const [isHoldEducationalModalVisible, setIsHoldEducationalModalVisible] = useState(false);
521520
const [rejectModalAction, setRejectModalAction] = useState<ValueOf<
522521
typeof CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.HOLD | typeof CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.REJECT | typeof CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.REJECT_BULK
@@ -532,7 +531,6 @@ function MoneyReportHeader({reportID: reportIDProp, shouldDisplayBackButton = fa
532531

533532
const shouldDisplayNarrowMoreButton = !shouldDisplayNarrowVersion || isWideRHPDisplayedOnWideLayout || isSuperWideRHPDisplayedOnWideLayout;
534533

535-
const [offlineModalVisible, setOfflineModalVisible] = useState(false);
536534
const {showNonReimbursablePaymentErrorModal, shouldBlockDirectPayment, nonReimbursablePaymentErrorDecisionModal} = useNonReimbursablePaymentModal(moneyRequestReport, transactions);
537535

538536
const showExportProgressModal = useCallback(() => {
@@ -547,7 +545,11 @@ function MoneyReportHeader({reportID: reportIDProp, shouldDisplayBackButton = fa
547545
const beginExportWithTemplate = useCallback(
548546
(templateName: string, templateType: string, transactionIDList: string[], policyID?: string) => {
549547
if (isOffline) {
550-
setOfflineModalVisible(true);
548+
showDecisionModal({
549+
title: translate('common.youAppearToBeOffline'),
550+
prompt: translate('common.offlinePrompt'),
551+
secondOptionText: translate('common.buttonConfirm'),
552+
});
551553
return;
552554
}
553555

@@ -570,7 +572,7 @@ function MoneyReportHeader({reportID: reportIDProp, shouldDisplayBackButton = fa
570572
policyID,
571573
});
572574
},
573-
[isOffline, moneyRequestReport, showExportProgressModal, clearSelectedTransactions],
575+
[isOffline, moneyRequestReport, showExportProgressModal, clearSelectedTransactions, showDecisionModal, translate],
574576
);
575577

576578
const isOnSearch = route.name.toLowerCase().startsWith('search');
@@ -583,8 +585,20 @@ function MoneyReportHeader({reportID: reportIDProp, shouldDisplayBackButton = fa
583585
reportActions,
584586
allTransactionsLength: transactions.length,
585587
session,
586-
onExportFailed: () => setIsDownloadErrorModalVisible(true),
587-
onExportOffline: () => setOfflineModalVisible(true),
588+
onExportFailed: () => {
589+
showDecisionModal({
590+
title: translate('common.downloadFailedTitle'),
591+
prompt: translate('common.downloadFailedDescription'),
592+
secondOptionText: translate('common.buttonConfirm'),
593+
});
594+
},
595+
onExportOffline: () => {
596+
showDecisionModal({
597+
title: translate('common.youAppearToBeOffline'),
598+
prompt: translate('common.offlinePrompt'),
599+
secondOptionText: translate('common.buttonConfirm'),
600+
});
601+
},
588602
policy,
589603
beginExportWithTemplate: (templateName, templateType, transactionIDList, policyID) => beginExportWithTemplate(templateName, templateType, transactionIDList, policyID),
590604
isOnSearch,
@@ -1152,7 +1166,11 @@ function MoneyReportHeader({reportID: reportIDProp, shouldDisplayBackButton = fa
11521166
return;
11531167
}
11541168
if (isOffline) {
1155-
setOfflineModalVisible(true);
1169+
showDecisionModal({
1170+
title: translate('common.youAppearToBeOffline'),
1171+
prompt: translate('common.offlinePrompt'),
1172+
secondOptionText: translate('common.buttonConfirm'),
1173+
});
11561174
return;
11571175
}
11581176
exportReportToCSV(
@@ -1161,7 +1179,11 @@ function MoneyReportHeader({reportID: reportIDProp, shouldDisplayBackButton = fa
11611179
transactionIDList: transactionIDs,
11621180
},
11631181
() => {
1164-
setDownloadErrorModalVisible(true);
1182+
showDecisionModal({
1183+
title: translate('common.downloadFailedTitle'),
1184+
prompt: translate('common.downloadFailedDescription'),
1185+
secondOptionText: translate('common.buttonConfirm'),
1186+
});
11651187
},
11661188
translate,
11671189
);
@@ -1236,6 +1258,7 @@ function MoneyReportHeader({reportID: reportIDProp, shouldDisplayBackButton = fa
12361258
isExported,
12371259
exportTemplates,
12381260
beginExportWithTemplate,
1261+
showDecisionModal,
12391262
]);
12401263

12411264
const primaryActionComponent = (
@@ -2334,24 +2357,6 @@ function MoneyReportHeader({reportID: reportIDProp, shouldDisplayBackButton = fa
23342357
onNonReimbursablePaymentError={showNonReimbursablePaymentErrorModal}
23352358
/>
23362359
)}
2337-
<DecisionModal
2338-
title={translate('common.downloadFailedTitle')}
2339-
prompt={translate('common.downloadFailedDescription')}
2340-
isSmallScreenWidth={isSmallScreenWidth}
2341-
onSecondOptionSubmit={() => setDownloadErrorModalVisible(false)}
2342-
secondOptionText={translate('common.buttonConfirm')}
2343-
isVisible={downloadErrorModalVisible}
2344-
onClose={() => setDownloadErrorModalVisible(false)}
2345-
/>
2346-
<DecisionModal
2347-
title={translate('common.downloadFailedTitle')}
2348-
prompt={translate('common.downloadFailedDescription')}
2349-
isSmallScreenWidth={isSmallScreenWidth}
2350-
onSecondOptionSubmit={() => setIsDownloadErrorModalVisible(false)}
2351-
secondOptionText={translate('common.buttonConfirm')}
2352-
isVisible={isDownloadErrorModalVisible}
2353-
onClose={() => setIsDownloadErrorModalVisible(false)}
2354-
/>
23552360
{!!rejectModalAction && (
23562361
<HoldOrRejectEducationalModal
23572362
onClose={dismissRejectModalBasedOnAction}
@@ -2364,15 +2369,6 @@ function MoneyReportHeader({reportID: reportIDProp, shouldDisplayBackButton = fa
23642369
onConfirm={dismissModalAndUpdateUseHold}
23652370
/>
23662371
)}
2367-
<DecisionModal
2368-
title={translate('common.youAppearToBeOffline')}
2369-
prompt={translate('common.offlinePrompt')}
2370-
isSmallScreenWidth={isSmallScreenWidth}
2371-
onSecondOptionSubmit={() => setOfflineModalVisible(false)}
2372-
secondOptionText={translate('common.buttonConfirm')}
2373-
isVisible={offlineModalVisible}
2374-
onClose={() => setOfflineModalVisible(false)}
2375-
/>
23762372
{nonReimbursablePaymentErrorDecisionModal}
23772373
<Modal
23782374
onClose={() => {

src/hooks/useDecisionModal.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import DecisionModalWrapper from '@components/Modal/Global/DecisionModalWrapper';
2+
import type {ModalProps} from '@components/Modal/Global/ModalContext';
3+
import {useModal} from '@components/Modal/Global/ModalContext';
4+
5+
type DecisionModalOptions = Omit<React.ComponentProps<typeof DecisionModalWrapper>, keyof ModalProps>;
6+
7+
const useDecisionModal = () => {
8+
const context = useModal();
9+
10+
const showDecisionModal = (options: DecisionModalOptions) => {
11+
return context.showModal({
12+
component: DecisionModalWrapper,
13+
props: {
14+
...options,
15+
},
16+
});
17+
};
18+
19+
return {
20+
...context,
21+
closeModal: () => context.closeModal(),
22+
showDecisionModal,
23+
};
24+
};
25+
26+
export default useDecisionModal;

0 commit comments

Comments
 (0)