2023-07-18 15:58:06 +02:00
|
|
|
import * as React from 'react';
|
|
|
|
import { Text, Alert, View } from 'react-native';
|
|
|
|
import { _ } from '@joplin/lib/locale';
|
2023-07-27 17:05:56 +02:00
|
|
|
import Logger from '@joplin/utils/Logger';
|
2023-07-18 15:58:06 +02:00
|
|
|
import { ProgressBar } from 'react-native-paper';
|
|
|
|
import { FunctionComponent, useCallback, useState } from 'react';
|
|
|
|
import shim from '@joplin/lib/shim';
|
|
|
|
import { join } from 'path';
|
|
|
|
import Share from 'react-native-share';
|
2023-11-09 21:19:08 +02:00
|
|
|
import exportAllFolders, { makeExportCacheDirectory } from './utils/exportAllFolders';
|
2023-07-18 15:58:06 +02:00
|
|
|
import { ExportProgressState } from '@joplin/lib/services/interop/types';
|
|
|
|
import { ConfigScreenStyles } from '../configScreenStyles';
|
2023-11-09 21:19:08 +02:00
|
|
|
import SettingsButton from '../SettingsButton';
|
2023-07-18 15:58:06 +02:00
|
|
|
|
|
|
|
const logger = Logger.create('NoteExportButton');
|
|
|
|
|
|
|
|
interface Props {
|
|
|
|
styles: ConfigScreenStyles;
|
|
|
|
}
|
|
|
|
|
|
|
|
enum ExportStatus {
|
|
|
|
NotStarted,
|
|
|
|
Exporting,
|
|
|
|
Exported,
|
|
|
|
}
|
|
|
|
|
2023-11-16 14:17:03 +02:00
|
|
|
export const exportButtonTitle = () => _('Export all notes as JEX');
|
|
|
|
export const exportButtonDescription = () => _('Share a copy of all notes in a file format that can be imported by Joplin on a computer.');
|
|
|
|
|
2023-07-18 15:58:06 +02:00
|
|
|
const NoteExportButton: FunctionComponent<Props> = props => {
|
|
|
|
const [exportStatus, setExportStatus] = useState<ExportStatus>(ExportStatus.NotStarted);
|
|
|
|
const [exportProgress, setExportProgress] = useState<number|undefined>(0);
|
|
|
|
const [warnings, setWarnings] = useState<string>('');
|
|
|
|
|
|
|
|
const startExport = useCallback(async () => {
|
|
|
|
// Don't run multiple exports at the same time.
|
|
|
|
if (exportStatus === ExportStatus.Exporting) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
setExportStatus(ExportStatus.Exporting);
|
|
|
|
const exportTargetPath = join(await makeExportCacheDirectory(), 'jex-export.jex');
|
|
|
|
logger.info(`Exporting all folders to path ${exportTargetPath}`);
|
|
|
|
|
|
|
|
try {
|
|
|
|
// Initially, undetermined progress
|
|
|
|
setExportProgress(undefined);
|
|
|
|
|
|
|
|
const status = await exportAllFolders(exportTargetPath, (status, progress) => {
|
|
|
|
if (progress !== null) {
|
|
|
|
setExportProgress(progress);
|
|
|
|
} else if (status === ExportProgressState.Closing || status === ExportProgressState.QueuingItems) {
|
|
|
|
// We don't have a numeric progress value and the closing/queuing state may take a while.
|
|
|
|
// Set a special progress value:
|
|
|
|
setExportProgress(undefined);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
setExportStatus(ExportStatus.Exported);
|
|
|
|
setWarnings(status.warnings.join('\n'));
|
|
|
|
|
|
|
|
await Share.open({
|
|
|
|
type: 'application/jex',
|
|
|
|
filename: 'export.jex',
|
|
|
|
url: `file://${exportTargetPath}`,
|
|
|
|
failOnCancel: false,
|
|
|
|
});
|
|
|
|
} catch (e) {
|
|
|
|
logger.error('Unable to export:', e);
|
|
|
|
|
|
|
|
// Display a message to the user (e.g. in the case where the user is out of disk space).
|
|
|
|
Alert.alert(_('Error'), _('Unable to export or share data. Reason: %s', e.toString()));
|
|
|
|
setExportStatus(ExportStatus.NotStarted);
|
|
|
|
} finally {
|
|
|
|
await shim.fsDriver().remove(exportTargetPath);
|
|
|
|
}
|
|
|
|
}, [exportStatus]);
|
|
|
|
|
|
|
|
if (exportStatus === ExportStatus.NotStarted || exportStatus === ExportStatus.Exporting) {
|
|
|
|
const progressComponent = (
|
|
|
|
<ProgressBar
|
|
|
|
visible={exportStatus === ExportStatus.Exporting}
|
|
|
|
indeterminate={exportProgress === undefined}
|
|
|
|
progress={exportProgress}/>
|
|
|
|
);
|
|
|
|
|
|
|
|
const startOrCancelExportButton = (
|
2023-11-09 21:19:08 +02:00
|
|
|
<SettingsButton
|
2023-11-16 14:17:03 +02:00
|
|
|
title={exportStatus === ExportStatus.Exporting ? _('Exporting...') : exportButtonTitle()}
|
2023-07-18 15:58:06 +02:00
|
|
|
disabled={exportStatus === ExportStatus.Exporting}
|
2023-11-16 14:17:03 +02:00
|
|
|
description={exportStatus === ExportStatus.NotStarted ? exportButtonDescription() : null}
|
2023-07-18 15:58:06 +02:00
|
|
|
statusComponent={progressComponent}
|
|
|
|
clickHandler={startExport}
|
|
|
|
styles={props.styles}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
|
|
|
|
return startOrCancelExportButton;
|
|
|
|
} else {
|
|
|
|
const warningComponent = (
|
2023-11-09 21:19:08 +02:00
|
|
|
<Text style={props.styles.styleSheet.warningText}>
|
2023-07-18 15:58:06 +02:00
|
|
|
{_('Warnings:\n%s', warnings)}
|
|
|
|
</Text>
|
|
|
|
);
|
|
|
|
|
|
|
|
const exportSummary = (
|
2023-11-09 21:19:08 +02:00
|
|
|
<View style={props.styles.styleSheet.settingContainer}>
|
|
|
|
<Text style={props.styles.styleSheet.descriptionText}>{_('Exported successfully!')}</Text>
|
2023-07-18 15:58:06 +02:00
|
|
|
{warnings.length > 0 ? warningComponent : null}
|
|
|
|
</View>
|
|
|
|
);
|
|
|
|
return exportSummary;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
export default NoteExportButton;
|