diff --git a/src/components/ExportModal.tsx b/src/components/ExportModal.tsx index 0450b43fe..2f6985701 100644 --- a/src/components/ExportModal.tsx +++ b/src/components/ExportModal.tsx @@ -70,14 +70,20 @@ export default function ExportModal(props: Props) { if (!isElectron()) { return; } - setExportFolder(getData(LS_KEYS.EXPORT)?.folder); + try { + setExportFolder(getData(LS_KEYS.EXPORT)?.folder); - exportService.electronAPIs.registerStopExportListener(stopExport); - exportService.electronAPIs.registerPauseExportListener(pauseExport); - exportService.electronAPIs.registerResumeExportListener(resumeExport); - exportService.electronAPIs.registerRetryFailedExportListener( - retryFailedExport - ); + exportService.electronAPIs.registerStopExportListener(stopExport); + exportService.electronAPIs.registerPauseExportListener(pauseExport); + exportService.electronAPIs.registerResumeExportListener( + resumeExport + ); + exportService.electronAPIs.registerRetryFailedExportListener( + retryFailedExport + ); + } catch (e) { + logError(e, 'error in exportModal'); + } }, []); useEffect(() => { @@ -85,19 +91,25 @@ export default function ExportModal(props: Props) { return; } const main = async () => { - const exportInfo = await exportService.getExportRecord(); - setExportStage(exportInfo?.stage ?? ExportStage.INIT); - setLastExportTime(exportInfo?.lastAttemptTimestamp); - setExportProgress(exportInfo?.progress ?? { current: 0, total: 0 }); - setExportStats({ - success: exportInfo?.exportedFiles?.length ?? 0, - failed: exportInfo?.failedFiles?.length ?? 0, - }); - if (exportInfo?.stage === ExportStage.INPROGRESS) { - resumeExport(); + try { + const exportInfo = await exportService.getExportRecord(); + setExportStage(exportInfo?.stage ?? ExportStage.INIT); + setLastExportTime(exportInfo?.lastAttemptTimestamp); + setExportProgress( + exportInfo?.progress ?? { current: 0, total: 0 } + ); + setExportStats({ + success: exportInfo?.exportedFiles?.length ?? 0, + failed: exportInfo?.failedFiles?.length ?? 0, + }); + if (exportInfo?.stage === ExportStage.INPROGRESS) { + resumeExport(); + } + } catch (e) { + logError(e, 'error handling exportFolder change'); } }; - main(); + void main(); }, [exportFolder]); useEffect(() => { @@ -117,7 +129,7 @@ export default function ExportModal(props: Props) { const failedFilesCnt = exportRecord.failedFiles?.length; const syncedFilesCnt = userPersonalFiles.length; if (syncedFilesCnt > exportedFileCnt + failedFilesCnt) { - updateExportProgress({ + await updateExportProgress({ current: exportedFileCnt + failedFilesCnt, total: syncedFilesCnt, }); @@ -131,11 +143,11 @@ export default function ExportModal(props: Props) { getExportRecordFileUID(file) ) ); - exportService.addFilesQueuedRecord( + await exportService.addFilesQueuedRecord( exportFolder, unExportedFiles ); - updateExportStage(ExportStage.PAUSED); + await updateExportStage(ExportStage.PAUSED); } } catch (e) { setExportStage(ExportStage.INIT); @@ -143,7 +155,7 @@ export default function ExportModal(props: Props) { } } }; - main(); + void main(); }, [props.show]); useEffect(() => { @@ -158,19 +170,21 @@ export default function ExportModal(props: Props) { setData(LS_KEYS.EXPORT, { folder: newFolder }); }; - const updateExportStage = (newStage: ExportStage) => { + const updateExportStage = async (newStage: ExportStage) => { setExportStage(newStage); - exportService.updateExportRecord({ stage: newStage }); + await exportService.updateExportRecord({ stage: newStage }); }; - const updateExportTime = (newTime: number) => { + const updateExportTime = async (newTime: number) => { setLastExportTime(newTime); - exportService.updateExportRecord({ lastAttemptTimestamp: newTime }); + await exportService.updateExportRecord({ + lastAttemptTimestamp: newTime, + }); }; - const updateExportProgress = (newProgress: ExportProgress) => { + const updateExportProgress = async (newProgress: ExportProgress) => { setExportProgress(newProgress); - exportService.updateExportRecord({ progress: newProgress }); + await exportService.updateExportRecord({ progress: newProgress }); }; // ====================== @@ -182,15 +196,15 @@ export default function ExportModal(props: Props) { if (!exportFolder) { await selectExportDirectory(); } - updateExportStage(ExportStage.INPROGRESS); + await updateExportStage(ExportStage.INPROGRESS); await sleep(100); }; const postExportRun = async (exportResult?: { paused?: boolean }) => { if (!exportResult?.paused) { - updateExportStage(ExportStage.FINISHED); + await updateExportStage(ExportStage.FINISHED); await sleep(100); - updateExportTime(Date.now()); - syncExportStatsWithRecord(); + await updateExportTime(Date.now()); + await syncExportStatsWithRecord(); } }; @@ -214,86 +228,106 @@ export default function ExportModal(props: Props) { // UI functions // ============= - const startExport = async () => { - try { - await preExportRun(); - updateExportProgress({ current: 0, total: 0 }); - const exportResult = await exportService.exportFiles( - updateExportProgress, - ExportType.NEW - ); - await postExportRun(exportResult); - } catch (e) { - if (e.message !== CustomError.REQUEST_CANCELLED) { - logError(e, 'startExport failed'); + const startExport = () => { + const main = async () => { + try { + await preExportRun(); + await updateExportProgress({ current: 0, total: 0 }); + const exportResult = await exportService.exportFiles( + updateExportProgress, + ExportType.NEW + ); + await postExportRun(exportResult); + } catch (e) { + if (e.message !== CustomError.REQUEST_CANCELLED) { + logError(e, 'startExport failed'); + } } - } + }; + void main(); }; - const stopExport = async () => { - try { - exportService.stopRunningExport(); - postExportRun(); - } catch (e) { - if (e.message !== CustomError.REQUEST_CANCELLED) { - logError(e, 'stopExport failed'); + const stopExport = () => { + const main = async () => { + try { + exportService.stopRunningExport(); + await postExportRun(); + } catch (e) { + if (e.message !== CustomError.REQUEST_CANCELLED) { + logError(e, 'stopExport failed'); + } } - } + }; + void main(); }; const pauseExport = () => { - try { - updateExportStage(ExportStage.PAUSED); - exportService.pauseRunningExport(); - postExportRun({ paused: true }); - } catch (e) { - if (e.message !== CustomError.REQUEST_CANCELLED) { - logError(e, 'pauseExport failed'); + const main = async () => { + try { + await updateExportStage(ExportStage.PAUSED); + exportService.pauseRunningExport(); + await postExportRun({ paused: true }); + } catch (e) { + if (e.message !== CustomError.REQUEST_CANCELLED) { + logError(e, 'pauseExport failed'); + } } - } + }; + void main(); }; - const resumeExport = async () => { - try { - const exportRecord = await exportService.getExportRecord(); - await preExportRun(); + const resumeExport = () => { + const main = async () => { + try { + const exportRecord = await exportService.getExportRecord(); + await preExportRun(); - const pausedStageProgress = exportRecord.progress; - setExportProgress(pausedStageProgress); + const pausedStageProgress = exportRecord.progress; + setExportProgress(pausedStageProgress); - const updateExportStatsWithOffset = (progress: ExportProgress) => - updateExportProgress({ - current: pausedStageProgress.current + progress.current, - total: pausedStageProgress.current + progress.total, + const updateExportStatsWithOffset = ( + progress: ExportProgress + ) => + updateExportProgress({ + current: pausedStageProgress.current + progress.current, + total: pausedStageProgress.current + progress.total, + }); + const exportResult = await exportService.exportFiles( + updateExportStatsWithOffset, + ExportType.PENDING + ); + + await postExportRun(exportResult); + } catch (e) { + if (e.message !== CustomError.REQUEST_CANCELLED) { + logError(e, 'resumeExport failed'); + } + } + }; + void main(); + }; + + const retryFailedExport = () => { + const main = async () => { + try { + await preExportRun(); + await updateExportProgress({ + current: 0, + total: exportStats.failed, }); - const exportResult = await exportService.exportFiles( - updateExportStatsWithOffset, - ExportType.PENDING - ); - await postExportRun(exportResult); - } catch (e) { - if (e.message !== CustomError.REQUEST_CANCELLED) { - logError(e, 'resumeExport failed'); + const exportResult = await exportService.exportFiles( + updateExportProgress, + ExportType.RETRY_FAILED + ); + await postExportRun(exportResult); + } catch (e) { + if (e.message !== CustomError.REQUEST_CANCELLED) { + logError(e, 'retryFailedExport failed'); + } } - } - }; - - const retryFailedExport = async () => { - try { - await preExportRun(); - updateExportProgress({ current: 0, total: exportStats.failed }); - - const exportResult = await exportService.exportFiles( - updateExportProgress, - ExportType.RETRY_FAILED - ); - await postExportRun(exportResult); - } catch (e) { - if (e.message !== CustomError.REQUEST_CANCELLED) { - logError(e, 'retryFailedExport failed'); - } - } + }; + void main(); }; const ExportDynamicContent = () => { diff --git a/src/services/exportService.ts b/src/services/exportService.ts index 68a905dc6..120bfb155 100644 --- a/src/services/exportService.ts +++ b/src/services/exportService.ts @@ -49,6 +49,7 @@ import { User } from 'types/user'; import { FILE_TYPE, TYPE_JPEG, TYPE_JPG } from 'constants/file'; import { ExportType, ExportNotification, RecordType } from 'constants/export'; import { ElectronAPIs } from 'types/electron'; +import { CustomError } from 'utils/error'; const LATEST_EXPORT_VERSION = 1; const EXPORT_RECORD_FILE_NAME = 'export_status.json'; @@ -83,10 +84,11 @@ class ExportService { this.pauseExport = true; } async exportFiles( - updateProgress: (progress: ExportProgress) => void, + updateProgress: (progress: ExportProgress) => Promise, exportType: ExportType ) { try { + // eslint-disable-next-line @typescript-eslint/no-misused-promises if (this.exportInProgress) { this.electronAPIs.sendNotification( ExportNotification.IN_PROGRESS @@ -177,7 +179,7 @@ class ExportService { newCollections: Collection[], renamedCollections: Collection[], collectionIDPathMap: CollectionIDPathMap, - updateProgress: (progress: ExportProgress) => void, + updateProgress: (progress: ExportProgress) => Promise, exportDir: string ): Promise<{ paused: boolean }> { try { @@ -212,7 +214,7 @@ class ExportService { this.electronAPIs.showOnTray({ export_progress: `0 / ${files.length} files exported`, }); - updateProgress({ + await updateProgress({ current: 0, total: files.length, }); @@ -239,6 +241,12 @@ class ExportService { RecordType.SUCCESS ); } catch (e) { + if ( + e.message === + CustomError.ADD_FILE_EXPORTED_RECORD_FAILED + ) { + throw e; + } await this.addFileExportedRecord( exportDir, file, @@ -255,7 +263,10 @@ class ExportService { files.length } files exported`, }); - updateProgress({ current: index + 1, total: files.length }); + await updateProgress({ + current: index + 1, + total: files.length, + }); } if (this.stopExport) { this.electronAPIs.sendNotification(ExportNotification.ABORT); @@ -266,7 +277,7 @@ class ExportService { } else if (failedFileCount > 0) { this.electronAPIs.sendNotification(ExportNotification.FAILED); this.electronAPIs.showOnTray({ - retry_export: `export failed - retry export`, + retry_export: `Retry failed export`, }); } else { this.electronAPIs.sendNotification(ExportNotification.FINISH); @@ -289,32 +300,37 @@ class ExportService { file: EnteFile, type: RecordType ) { - const fileUID = getExportRecordFileUID(file); - const exportRecord = await this.getExportRecord(folder); - exportRecord.queuedFiles = exportRecord.queuedFiles.filter( - (queuedFilesUID) => queuedFilesUID !== fileUID - ); - if (type === RecordType.SUCCESS) { - if (!exportRecord.exportedFiles) { - exportRecord.exportedFiles = []; - } - exportRecord.exportedFiles.push(fileUID); - exportRecord.failedFiles && - (exportRecord.failedFiles = exportRecord.failedFiles.filter( - (FailedFileUID) => FailedFileUID !== fileUID - )); - } else { - if (!exportRecord.failedFiles) { - exportRecord.failedFiles = []; - } - if (!exportRecord.failedFiles.find((x) => x === fileUID)) { - exportRecord.failedFiles.push(fileUID); + try { + const fileUID = getExportRecordFileUID(file); + const exportRecord = await this.getExportRecord(folder); + exportRecord.queuedFiles = exportRecord.queuedFiles.filter( + (queuedFilesUID) => queuedFilesUID !== fileUID + ); + if (type === RecordType.SUCCESS) { + if (!exportRecord.exportedFiles) { + exportRecord.exportedFiles = []; + } + exportRecord.exportedFiles.push(fileUID); + exportRecord.failedFiles && + (exportRecord.failedFiles = exportRecord.failedFiles.filter( + (FailedFileUID) => FailedFileUID !== fileUID + )); + } else { + if (!exportRecord.failedFiles) { + exportRecord.failedFiles = []; + } + if (!exportRecord.failedFiles.find((x) => x === fileUID)) { + exportRecord.failedFiles.push(fileUID); + } } + exportRecord.exportedFiles = dedupe(exportRecord.exportedFiles); + exportRecord.queuedFiles = dedupe(exportRecord.queuedFiles); + exportRecord.failedFiles = dedupe(exportRecord.failedFiles); + await this.updateExportRecord(exportRecord, folder); + } catch (e) { + logError(e, 'addFileExportedRecord failed'); + throw Error(CustomError.ADD_FILE_EXPORTED_RECORD_FAILED); } - exportRecord.exportedFiles = dedupe(exportRecord.exportedFiles); - exportRecord.queuedFiles = dedupe(exportRecord.queuedFiles); - exportRecord.failedFiles = dedupe(exportRecord.failedFiles); - await this.updateExportRecord(exportRecord, folder); } async addCollectionExportedRecord( @@ -354,6 +370,7 @@ class ExportService { ); } catch (e) { logError(e, 'error updating Export Record'); + throw e; } } @@ -372,6 +389,7 @@ class ExportService { } } catch (e) { logError(e, 'export Record JSON parsing failed '); + throw e; } } diff --git a/src/utils/error/index.ts b/src/utils/error/index.ts index 557d7c7f2..a4bf9a7c2 100644 --- a/src/utils/error/index.ts +++ b/src/utils/error/index.ts @@ -55,6 +55,7 @@ export const CustomError = { 'Windows native image processing is not supported', NETWORK_ERROR: 'Network Error', NOT_FILE_OWNER: 'not file owner', + ADD_FILE_EXPORTED_RECORD_FAILED: 'add file exported record failed', }; export function parseUploadErrorCodes(error) {