2021-07-13 13:39:31 +00:00
|
|
|
import isElectron from 'is-electron';
|
2021-07-07 05:31:48 +00:00
|
|
|
import React, { useEffect, useState } from 'react';
|
2021-07-07 09:20:59 +00:00
|
|
|
import { Button } from 'react-bootstrap';
|
2021-07-14 09:16:00 +00:00
|
|
|
import exportService, { ExportProgress, ExportStage, ExportStats, ExportType } from 'services/exportService';
|
2021-07-15 11:04:16 +00:00
|
|
|
import { getLocalFiles } from 'services/fileService';
|
2021-07-08 05:44:54 +00:00
|
|
|
import styled from 'styled-components';
|
2021-07-14 04:33:28 +00:00
|
|
|
import { sleep } from 'utils/common';
|
2021-08-12 07:15:27 +00:00
|
|
|
import { getExportRecordFileUID } from 'utils/export';
|
2021-07-07 05:31:48 +00:00
|
|
|
import { getData, LS_KEYS, setData } from 'utils/storage/localStorage';
|
2021-07-07 09:20:59 +00:00
|
|
|
import constants from 'utils/strings/constants';
|
|
|
|
import { Label, Row, Value } from './Container';
|
2021-07-06 11:23:13 +00:00
|
|
|
import ExportFinished from './ExportFinished';
|
2021-07-06 09:20:05 +00:00
|
|
|
import ExportInit from './ExportInit';
|
|
|
|
import ExportInProgress from './ExportInProgress';
|
2021-07-07 09:20:59 +00:00
|
|
|
import FolderIcon from './icons/FolderIcon';
|
|
|
|
import InProgressIcon from './icons/InProgressIcon';
|
|
|
|
import MessageDialog from './MessageDialog';
|
2021-07-06 09:20:05 +00:00
|
|
|
|
2021-07-08 05:44:54 +00:00
|
|
|
const FolderIconWrapper = styled.div`
|
|
|
|
width: 15%;
|
|
|
|
margin-left: 10px;
|
|
|
|
cursor: pointer;
|
|
|
|
padding: 3px;
|
|
|
|
border: 1px solid #444;
|
2021-07-12 07:21:52 +00:00
|
|
|
border-radius:15%;
|
2021-07-08 05:44:54 +00:00
|
|
|
&:hover{
|
|
|
|
background-color:#444;
|
|
|
|
}
|
2021-07-18 16:20:47 +00:00
|
|
|
`;
|
|
|
|
|
|
|
|
const ExportFolderPathContainer =styled.span`
|
|
|
|
white-space: nowrap;
|
|
|
|
overflow: hidden;
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
width: 200px;
|
|
|
|
|
|
|
|
/* Beginning of string */
|
|
|
|
direction: rtl;
|
|
|
|
text-align: left;
|
|
|
|
`;
|
2021-07-06 09:20:05 +00:00
|
|
|
|
2021-07-07 05:31:48 +00:00
|
|
|
interface Props {
|
|
|
|
show: boolean
|
|
|
|
onHide: () => void
|
2021-07-07 07:45:55 +00:00
|
|
|
usage: string
|
|
|
|
}
|
2021-07-07 05:31:48 +00:00
|
|
|
export default function ExportModal(props: Props) {
|
2021-07-08 06:42:28 +00:00
|
|
|
const [exportStage, setExportStage] = useState(ExportStage.INIT);
|
2021-07-13 13:39:31 +00:00
|
|
|
const [exportFolder, setExportFolder] = useState('');
|
|
|
|
const [exportSize, setExportSize] = useState('');
|
2021-07-14 07:39:40 +00:00
|
|
|
const [exportProgress, setExportProgress] = useState<ExportProgress>({ current: 0, total: 0 });
|
|
|
|
const [exportStats, setExportStats] = useState<ExportStats>({ failed: 0, success: 0 });
|
2021-07-07 07:45:55 +00:00
|
|
|
const [lastExportTime, setLastExportTime] = useState(0);
|
2021-07-07 09:20:59 +00:00
|
|
|
|
2021-07-14 09:16:00 +00:00
|
|
|
// ====================
|
|
|
|
// SIDE EFFECTS
|
|
|
|
// ====================
|
2021-07-07 05:31:48 +00:00
|
|
|
useEffect(() => {
|
2021-07-13 13:39:31 +00:00
|
|
|
if (!isElectron()) {
|
|
|
|
return;
|
|
|
|
}
|
2021-07-14 05:52:27 +00:00
|
|
|
setExportFolder(getData(LS_KEYS.EXPORT)?.folder);
|
2021-07-13 13:39:31 +00:00
|
|
|
|
2021-07-09 03:35:33 +00:00
|
|
|
exportService.ElectronAPIs.registerStopExportListener(stopExport);
|
|
|
|
exportService.ElectronAPIs.registerPauseExportListener(pauseExport);
|
2021-07-14 05:52:27 +00:00
|
|
|
exportService.ElectronAPIs.registerResumeExportListener(resumeExport);
|
|
|
|
exportService.ElectronAPIs.registerRetryFailedExportListener(retryFailedExport);
|
2021-07-07 05:31:48 +00:00
|
|
|
}, []);
|
2021-07-14 09:16:00 +00:00
|
|
|
|
2021-07-07 07:45:55 +00:00
|
|
|
useEffect(() => {
|
2021-07-15 11:04:16 +00:00
|
|
|
if (!exportFolder) {
|
|
|
|
return;
|
|
|
|
}
|
2021-07-13 13:39:31 +00:00
|
|
|
const main = async () => {
|
|
|
|
const exportInfo = await exportService.getExportRecord();
|
|
|
|
setExportStage(exportInfo?.stage ?? ExportStage.INIT);
|
2021-07-14 07:39:40 +00:00
|
|
|
setLastExportTime(exportInfo?.lastAttemptTimestamp);
|
|
|
|
setExportProgress(exportInfo?.progress ?? { current: 0, total: 0 });
|
|
|
|
setExportStats({ success: exportInfo?.exportedFiles?.length ?? 0, failed: exportInfo?.failedFiles?.length ?? 0 });
|
2021-07-13 13:39:31 +00:00
|
|
|
if (exportInfo?.stage === ExportStage.INPROGRESS) {
|
2021-07-14 13:43:16 +00:00
|
|
|
resumeExport();
|
2021-07-13 13:39:31 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
main();
|
|
|
|
}, [exportFolder]);
|
|
|
|
|
2021-07-15 11:04:16 +00:00
|
|
|
useEffect(() => {
|
|
|
|
if (!props.show) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const main = async () => {
|
|
|
|
if (exportStage === ExportStage.FINISHED) {
|
|
|
|
const localFiles = await getLocalFiles();
|
|
|
|
const exportRecord = await exportService.getExportRecord();
|
|
|
|
const exportedFileCnt = exportRecord.exportedFiles.length;
|
|
|
|
const failedFilesCnt = exportRecord.failedFiles.length;
|
|
|
|
const syncedFilesCnt = localFiles.length;
|
|
|
|
if (syncedFilesCnt > exportedFileCnt + failedFilesCnt) {
|
|
|
|
updateExportProgress({ current: exportedFileCnt + failedFilesCnt, total: syncedFilesCnt });
|
|
|
|
const exportFileUIDs = new Set([...exportRecord.exportedFiles, ...exportRecord.failedFiles]);
|
2021-08-12 07:15:27 +00:00
|
|
|
const unExportedFiles = localFiles.filter((file) => !exportFileUIDs.has(getExportRecordFileUID(file)));
|
2021-07-15 11:04:16 +00:00
|
|
|
exportService.addFilesQueuedRecord(exportFolder, unExportedFiles);
|
|
|
|
updateExportStage(ExportStage.PAUSED);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
main();
|
|
|
|
}, [props.show]);
|
|
|
|
|
2021-07-07 09:20:59 +00:00
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
setExportSize(props.usage);
|
|
|
|
}, [props.usage]);
|
|
|
|
|
2021-07-14 09:16:00 +00:00
|
|
|
// =============
|
|
|
|
// STATE UPDATERS
|
|
|
|
// ==============
|
2021-07-14 05:52:27 +00:00
|
|
|
const updateExportFolder = (newFolder: string) => {
|
2021-07-07 05:31:48 +00:00
|
|
|
setExportFolder(newFolder);
|
2021-07-14 05:52:27 +00:00
|
|
|
setData(LS_KEYS.EXPORT, { folder: newFolder });
|
2021-07-07 05:31:48 +00:00
|
|
|
};
|
2021-07-14 09:16:00 +00:00
|
|
|
|
2021-07-14 05:52:27 +00:00
|
|
|
const updateExportStage = (newStage: ExportStage) => {
|
2021-07-09 03:48:19 +00:00
|
|
|
setExportStage(newStage);
|
2021-07-13 13:39:31 +00:00
|
|
|
exportService.updateExportRecord({ stage: newStage });
|
2021-07-07 07:45:55 +00:00
|
|
|
};
|
2021-07-14 09:16:00 +00:00
|
|
|
|
2021-07-14 05:52:27 +00:00
|
|
|
const updateExportTime = (newTime: number) => {
|
2021-07-07 07:45:55 +00:00
|
|
|
setLastExportTime(newTime);
|
2021-07-13 13:39:31 +00:00
|
|
|
exportService.updateExportRecord({ time: newTime });
|
2021-07-07 07:45:55 +00:00
|
|
|
};
|
|
|
|
|
2021-07-14 07:39:40 +00:00
|
|
|
const updateExportProgress = (newProgress: ExportProgress) => {
|
|
|
|
setExportProgress(newProgress);
|
|
|
|
exportService.updateExportRecord({ progress: newProgress });
|
2021-07-09 03:48:19 +00:00
|
|
|
};
|
|
|
|
|
2021-07-14 09:16:00 +00:00
|
|
|
// ======================
|
|
|
|
// HELPER FUNCTIONS
|
|
|
|
// =========================
|
|
|
|
|
|
|
|
const preExportRun = async () => {
|
2021-07-14 05:52:27 +00:00
|
|
|
const exportFolder = getData(LS_KEYS.EXPORT)?.folder;
|
2021-07-08 07:20:20 +00:00
|
|
|
if (!exportFolder) {
|
|
|
|
const folderSelected = await selectExportDirectory();
|
|
|
|
if (!folderSelected) {
|
|
|
|
// no-op as select folder aborted
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2021-07-08 06:42:28 +00:00
|
|
|
updateExportStage(ExportStage.INPROGRESS);
|
2021-07-14 09:16:00 +00:00
|
|
|
await sleep(100);
|
|
|
|
};
|
|
|
|
const postExportRun = async (paused: Boolean) => {
|
2021-07-14 07:21:32 +00:00
|
|
|
if (!paused) {
|
2021-07-14 05:52:27 +00:00
|
|
|
updateExportStage(ExportStage.FINISHED);
|
|
|
|
await sleep(100);
|
|
|
|
updateExportTime(Date.now());
|
2021-07-14 09:16:00 +00:00
|
|
|
syncExportStatsWithReport();
|
2021-07-14 05:52:27 +00:00
|
|
|
}
|
|
|
|
};
|
2021-07-14 09:16:00 +00:00
|
|
|
const startExport = async () => {
|
|
|
|
await preExportRun();
|
2021-07-14 07:39:40 +00:00
|
|
|
updateExportProgress({ current: 0, total: 0 });
|
2021-07-14 10:03:14 +00:00
|
|
|
const { paused } = await exportService.exportFiles(updateExportProgress, ExportType.NEW);
|
2021-07-14 09:16:00 +00:00
|
|
|
await postExportRun(paused);
|
|
|
|
};
|
|
|
|
|
|
|
|
const stopExport = async () => {
|
2021-07-14 13:43:16 +00:00
|
|
|
exportService.stopRunningExport();
|
|
|
|
postExportRun(false);
|
2021-07-14 05:52:27 +00:00
|
|
|
};
|
|
|
|
|
2021-07-14 09:16:00 +00:00
|
|
|
const pauseExport = () => {
|
|
|
|
updateExportStage(ExportStage.PAUSED);
|
|
|
|
exportService.pauseRunningExport();
|
2021-07-14 13:43:16 +00:00
|
|
|
postExportRun(true);
|
2021-07-14 09:16:00 +00:00
|
|
|
};
|
|
|
|
|
2021-07-14 05:52:27 +00:00
|
|
|
const resumeExport = async () => {
|
|
|
|
const exportRecord = await exportService.getExportRecord();
|
2021-07-14 09:16:00 +00:00
|
|
|
await preExportRun();
|
|
|
|
|
2021-07-14 07:39:40 +00:00
|
|
|
const pausedStageProgress = exportRecord.progress;
|
2021-07-15 11:04:16 +00:00
|
|
|
setExportProgress(pausedStageProgress);
|
2021-07-14 09:16:00 +00:00
|
|
|
|
2021-07-14 07:39:40 +00:00
|
|
|
const updateExportStatsWithOffset = ((progress: ExportProgress) => updateExportProgress(
|
2021-07-14 05:52:27 +00:00
|
|
|
{
|
2021-07-14 07:39:40 +00:00
|
|
|
current: pausedStageProgress.current + progress.current,
|
|
|
|
total: pausedStageProgress.current + progress.total,
|
2021-07-14 05:52:27 +00:00
|
|
|
},
|
|
|
|
));
|
2021-07-14 09:16:00 +00:00
|
|
|
const { paused } = await exportService.exportFiles(updateExportStatsWithOffset, ExportType.PENDING);
|
2021-07-14 05:52:27 +00:00
|
|
|
|
2021-07-14 09:16:00 +00:00
|
|
|
await postExportRun(paused);
|
2021-07-07 05:31:48 +00:00
|
|
|
};
|
2021-07-07 09:20:59 +00:00
|
|
|
|
2021-07-14 09:16:00 +00:00
|
|
|
const retryFailedExport = async () => {
|
|
|
|
await preExportRun();
|
|
|
|
updateExportProgress({ current: 0, total: exportStats.failed });
|
|
|
|
|
2021-07-14 10:03:14 +00:00
|
|
|
const { paused } = await exportService.exportFiles(updateExportProgress, ExportType.RETRY_FAILED);
|
2021-07-14 09:16:00 +00:00
|
|
|
await postExportRun(paused);
|
2021-07-07 09:20:59 +00:00
|
|
|
};
|
|
|
|
|
2021-07-14 09:16:00 +00:00
|
|
|
const syncExportStatsWithReport = async () => {
|
|
|
|
const exportRecord = await exportService.getExportRecord();
|
2021-07-13 13:39:31 +00:00
|
|
|
const failed = exportRecord?.failedFiles?.length ?? 0;
|
|
|
|
const success = exportRecord?.exportedFiles?.length ?? 0;
|
2021-07-14 07:39:40 +00:00
|
|
|
setExportStats({ failed, success });
|
2021-07-13 13:39:31 +00:00
|
|
|
};
|
|
|
|
|
2021-07-14 09:16:00 +00:00
|
|
|
const selectExportDirectory = async () => {
|
|
|
|
const newFolder = await exportService.selectExportDirectory();
|
|
|
|
if (newFolder) {
|
|
|
|
updateExportFolder(newFolder);
|
|
|
|
return true;
|
2021-07-08 06:42:28 +00:00
|
|
|
} else {
|
2021-07-14 09:16:00 +00:00
|
|
|
return false;
|
2021-07-08 06:42:28 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-07-07 09:20:59 +00:00
|
|
|
const ExportDynamicState = () => {
|
2021-07-08 06:42:28 +00:00
|
|
|
switch (exportStage) {
|
|
|
|
case ExportStage.INIT:
|
2021-07-07 09:20:59 +00:00
|
|
|
return (
|
|
|
|
<ExportInit {...props}
|
|
|
|
exportFolder={exportFolder}
|
|
|
|
exportSize={exportSize}
|
|
|
|
updateExportFolder={updateExportFolder}
|
2021-07-08 07:20:20 +00:00
|
|
|
startExport={startExport}
|
2021-07-07 09:20:59 +00:00
|
|
|
selectExportDirectory={selectExportDirectory}
|
|
|
|
/>
|
|
|
|
);
|
2021-07-08 06:42:28 +00:00
|
|
|
case ExportStage.INPROGRESS:
|
|
|
|
case ExportStage.PAUSED:
|
2021-07-07 09:20:59 +00:00
|
|
|
return (
|
|
|
|
<ExportInProgress {...props}
|
|
|
|
exportFolder={exportFolder}
|
|
|
|
exportSize={exportSize}
|
2021-07-08 06:42:28 +00:00
|
|
|
exportStage={exportStage}
|
2021-07-14 07:39:40 +00:00
|
|
|
exportProgress={exportProgress}
|
2021-07-14 05:52:27 +00:00
|
|
|
resumeExport={resumeExport}
|
2021-07-09 03:35:33 +00:00
|
|
|
cancelExport={stopExport}
|
2021-07-08 06:42:28 +00:00
|
|
|
pauseExport={pauseExport}
|
2021-07-07 09:20:59 +00:00
|
|
|
/>
|
|
|
|
);
|
2021-07-08 06:42:28 +00:00
|
|
|
case ExportStage.FINISHED:
|
2021-07-07 09:20:59 +00:00
|
|
|
return (
|
|
|
|
<ExportFinished
|
|
|
|
{...props}
|
|
|
|
exportFolder={exportFolder}
|
|
|
|
exportSize={exportSize}
|
|
|
|
updateExportFolder={updateExportFolder}
|
|
|
|
lastExportTime={lastExportTime}
|
|
|
|
exportStats={exportStats}
|
|
|
|
exportFiles={startExport}
|
2021-07-14 09:16:00 +00:00
|
|
|
retryFailed={retryFailedExport}
|
2021-07-07 09:20:59 +00:00
|
|
|
/>
|
|
|
|
);
|
|
|
|
|
|
|
|
default: return (<></>);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
return (
|
|
|
|
<MessageDialog
|
|
|
|
show={props.show}
|
|
|
|
onHide={props.onHide}
|
|
|
|
attributes={{
|
|
|
|
title: constants.EXPORT_DATA,
|
|
|
|
}}
|
|
|
|
>
|
2021-08-04 10:21:37 +00:00
|
|
|
<div style={{ borderBottom: '1px solid #444', marginBottom: '20px', padding: '0 5%', width: '450px' }}>
|
2021-07-07 09:20:59 +00:00
|
|
|
<Row>
|
|
|
|
<Label width="40%">{constants.DESTINATION}</Label>
|
|
|
|
<Value width="60%">
|
2021-08-12 15:40:02 +00:00
|
|
|
{!exportFolder
|
|
|
|
? (<Button variant={'outline-success'} size={'sm'} onClick={selectExportDirectory}>{constants.SELECT_FOLDER}</Button>)
|
|
|
|
: (<>
|
2021-07-18 16:20:47 +00:00
|
|
|
{/* <span style={{ overflow: 'hidden', direction: 'rtl', height: '1.5rem', width: '90%', whiteSpace: 'nowrap' }}> */}
|
|
|
|
<ExportFolderPathContainer>
|
2021-07-07 09:20:59 +00:00
|
|
|
{exportFolder}
|
2021-07-18 16:20:47 +00:00
|
|
|
</ExportFolderPathContainer>
|
|
|
|
{/* </span> */}
|
2021-07-13 13:39:31 +00:00
|
|
|
{(exportStage === ExportStage.FINISHED || exportStage === ExportStage.INIT) && (
|
|
|
|
<FolderIconWrapper onClick={selectExportDirectory} >
|
|
|
|
<FolderIcon />
|
|
|
|
</FolderIconWrapper>
|
|
|
|
)}
|
2021-07-07 09:20:59 +00:00
|
|
|
</>)
|
|
|
|
}
|
|
|
|
</Value>
|
|
|
|
</Row>
|
|
|
|
<Row>
|
2021-08-04 10:21:37 +00:00
|
|
|
<Label width="40%">{constants.TOTAL_EXPORT_SIZE} </Label><Value width="60%">{exportSize ? `${exportSize}` : <InProgressIcon />}</Value>
|
2021-07-07 09:20:59 +00:00
|
|
|
</Row>
|
|
|
|
</div>
|
|
|
|
<ExportDynamicState />
|
|
|
|
</MessageDialog >
|
|
|
|
);
|
2021-07-06 09:20:05 +00:00
|
|
|
}
|