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
59 changes: 59 additions & 0 deletions src/components/ExportDownloadStatusManager.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import useOnyx from '@hooks/useOnyx';

import {clearExportDownload} from '@libs/actions/Export';

import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';

import React from 'react';

import ExportDownloadStatusModal from './ExportDownloadStatusModal';

/**
* Renders the queued export status modal for the whole app. It watches the export collection and shows the modal
* for the active export (preparing, ready, or failed). Because it is the single owner of the modal and reads
* straight from Onyx, a screen only has to start an export (which writes its record); this shows the progress,
* delivers the file when it is ready, and still surfaces it after a reload or once the screen that started it is
* gone. There is no per-screen modal to coordinate with.
*/
function ExportDownloadStatusManager() {
const [exportDownloads] = useOnyx(ONYXKEYS.COLLECTION.EXPORT_DOWNLOAD);

// Skip Concierge hand-offs: once the user opts into Concierge delivery, the file (or the error) is delivered
// through the Concierge chat, so the modal has nothing left to show. Setting shouldSendFromConcierge is what
// closes the modal, and the worker deletes the record once it is done.
const activeEntry = Object.entries(exportDownloads ?? {}).find(
([, exportDownload]) =>
!exportDownload?.shouldSendFromConcierge &&
(exportDownload?.state === CONST.EXPORT_DOWNLOAD.STATE.PREPARING ||
exportDownload?.state === CONST.EXPORT_DOWNLOAD.STATE.READY ||
exportDownload?.state === CONST.EXPORT_DOWNLOAD.STATE.FAILED),
);

if (!activeEntry) {
return null;
}

const exportID = activeEntry[0].replace(ONYXKEYS.COLLECTION.EXPORT_DOWNLOAD, '');
const exportDownload = activeEntry[1];

const handleClose = () => {
// The modal blocks dismissal while still preparing, so this is belt-and-suspenders.
if (exportDownload?.state === CONST.EXPORT_DOWNLOAD.STATE.PREPARING) {
return;
}
clearExportDownload(exportID, exportDownload);
};

return (
<ExportDownloadStatusModal
exportID={exportID}
isVisible
onClose={handleClose}
/>
);
}

ExportDownloadStatusManager.displayName = 'ExportDownloadStatusManager';

