Merge pull request #106 from ente-io/improve-export

Improve export
This commit is contained in:
Abhinav-grd 2021-07-18 22:04:01 +05:30 committed by GitHub
commit 063797df55
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 52 additions and 24 deletions

View file

@ -32,12 +32,13 @@ export default function ExportFinished(props: Props) {
<Label width="60%">{constants.SUCCESSFULLY_EXPORTED_FILES}</Label> <Label width="60%">{constants.SUCCESSFULLY_EXPORTED_FILES}</Label>
<Value width="35%"><ComfySpan>{props.exportStats.success} / {totalFiles}</ComfySpan></Value> <Value width="35%"><ComfySpan>{props.exportStats.success} / {totalFiles}</ComfySpan></Value>
</Row> </Row>
{props.exportStats.failed>0 &&
<Row> <Row>
<Label width="60%">{constants.FAILED_EXPORTED_FILES}</Label> <Label width="60%">{constants.FAILED_EXPORTED_FILES}</Label>
<Value width="35%"> <Value width="35%">
<ComfySpan>{props.exportStats.failed} / {totalFiles}</ComfySpan> <ComfySpan>{props.exportStats.failed} / {totalFiles}</ComfySpan>
</Value> </Value>
</Row> </Row>}
</div> </div>
<div style={{ width: '100%', display: 'flex', justifyContent: 'space-around' }}> <div style={{ width: '100%', display: 'flex', justifyContent: 'space-around' }}>
<Button block variant={'outline-secondary'} onClick={props.onHide}>{constants.CLOSE}</Button> <Button block variant={'outline-secondary'} onClick={props.onHide}>{constants.CLOSE}</Button>

View file

@ -26,7 +26,18 @@ const FolderIconWrapper = styled.div`
&:hover{ &:hover{
background-color:#444; background-color:#444;
} }
`; `;
const ExportFolderPathContainer =styled.span`
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 200px;
/* Beginning of string */
direction: rtl;
text-align: left;
`;
interface Props { interface Props {
show: boolean show: boolean
@ -88,7 +99,6 @@ export default function ExportModal(props: Props) {
updateExportProgress({ current: exportedFileCnt + failedFilesCnt, total: syncedFilesCnt }); updateExportProgress({ current: exportedFileCnt + failedFilesCnt, total: syncedFilesCnt });
const exportFileUIDs = new Set([...exportRecord.exportedFiles, ...exportRecord.failedFiles]); const exportFileUIDs = new Set([...exportRecord.exportedFiles, ...exportRecord.failedFiles]);
const unExportedFiles = localFiles.filter((file) => !exportFileUIDs.has(getFileUID(file))); const unExportedFiles = localFiles.filter((file) => !exportFileUIDs.has(getFileUID(file)));
console.log(exportedFileCnt + failedFilesCnt + unExportedFiles.length, syncedFilesCnt);
exportService.addFilesQueuedRecord(exportFolder, unExportedFiles); exportService.addFilesQueuedRecord(exportFolder, unExportedFiles);
updateExportStage(ExportStage.PAUSED); updateExportStage(ExportStage.PAUSED);
} }
@ -266,11 +276,13 @@ export default function ExportModal(props: Props) {
<Label width="40%">{constants.DESTINATION}</Label> <Label width="40%">{constants.DESTINATION}</Label>
<Value width="60%"> <Value width="60%">
{!exportFolder ? {!exportFolder ?
(<Button variant={'outline-success'} onClick={selectExportDirectory}>{constants.SELECT_FOLDER}</Button>) : (<Button variant={'outline-success'} size={'sm'} onClick={selectExportDirectory}>{constants.SELECT_FOLDER}</Button>) :
(<> (<>
<span style={{ overflow: 'hidden', direction: 'rtl', height: '1.5rem', width: '90%', whiteSpace: 'nowrap' }}> {/* <span style={{ overflow: 'hidden', direction: 'rtl', height: '1.5rem', width: '90%', whiteSpace: 'nowrap' }}> */}
<ExportFolderPathContainer>
{exportFolder} {exportFolder}
</span> </ExportFolderPathContainer>
{/* </span> */}
{(exportStage === ExportStage.FINISHED || exportStage === ExportStage.INIT) && ( {(exportStage === ExportStage.FINISHED || exportStage === ExportStage.INIT) && (
<FolderIconWrapper onClick={selectExportDirectory} > <FolderIconWrapper onClick={selectExportDirectory} >
<FolderIcon /> <FolderIcon />

View file

@ -2,7 +2,7 @@ import { getEndpoint } from 'utils/common/apiUtil';
import { getStripePublishableKey, getToken } from 'utils/common/key'; import { getStripePublishableKey, getToken } from 'utils/common/key';
import { checkConnectivity, runningInBrowser } from 'utils/common/'; import { checkConnectivity, runningInBrowser } from 'utils/common/';
import { setData, LS_KEYS } from 'utils/storage/localStorage'; import { setData, LS_KEYS } from 'utils/storage/localStorage';
import { convertBytesToGBs } from 'utils/billingUtil'; import { convertToHumanReadable } from 'utils/billingUtil';
import { loadStripe, Stripe } from '@stripe/stripe-js'; import { loadStripe, Stripe } from '@stripe/stripe-js';
import { SUBSCRIPTION_VERIFICATION_ERROR } from 'utils/common/errorUtil'; import { SUBSCRIPTION_VERIFICATION_ERROR } from 'utils/common/errorUtil';
import HTTPService from './HTTPService'; import HTTPService from './HTTPService';
@ -242,7 +242,7 @@ class billingService {
'X-Auth-Token': getToken(), 'X-Auth-Token': getToken(),
}, },
); );
return convertBytesToGBs(response.data.usage); return convertToHumanReadable(response.data.usage);
} catch (e) { } catch (e) {
logError(e, 'error getting usage'); logError(e, 'error getting usage');
} }

View file

@ -49,6 +49,10 @@ export enum ExportType {
PENDING, PENDING,
RETRY_FAILED RETRY_FAILED
} }
const ExportRecordFileName='export_status.json';
const MetadataFolderName='metadata';
class ExportService { class ExportService {
ElectronAPIs: any; ElectronAPIs: any;
@ -124,6 +128,9 @@ class ExportService {
await this.ElectronAPIs.checkExistsAndCreateCollectionDir( await this.ElectronAPIs.checkExistsAndCreateCollectionDir(
collectionFolderPath, collectionFolderPath,
); );
await this.ElectronAPIs.checkExistsAndCreateCollectionDir(
`${collectionFolderPath}/${MetadataFolderName}`,
);
collectionIDMap.set(collection.id, collectionFolderPath); collectionIDMap.set(collection.id, collectionFolderPath);
} }
for (const [index, file] of files.entries()) { for (const [index, file] of files.entries()) {
@ -137,12 +144,9 @@ class ExportService {
} }
break; break;
} }
const uid = `${file.id}_${this.sanitizeName( const collectionPath = collectionIDMap.get(file.collectionID);
file.metadata.title,
)}`;
const filePath = `${collectionIDMap.get(file.collectionID)}/${uid}`;
try { try {
await this.downloadAndSave(file, filePath); await this.downloadAndSave(file, collectionPath);
await this.addFileExportRecord(dir, file, RecordType.SUCCESS); await this.addFileExportRecord(dir, file, RecordType.SUCCESS);
} catch (e) { } catch (e) {
await this.addFileExportRecord(dir, file, RecordType.FAILED); await this.addFileExportRecord(dir, file, RecordType.FAILED);
@ -222,7 +226,7 @@ class ExportService {
} }
const exportRecord = await this.getExportRecord(folder); const exportRecord = await this.getExportRecord(folder);
const newRecord = { ...exportRecord, ...newData }; const newRecord = { ...exportRecord, ...newData };
await this.ElectronAPIs.setExportRecord(folder, JSON.stringify(newRecord, null, 2)); await this.ElectronAPIs.setExportRecord(`${folder}/${ExportRecordFileName}`, JSON.stringify(newRecord, null, 2));
} catch (e) { } catch (e) {
logError(e, 'error updating Export Record'); logError(e, 'error updating Export Record');
} }
@ -235,7 +239,7 @@ class ExportService {
if (!folder) { if (!folder) {
folder = getData(LS_KEYS.EXPORT)?.folder; folder = getData(LS_KEYS.EXPORT)?.folder;
} }
const recordFile = await this.ElectronAPIs.getExportRecord(folder); const recordFile = await this.ElectronAPIs.getExportRecord(`${folder}/${ExportRecordFileName}`);
if (recordFile) { if (recordFile) {
return JSON.parse(recordFile); return JSON.parse(recordFile);
} else { } else {
@ -246,11 +250,14 @@ class ExportService {
} }
} }
async downloadAndSave(file: File, path) { async downloadAndSave(file: File, collectionPath:string) {
const uid = `${file.id}_${this.sanitizeName(
file.metadata.title,
)}`;
const fileStream = await retryPromise(downloadManager.downloadFile(file)); const fileStream = await retryPromise(downloadManager.downloadFile(file));
this.ElectronAPIs.saveStreamToDisk(path, fileStream); this.ElectronAPIs.saveStreamToDisk(`${collectionPath}/${uid}`, fileStream);
this.ElectronAPIs.saveFileToDisk( this.ElectronAPIs.saveFileToDisk(
`${path}.json`, `${collectionPath}/${MetadataFolderName}/${uid}.json`,
JSON.stringify(file.metadata, null, 2), JSON.stringify(file.metadata, null, 2),
); );
} }

View file

@ -16,6 +16,14 @@ const STRIPE = 'stripe';
export function convertBytesToGBs(bytes, precision?): string { export function convertBytesToGBs(bytes, precision?): string {
return (bytes / (1024 * 1024 * 1024)).toFixed(precision ?? 2); return (bytes / (1024 * 1024 * 1024)).toFixed(precision ?? 2);
} }
export function convertToHumanReadable(bytes:number, precision=2): string {
const i = Math.floor(Math.log(bytes) / Math.log(1024));
const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
return (bytes / Math.pow(1024, i)).toFixed(precision)+ ' ' + sizes[i];
}
export function hasPaidSubscription(subscription?: Subscription) { export function hasPaidSubscription(subscription?: Subscription) {
subscription = subscription ?? getUserSubscription(); subscription = subscription ?? getUserSubscription();
return ( return (

View file

@ -2,7 +2,7 @@ import { ExportRecord } from 'services/exportService';
import { File } from 'services/fileService'; import { File } from 'services/fileService';
export const getFileUID = (file: File) => `${file.id}_${file.collectionID}`; export const getFileUID = (file: File) => `${file.id}_${file.collectionID}_${file.updationTime}`;
export const getExportPendingFiles = async (allFiles: File[], exportRecord: ExportRecord) => { export const getExportPendingFiles = async (allFiles: File[], exportRecord: ExportRecord) => {

View file

@ -260,7 +260,7 @@ const englishConstants = {
<p> <p>
you have used {usage} you have used {usage}
{' '} {' '}
GB out of your {quota} out of your {quota}
{' '} {' '}
GB quota GB quota
</p> </p>
@ -439,10 +439,10 @@ const englishConstants = {
RESUME: 'resume', RESUME: 'resume',
MINIMIZE: 'minimize', MINIMIZE: 'minimize',
LAST_EXPORT_TIME: 'last export time', LAST_EXPORT_TIME: 'last export time',
SUCCESSFULLY_EXPORTED_FILES: 'successfully exported files', SUCCESSFULLY_EXPORTED_FILES: 'successful exports',
FAILED_EXPORTED_FILES: 'failed file export', FAILED_EXPORTED_FILES: 'failed exports',
EXPORT_AGAIN: 'export new data ', EXPORT_AGAIN: 'resync',
RETRY_EXPORT_: 'retry failed files ', RETRY_EXPORT_: 'retry failed exports',
LOCAL_STORAGE_NOT_ACCESSIBLE: 'local storage not accessible', LOCAL_STORAGE_NOT_ACCESSIBLE: 'local storage not accessible',
LOCAL_STORAGE_NOT_ACCESSIBLE_MESSAGE: 'your browser or an addon is blocking ente from saving data into local storage. please try loading this page after switching your browsing mode.', LOCAL_STORAGE_NOT_ACCESSIBLE_MESSAGE: 'your browser or an addon is blocking ente from saving data into local storage. please try loading this page after switching your browsing mode.',
RETRY: 'retry', RETRY: 'retry',