diff --git a/src/components/ExportDownloadStatusModal.tsx b/src/components/ExportDownloadStatusModal.tsx new file mode 100644 index 000000000000..53ca1ce64e16 --- /dev/null +++ b/src/components/ExportDownloadStatusModal.tsx @@ -0,0 +1,186 @@ +import {hasSeenTourSelector} from '@selectors/Onboarding'; +import React, {useCallback, useEffect} from 'react'; +import {View} from 'react-native'; +import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; +import useLocalize from '@hooks/useLocalize'; +import useOnyx from '@hooks/useOnyx'; +import usePreviousDefined from '@hooks/usePreviousDefined'; +import useResponsiveLayout from '@hooks/useResponsiveLayout'; +import useThemeStyles from '@hooks/useThemeStyles'; +import fileDownload from '@libs/fileDownload'; +import {clearExportDownload, sendExportFileFromConcierge} from '@userActions/Export'; +import {navigateToConciergeChat} from '@userActions/Report'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import ActivityIndicator from './ActivityIndicator'; +import Button from './Button'; +import Modal from './Modal'; +import Text from './Text'; + +type ExportDownloadStatusModalProps = { + /** The export ID to subscribe to */ + exportID: string; + + /** Whether the modal is visible */ + isVisible: boolean; + + /** Callback when the modal is closed */ + onClose: () => void; + + /** Body text for the failed state — PDF and CSV use different copy */ + failedBody?: string; +}; + +function ExportDownloadStatusModal({exportID, isVisible, onClose, failedBody}: ExportDownloadStatusModalProps) { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + // isSmallScreenWidth is needed here because the modal type depends on actual screen width, not layout mode + // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth + const {isSmallScreenWidth} = useResponsiveLayout(); + const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails(); + + const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); + const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); + const [betas] = useOnyx(ONYXKEYS.BETAS); + const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector}); + + const [exportDownload] = useOnyx(`${ONYXKEYS.COLLECTION.EXPORT_DOWNLOAD}${exportID}`); + const displayedExport = usePreviousDefined(exportDownload); + + const state = displayedExport?.state; + const shouldSendFromConcierge = displayedExport?.shouldSendFromConcierge; + const downloadURL = displayedExport?.downloadURL; + const isPreparing = state === CONST.EXPORT_DOWNLOAD.STATE.PREPARING && !shouldSendFromConcierge; + const isConcierge = !!shouldSendFromConcierge; + const isReady = state === CONST.EXPORT_DOWNLOAD.STATE.READY; + const isFailed = state === CONST.EXPORT_DOWNLOAD.STATE.FAILED; + + useEffect(() => { + if (!isReady || !downloadURL || shouldSendFromConcierge) { + return; + } + fileDownload(translate, downloadURL); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isReady]); + + const handleSendFromConcierge = useCallback(() => { + sendExportFileFromConcierge(exportID, displayedExport ?? undefined); + }, [exportID, displayedExport]); + + const handleGoToConcierge = useCallback(() => { + onClose(); + navigateToConciergeChat(conciergeReportID, introSelected, currentUserAccountID, isSelfTourViewed, betas); + }, [onClose, conciergeReportID, introSelected, currentUserAccountID, isSelfTourViewed, betas]); + + const handleDismiss = useCallback(() => { + onClose(); + }, [onClose]); + + const handleDownloadFile = useCallback(() => { + if (!downloadURL) { + return; + } + fileDownload(translate, downloadURL); + }, [downloadURL, translate]); + + const handleClose = useCallback(() => { + clearExportDownload(exportID, displayedExport ?? undefined); + onClose(); + }, [exportID, displayedExport, onClose]); + + const isNonDismissible = isPreparing; + + const renderContent = () => { + if (isPreparing) { + return ( + <> + + {translate('exportDownload.preparingTitle')} + {translate('exportDownload.preparingBody')} +