export default ExportDownloadStatusManager;
32 changes: 2 additions & 30 deletions src/components/ExportDownloadStatusModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'
import useEnvironment from '@hooks/useEnvironment';
import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
import useOpenConciergeAnywhere from '@hooks/useOpenConciergeAnywhere';
import usePreviousDefined from '@hooks/usePreviousDefined';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useThemeStyles from '@hooks/useThemeStyles';
Expand Down Expand Up @@ -64,7 +63,6 @@ function ExportDownloadStatusModal({exportID, isVisible, onClose, failedBody}: E
const receiptCount = displayedExport?.receiptCount;
const failedReceiptCount = displayedExport?.failedReceiptCount ?? 0;
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;
const isEmptyReceipts = isReady && exportType === CONST.EXPORT_DOWNLOAD.TYPE.RECEIPTS && receiptCount === 0;
Expand Down Expand Up @@ -94,17 +92,11 @@ function ExportDownloadStatusModal({exportID, isVisible, onClose, failedBody}: E
const handleSendFromConcierge = () => {
sendExportFileFromConcierge(exportID, displayedExport ?? undefined);
};
const {openConciergeAnywhere} = useOpenConciergeAnywhere();

const handleGoToConcierge = () => {
onClose();
openConciergeAnywhere({forceConcierge: true});
};

const handleDownloadFile = () => {
downloadFile();
// Clearing the export download is owned by the parent's onClose handler (it runs on every dismissal and
// skips the clear for the Concierge path). Clearing here too would queue a duplicate ClearExportDownload write.
// Clearing the export download is owned by the parent's onClose handler (it runs on every dismissal).
// Clearing here too would queue a duplicate ClearExportDownload write.
onClose();
};

Expand All @@ -128,26 +120,6 @@ function ExportDownloadStatusModal({exportID, isVisible, onClose, failedBody}: E
);
}

if (isConcierge) {
return (
<>
<Text style={[styles.exportDownloadTitle, styles.mb2]}>{translate('exportDownload.conciergeTitle')}</Text>
<Text style={styles.mb5}>{translate('exportDownload.conciergeBody')}</Text>
<Button
success
text={translate('exportDownload.goToConcierge')}
onPress={handleGoToConcierge}
style={styles.w100}
/>
<Button
text={translate('exportDownload.dismiss')}
onPress={onClose}
style={[styles.w100, styles.mt3]}
/>
</>
);
}

if (isEmptyReceipts) {
return (
<>
Expand Down
17 changes: 7 additions & 10 deletions src/components/MoneyReportHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import {View} from 'react-native';
import HeaderLoadingBar from './HeaderLoadingBar';
import HeaderWithBackButton from './HeaderWithBackButton';
import MoneyReportHeaderActions from './MoneyReportHeaderActions';
import {ExportDownloadStatusProvider} from './MoneyReportHeaderActions/ExportDownloadStatusProvider';
import MoneyReportHeaderModals from './MoneyReportHeaderModals';
import MoneyReportHeaderMoreContent from './MoneyReportHeaderMoreContent';
import {PaymentAnimationsProvider} from './PaymentAnimationsContext';
Expand All @@ -44,15 +43,13 @@ type MoneyReportHeaderProps = {
function MoneyReportHeader({reportID, shouldDisplayBackButton = false, onBackButtonPress}: MoneyReportHeaderProps) {
return (
<MoneyReportHeaderModals reportID={reportID}>
<ExportDownloadStatusProvider>
<PaymentAnimationsProvider>
<MoneyReportHeaderContent
reportID={reportID}
shouldDisplayBackButton={shouldDisplayBackButton}
onBackButtonPress={onBackButtonPress}
/>
</PaymentAnimationsProvider>
</ExportDownloadStatusProvider>
<PaymentAnimationsProvider>
<MoneyReportHeaderContent
reportID={reportID}
shouldDisplayBackButton={shouldDisplayBackButton}
onBackButtonPress={onBackButtonPress}
/>
</PaymentAnimationsProvider>
</MoneyReportHeaderModals>
);
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import BulkDuplicateHandler from '@components/Search/BulkDuplicateHandler';
import {useSearchSelectionActions, useSearchSelectionContext} from '@components/Search/SearchContext';

import useConfirmModal from '@hooks/useConfirmModal';
import useExportDownloadStatusModal from '@hooks/useExportDownloadStatusModal';
import useFilterSelectedTransactions from '@hooks/useFilterSelectedTransactions';
import useLocalize from '@hooks/useLocalize';
import useMobileSelectionMode from '@hooks/useMobileSelectionMode';
Expand Down Expand Up @@ -82,7 +81,6 @@ function SelectionToolbar({reportID, transactions, reportActions}: SelectionTool

const isMobileSelectionModeEnabled = useMobileSelectionMode();
const {showConfirmModal} = useConfirmModal();
const {trackExport, exportDownloadStatusModal} = useExportDownloadStatusModal(() => clearSelectedTransactions(true));

const [offlineModalVisible, setOfflineModalVisible] = useState(false);
const [isDownloadErrorModalVisible, setIsDownloadErrorModalVisible] = useState(false);
Expand All @@ -100,7 +98,7 @@ function SelectionToolbar({reportID, transactions, reportActions}: SelectionTool
return;
}

const exportID = queueExportSearchWithTemplate(
queueExportSearchWithTemplate(
{
templateName,
templateType,
Expand All @@ -112,7 +110,8 @@ function SelectionToolbar({reportID, transactions, reportActions}: SelectionTool
},
true,
);
trackExport(exportID);
// Clear the selection now that the export has started; the app-level ExportDownloadStatusManager shows the modal.
clearSelectedTransactions(true);
};

const onDeleteSelected = (handleDeleteTransactions: () => void, handleDeleteTransactionsWithNavigation: (backToRoute?: Route) => void) => {
Expand Down Expand Up @@ -238,7 +237,6 @@ function SelectionToolbar({reportID, transactions, reportActions}: SelectionTool

return (
<>
{exportDownloadStatusModal}
{isDuplicateOptionVisible && (
<BulkDuplicateHandler
selectedTransactionsKeys={selectedTransactionIDs}
Expand Down
2 changes: 0 additions & 2 deletions src/components/Search/SearchBulkActionsButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ function SearchBulkActionsButton({queryJSON}: SearchBulkActionsButtonProps) {
handleExpensifyCardStatementPDFModalHide,
isExpensifyCardStatementMultiFeedAlertVisible,
handleExpensifyCardStatementMultiFeedAlertClose,
exportDownloadStatusModal,
dismissModalAndUpdateUseHold,
dismissRejectModalBasedOnAction,
isDuplicateOptionVisible,
Expand Down Expand Up @@ -339,7 +338,6 @@ function SearchBulkActionsButton({queryJSON}: SearchBulkActionsButtonProps) {
isDM={areAllTransactionsFromDMReports}
/>
)}
{exportDownloadStatusModal}
</>
);
}
Expand Down
13 changes: 7 additions & 6 deletions src/hooks/useExportActions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type {DropdownOption} from '@components/ButtonWithDropdownMenu/types';
import {useExportDownloadStatus} from '@components/MoneyReportHeaderActions/ExportDownloadStatusProvider';
import type {PopoverMenuItem} from '@components/PopoverMenu';
import {useSearchSelectionActions} from '@components/Search/SearchContext';

import {exportReceiptsToZip} from '@libs/actions/Export';
import {openOldDotLink} from '@libs/actions/Link';
Expand Down Expand Up @@ -70,7 +70,7 @@ function useExportActions({reportID, policy, onPDFModalOpen}: UseExportActionsPa

const {showDecisionModal} = useDecisionModal();
const {triggerExportOrConfirm} = useExportAgainModal(moneyRequestReport?.reportID, moneyRequestReport?.policyID);
const {trackExport} = useExportDownloadStatus();
const {clearSelectedTransactions} = useSearchSelectionActions();

const expensifyIcons = useMemoizedLazyExpensifyIcons([
'Table',
Expand Down Expand Up @@ -116,7 +116,7 @@ function useExportActions({reportID, policy, onPDFModalOpen}: UseExportActionsPa
return;
}

const exportID = queueExportSearchWithTemplate(
queueExportSearchWithTemplate(
{
templateName,
templateType,
Expand All @@ -128,7 +128,8 @@ function useExportActions({reportID, policy, onPDFModalOpen}: UseExportActionsPa
},
true,
);
trackExport(exportID);
// Clear the selection now that the export has started; the app-level ExportDownloadStatusManager shows the modal.
clearSelectedTransactions(true);
};

const exportSubmenuOptions: Record<string, DropdownOption<string>> = {
Expand Down Expand Up @@ -278,8 +279,8 @@ function useExportActions({reportID, policy, onPDFModalOpen}: UseExportActionsPa
if (!moneyRequestReport?.reportID) {
return;
}
const exportID = exportReceiptsToZip([moneyRequestReport.reportID]);
trackExport(exportID);
exportReceiptsToZip([moneyRequestReport.reportID]);
clearSelectedTransactions(true);
},
},
[CONST.REPORT.SECONDARY_ACTIONS.PRINT]: {
Expand Down
56 changes: 0 additions & 56 deletions src/hooks/useExportDownloadStatusModal.tsx

This file was deleted.

Loading
Loading