Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 48 additions & 50 deletions src/components/DecisionModal.tsx
Original file line number Diff line number Diff line change
@@ -1,80 +1,78 @@
import type {ReactNode} from 'react';
import React from 'react';
import {View} from 'react-native';
import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
import Button from './Button';
import Header from './Header';
import Modal from './Modal';
import Text from './Text';
import RenderHTML from './RenderHTML';

type DecisionModalProps = {
/** Title describing purpose of modal */
title: string;

/** Modal subtitle/description */
prompt?: string;

/** Text content used in first button */
firstOptionText?: string;

/** Text content used in second button */
secondOptionText: string;

/** onSubmit callback fired after clicking on first button */
onFirstOptionSubmit?: () => void;

/** onSubmit callback fired after clicking on second button */
onSecondOptionSubmit: () => void;
/** Modal content */
children: ReactNode;

/** Is the window width narrow, like on a mobile device? */
isSmallScreenWidth: boolean;

/** Whether modal is visible */
isVisible: boolean;

/** Callback for closing modal */
onClose: () => void;
};

/** Whether modal is visible */
isVisible: boolean;
type DecisionModalHeaderProps = {
/** Title describing purpose of modal */
title: string;
};

function DecisionModal({title, prompt = '', firstOptionText, secondOptionText, onFirstOptionSubmit, onSecondOptionSubmit, isSmallScreenWidth, onClose, isVisible}: DecisionModalProps) {
const styles = useThemeStyles();
type DecisionModalContentProps = {
/** HTML content to render */
html: string;
};

type DecisionModalFooterProps = {
/** Footer content */
children: ReactNode;
};

function DecisionModal({children, isSmallScreenWidth, isVisible, onClose}: DecisionModalProps) {
const styles = useThemeStyles();
return (
<Modal
onClose={onClose}
isVisible={isVisible}
type={isSmallScreenWidth ? CONST.MODAL.MODAL_TYPE.BOTTOM_DOCKED : CONST.MODAL.MODAL_TYPE.CONFIRM}
innerContainerStyle={styles.pv0}
>
<View style={[styles.m5]}>
<View>
<View style={[styles.flexRow, styles.mb5]}>
<Header
title={title}
containerStyles={[styles.alignItemsCenter]}
/>
</View>
<Text>{prompt}</Text>
</View>
{!!firstOptionText && (
<Button
success
style={[styles.mt5]}
onPress={onFirstOptionSubmit}
pressOnEnter
text={firstOptionText}
large
/>
)}
<Button
style={[firstOptionText ? styles.mt3 : styles.mt5, styles.noSelect]}
onPress={onSecondOptionSubmit}
text={secondOptionText}
large
/>
</View>
<View style={styles.m5}>{children}</View>
</Modal>
);
}

function DecisionModalHeader({title}: DecisionModalHeaderProps) {
const styles = useThemeStyles();
return (
<View style={[styles.flexRow, styles.mb5]}>
<Header
title={title}
containerStyles={styles.alignItemsCenter}
/>
</View>
);
}

function DecisionModalContent({html}: DecisionModalContentProps) {
return <RenderHTML html={html} />;
}
Comment on lines +65 to +67
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we even need that? 🤔

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not used anywhere


function DecisionModalFooter({children}: DecisionModalFooterProps) {
const styles = useThemeStyles();
return <View style={styles.mt5}>{children}</View>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think those styles are aligned with the previous ones

Image

}

DecisionModal.Header = DecisionModalHeader;
DecisionModal.Content = DecisionModalContent;
DecisionModal.Footer = DecisionModalFooter;

export default DecisionModal;
20 changes: 14 additions & 6 deletions src/components/ImportOnyxState/BaseImportOnyxState.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import React from 'react';
import AttachmentPicker from '@components/AttachmentPicker';
import Button from '@components/Button';
import DecisionModal from '@components/DecisionModal';
import MenuItem from '@components/MenuItem';
import Text from '@components/Text';
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
import useLocalize from '@hooks/useLocalize';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
Expand Down Expand Up @@ -48,14 +50,20 @@ function BaseImportOnyxState({
}}
</AttachmentPicker>
<DecisionModal
title={translate('initialSettingsPage.troubleshoot.invalidFile')}
prompt={translate('initialSettingsPage.troubleshoot.invalidFileDescription')}
isSmallScreenWidth={isSmallScreenWidth}
onSecondOptionSubmit={() => setIsErrorModalVisible(false)}
secondOptionText={translate('common.ok')}
isVisible={isErrorModalVisible}
onClose={() => setIsErrorModalVisible(false)}
/>
isSmallScreenWidth={isSmallScreenWidth}
>
<DecisionModal.Header title={translate('initialSettingsPage.troubleshoot.invalidFile')} />
<Text>{translate('initialSettingsPage.troubleshoot.invalidFileDescription')}</Text>
<DecisionModal.Footer>
<Button
text={translate('common.ok')}
onPress={() => setIsErrorModalVisible(false)}
large
/>
</DecisionModal.Footer>
</DecisionModal>
</>
);
}
Expand Down
54 changes: 36 additions & 18 deletions src/components/MoneyReportHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1798,23 +1798,35 @@ function MoneyReportHeader({
/>
)}
<DecisionModal
title={translate('common.downloadFailedTitle')}
prompt={translate('common.downloadFailedDescription')}
isSmallScreenWidth={isSmallScreenWidth}
onSecondOptionSubmit={() => setDownloadErrorModalVisible(false)}
secondOptionText={translate('common.buttonConfirm')}
isVisible={downloadErrorModalVisible}
onClose={() => setDownloadErrorModalVisible(false)}
/>
<DecisionModal
title={translate('common.downloadFailedTitle')}
prompt={translate('common.downloadFailedDescription')}
isSmallScreenWidth={isSmallScreenWidth}
onSecondOptionSubmit={() => setIsDownloadErrorModalVisible(false)}
secondOptionText={translate('common.buttonConfirm')}
>
<DecisionModal.Header title={translate('common.downloadFailedTitle')} />
<Text>{translate('common.downloadFailedDescription')}</Text>
<DecisionModal.Footer>
<Button
text={translate('common.buttonConfirm')}
onPress={() => setDownloadErrorModalVisible(false)}
large
/>
</DecisionModal.Footer>
</DecisionModal>
<DecisionModal
isVisible={isDownloadErrorModalVisible}
onClose={() => setIsDownloadErrorModalVisible(false)}
/>
isSmallScreenWidth={isSmallScreenWidth}
>
<DecisionModal.Header title={translate('common.downloadFailedTitle')} />
<Text>{translate('common.downloadFailedDescription')}</Text>
<DecisionModal.Footer>
<Button
text={translate('common.buttonConfirm')}
onPress={() => setIsDownloadErrorModalVisible(false)}
large
/>
</DecisionModal.Footer>
</DecisionModal>
<ConfirmModal
title={translate('common.duplicateExpense')}
isVisible={duplicateDistanceErrorModalVisible}
Expand All @@ -1837,14 +1849,20 @@ function MoneyReportHeader({
/>
)}
<DecisionModal
title={translate('common.youAppearToBeOffline')}
prompt={translate('common.offlinePrompt')}
isSmallScreenWidth={isSmallScreenWidth}
onSecondOptionSubmit={() => setOfflineModalVisible(false)}
secondOptionText={translate('common.buttonConfirm')}
isVisible={offlineModalVisible}
onClose={() => setOfflineModalVisible(false)}
/>
isSmallScreenWidth={isSmallScreenWidth}
>
<DecisionModal.Header title={translate('common.youAppearToBeOffline')} />
<Text>{translate('common.offlinePrompt')}</Text>
<DecisionModal.Footer>
<Button
text={translate('common.buttonConfirm')}
onPress={() => setOfflineModalVisible(false)}
large
/>
</DecisionModal.Footer>
</DecisionModal>
<Modal
onClose={() => {
setIsPDFModalVisible(false);
Expand Down
19 changes: 13 additions & 6 deletions src/components/MoneyRequestHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import {View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
import Text from '@components/Text';

Check warning on line 8 in src/components/MoneyRequestHeader.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

Unexpected subpath import via alias '@components/Text'. Use './Text' instead

Check warning on line 8 in src/components/MoneyRequestHeader.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Unexpected subpath import via alias '@components/Text'. Use './Text' instead
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
import useDefaultExpensePolicy from '@hooks/useDefaultExpensePolicy';
import useDeleteTransactions from '@hooks/useDeleteTransactions';
Expand Down Expand Up @@ -570,14 +571,20 @@
)}
<LoadingBar shouldShow={shouldShowLoadingBar && shouldUseNarrowLayout} />
<DecisionModal
title={translate('common.downloadFailedTitle')}
prompt={translate('common.downloadFailedDescription')}
isSmallScreenWidth={isSmallScreenWidth}
onSecondOptionSubmit={() => setDownloadErrorModalVisible(false)}
secondOptionText={translate('common.buttonConfirm')}
isVisible={downloadErrorModalVisible}
onClose={() => setDownloadErrorModalVisible(false)}
/>
isSmallScreenWidth={isSmallScreenWidth}
>
<DecisionModal.Header title={translate('common.downloadFailedTitle')} />
<Text>{translate('common.downloadFailedDescription')}</Text>
<DecisionModal.Footer>
<Button
text={translate('common.buttonConfirm')}
onPress={() => setDownloadErrorModalVisible(false)}
large
/>
</DecisionModal.Footer>
</DecisionModal>
<ConfirmModal
title={translate('iou.deleteExpense', {count: 1})}
isVisible={isDeleteModalVisible}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import React, {useCallback, useEffect, useLayoutEffect, useMemo, useRef, useStat
import type {NativeScrollEvent, NativeSyntheticEvent} from 'react-native';
import {DeviceEventEmitter, InteractionManager, View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import Button from '@components/Button';
import ButtonWithDropdownMenu from '@components/ButtonWithDropdownMenu';
import Checkbox from '@components/Checkbox';
import ConfirmModal from '@components/ConfirmModal';
Expand Down Expand Up @@ -799,23 +800,35 @@ function MoneyRequestReportActionsList({
)}
</View>
<DecisionModal
title={translate('common.downloadFailedTitle')}
prompt={translate('common.downloadFailedDescription')}
isSmallScreenWidth={shouldUseNarrowLayout}
onSecondOptionSubmit={() => setIsDownloadErrorModalVisible(false)}
secondOptionText={translate('common.buttonConfirm')}
isVisible={isDownloadErrorModalVisible}
onClose={() => setIsDownloadErrorModalVisible(false)}
/>
<DecisionModal
title={translate('common.youAppearToBeOffline')}
prompt={translate('common.offlinePrompt')}
isSmallScreenWidth={shouldUseNarrowLayout}
onSecondOptionSubmit={() => setOfflineModalVisible(false)}
secondOptionText={translate('common.buttonConfirm')}
>
<DecisionModal.Header title={translate('common.downloadFailedTitle')} />
<Text>{translate('common.downloadFailedDescription')}</Text>
<DecisionModal.Footer>
<Button
text={translate('common.buttonConfirm')}
onPress={() => setIsDownloadErrorModalVisible(false)}
large
/>
</DecisionModal.Footer>
</DecisionModal>
<DecisionModal
isVisible={offlineModalVisible}
onClose={() => setOfflineModalVisible(false)}
/>
isSmallScreenWidth={shouldUseNarrowLayout}
>
<DecisionModal.Header title={translate('common.youAppearToBeOffline')} />
<Text>{translate('common.offlinePrompt')}</Text>
<DecisionModal.Footer>
<Button
text={translate('common.buttonConfirm')}
onPress={() => setOfflineModalVisible(false)}
large
/>
</DecisionModal.Footer>
</DecisionModal>
<ConfirmModal
onConfirm={() => {
setIsExportWithTemplateModalVisible(false);
Expand Down
33 changes: 25 additions & 8 deletions src/components/ProcessMoneyReportHoldMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import React, {useContext, useMemo} from 'react';
import type {OnyxEntry} from 'react-native-onyx';
import Button from '@components/Button';

Check warning on line 3 in src/components/ProcessMoneyReportHoldMenu.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

Unexpected subpath import via alias '@components/Button'. Use './Button' instead

Check warning on line 3 in src/components/ProcessMoneyReportHoldMenu.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Unexpected subpath import via alias '@components/Button'. Use './Button' instead
import Text from '@components/Text';

Check warning on line 4 in src/components/ProcessMoneyReportHoldMenu.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

Unexpected subpath import via alias '@components/Text'. Use './Text' instead

Check warning on line 4 in src/components/ProcessMoneyReportHoldMenu.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Unexpected subpath import via alias '@components/Text'. Use './Text' instead
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
import usePermissions from '@hooks/usePermissions';
import usePolicy from '@hooks/usePolicy';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useThemeStyles from '@hooks/useThemeStyles';
import {hasViolations as hasViolationsReportUtils} from '@libs/ReportUtils';
import {approveMoneyRequest, payMoneyRequest} from '@userActions/IOU';
import CONST from '@src/CONST';
Expand Down Expand Up @@ -67,6 +70,7 @@
hasNonHeldExpenses,
}: ProcessMoneyReportHoldMenuProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
const isApprove = requestType === CONST.IOU.REPORT_ACTION_TYPE.APPROVE;
// We need to use isSmallScreenWidth instead of shouldUseNarrowLayout to apply the correct modal type
// eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
Expand Down Expand Up @@ -121,16 +125,29 @@

return (
<DecisionModal
title={translate(isApprove ? 'iou.confirmApprove' : 'iou.confirmPay')}
onClose={onClose}
isVisible={isVisible}
prompt={promptText}
firstOptionText={hasNonHeldExpenses ? `${translate(isApprove ? 'iou.approveOnly' : 'iou.payOnly')} ${nonHeldAmount}` : undefined}
secondOptionText={`${translate(isApprove ? 'iou.approve' : 'iou.pay')} ${fullAmount}`}
onFirstOptionSubmit={() => onSubmit(false)}
onSecondOptionSubmit={() => onSubmit(true)}
onClose={onClose}
isSmallScreenWidth={isSmallScreenWidth}
/>
>
<DecisionModal.Header title={translate(isApprove ? 'iou.confirmApprove' : 'iou.confirmPay')} />
<Text>{promptText}</Text>
<DecisionModal.Footer>
{!!hasNonHeldExpenses && (
<Button
success
text={`${translate(isApprove ? 'iou.approveOnly' : 'iou.payOnly')} ${nonHeldAmount}`}
onPress={() => onSubmit(false)}
large
style={styles.mb4}
/>
)}
<Button
text={`${translate(isApprove ? 'iou.approve' : 'iou.pay')} ${fullAmount}`}
onPress={() => onSubmit(true)}
large
/>
</DecisionModal.Footer>
</DecisionModal>
);
}

Expand Down
Loading
Loading