diff --git a/src/script/components/HistoryExport/HistoryExport.tsx b/src/script/components/HistoryExport/HistoryExport.tsx new file mode 100644 index 00000000000..5fff62ee0b4 --- /dev/null +++ b/src/script/components/HistoryExport/HistoryExport.tsx @@ -0,0 +1,273 @@ +/* + * Wire + * Copyright (C) 2022 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import {FC, useContext, useEffect, useState} from 'react'; + +import {container} from 'tsyringe'; + +import {LoadingBar} from 'Components/LoadingBar/LoadingBar'; +import {PrimaryModal} from 'Components/Modals/PrimaryModal'; +import {ClientState} from 'src/script/client/ClientState'; +import {User} from 'src/script/entity/User'; +import {ContentState} from 'src/script/page/useAppState'; +import {t} from 'Util/LocalizerUtil'; +import {getLogger} from 'Util/Logger'; +import {getCurrentDate} from 'Util/TimeUtil'; +import {downloadBlob} from 'Util/util'; + +import {CancelError} from '../../backup/Error'; +import {Config} from '../../Config'; +import {RootContext} from '../../page/RootProvider'; + +enum ExportState { + COMPRESSING = 'ExportState.STATE.COMPRESSING', + DONE = 'ExportState.STATE.DONE', + EXPORTING = 'ExportState.STATE.EXPORTING', + PREPARING = 'ExportState.STATE.PREPARING', +} + +export const CONFIG = { + FILE_EXTENSION: 'desktop_wbu', +}; + +interface HistoryExportProps { + switchContent: (contentState: ContentState) => void; + readonly user: User; + readonly clientState?: ClientState; +} + +const HistoryExport: FC = ({switchContent, user, clientState = container.resolve(ClientState)}) => { + const logger = getLogger('HistoryExport'); + + const [historyState, setHistoryState] = useState(ExportState.PREPARING); + const [hasError, setHasError] = useState(false); + + const [numberOfRecords, setNumberOfRecords] = useState(0); + const [numberOfProcessedRecords, setNumberOfProcessedRecords] = useState(0); + + const [archiveBlob, setArchiveBlob] = useState(null); + + const mainViewModel = useContext(RootContext); + + useEffect(() => { + exportHistory(); + }, []); + + if (!mainViewModel) { + return null; + } + + const {content: contentViewModel} = mainViewModel; + const backupRepository = contentViewModel.repositories.backup; + + const loadingProgress = Math.floor((numberOfProcessedRecords / numberOfRecords) * 100); + + const isPreparing = !hasError && historyState === ExportState.PREPARING; + const isExporting = !hasError && [ExportState.EXPORTING, ExportState.COMPRESSING].includes(historyState); + const isDone = !hasError && historyState === ExportState.DONE; + + const replacements = { + processed: numberOfProcessedRecords.toString(), + progress: loadingProgress.toString(), + total: numberOfRecords.toString(), + }; + + const historyMessages: Partial> = { + [ExportState.PREPARING]: t('backupExportProgressHeadline'), + [ExportState.EXPORTING]: t('backupExportProgressSecondary', replacements), + [ExportState.COMPRESSING]: t('backupExportProgressCompressing'), + }; + + const loadingMessage = historyMessages?.[historyState] || ''; + + const dismissExport = () => { + switchContent(ContentState.PREFERENCES_ACCOUNT); + }; + + const onProgress = (processedNumber: number) => { + setHistoryState(ExportState.EXPORTING); + setNumberOfProcessedRecords(prevState => prevState + processedNumber); + }; + + const onSuccess = (archiveBlob: Blob) => { + setHistoryState(ExportState.DONE); + setHasError(false); + setArchiveBlob(archiveBlob); + }; + + const onError = (error: Error) => { + if (error instanceof CancelError) { + logger.log('History export was cancelled'); + dismissExport(); + + return; + } + + setHasError(true); + logger.error(`Failed to export history: ${error.message}`, error); + }; + + const downloadArchiveFile = () => { + const userName = user.username(); + const fileExtension = CONFIG.FILE_EXTENSION; + const sanitizedBrandName = Config.getConfig().BRAND_NAME.replace(/[^A-Za-z0-9_]/g, ''); + const filename = `${sanitizedBrandName}-${userName}-Backup_${getCurrentDate()}.${fileExtension}`; + + dismissExport(); + + if (archiveBlob) { + downloadBlob(archiveBlob, filename, 'application/octet-stream'); + } + }; + + const onCancel = () => backupRepository.cancelAction(); + + const getBackUpPassword = (): Promise => { + return new Promise(resolve => { + PrimaryModal.show(PrimaryModal.type.PASSWORD_ADVANCED_SECURITY, { + primaryAction: { + action: async (password: string) => { + resolve(password); + }, + text: t('backupEncryptionModalAction'), + }, + secondaryAction: [ + { + action: () => { + resolve(''); + dismissExport(); + }, + text: t('backupEncryptionModalCloseBtn'), + }, + ], + passwordOptional: true, + text: { + closeBtnLabel: t('backupEncryptionModalCloseBtn'), + input: t('backupEncryptionModalPlaceholder'), + message: t('backupEncryptionModalMessage'), + title: t('backupEncryptionModalTitle'), + }, + }); + }); + }; + + const exportHistory = async () => { + const password = await getBackUpPassword(); + setHistoryState(ExportState.PREPARING); + setHasError(false); + + try { + const numberOfRecords = await backupRepository.getBackupInitData(); + logger.log(`Exporting '${numberOfRecords}' records from history`); + + setNumberOfRecords(numberOfRecords); + setNumberOfProcessedRecords(0); + + const archiveBlob = await backupRepository.generateHistory( + user, + clientState.currentClient().id, + onProgress, + password, + ); + + onSuccess(archiveBlob); + logger.log(`Completed export of '${numberOfRecords}' records from history`); + } catch (error) { + onError(error as Error); + } + }; + + return ( +
+ {isPreparing && } + + {isExporting && ( + <> + + + + + )} + + {isDone && ( + <> +
+

+ {t('backupExportSuccessHeadline')} +

+ +

+ {t('backupExportSuccessSecondary')} +

+ +
+ +
+
+ + + + )} + + {hasError && ( +
+

+ {t('backupExportGenericErrorHeadline')} +

+ +

+ {t('backupExportGenericErrorSecondary')} +

+ +
+ + + +
+
+ )} +
+ ); +}; + +export {HistoryExport}; diff --git a/src/script/components/HistoryExport/index.tsx b/src/script/components/HistoryExport/index.tsx index 5fff62ee0b4..31f58503b4f 100644 --- a/src/script/components/HistoryExport/index.tsx +++ b/src/script/components/HistoryExport/index.tsx @@ -1,6 +1,6 @@ /* * Wire - * Copyright (C) 2022 Wire Swiss GmbH + * Copyright (C) 2023 Wire Swiss GmbH * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,257 +17,4 @@ * */ -import {FC, useContext, useEffect, useState} from 'react'; - -import {container} from 'tsyringe'; - -import {LoadingBar} from 'Components/LoadingBar/LoadingBar'; -import {PrimaryModal} from 'Components/Modals/PrimaryModal'; -import {ClientState} from 'src/script/client/ClientState'; -import {User} from 'src/script/entity/User'; -import {ContentState} from 'src/script/page/useAppState'; -import {t} from 'Util/LocalizerUtil'; -import {getLogger} from 'Util/Logger'; -import {getCurrentDate} from 'Util/TimeUtil'; -import {downloadBlob} from 'Util/util'; - -import {CancelError} from '../../backup/Error'; -import {Config} from '../../Config'; -import {RootContext} from '../../page/RootProvider'; - -enum ExportState { - COMPRESSING = 'ExportState.STATE.COMPRESSING', - DONE = 'ExportState.STATE.DONE', - EXPORTING = 'ExportState.STATE.EXPORTING', - PREPARING = 'ExportState.STATE.PREPARING', -} - -export const CONFIG = { - FILE_EXTENSION: 'desktop_wbu', -}; - -interface HistoryExportProps { - switchContent: (contentState: ContentState) => void; - readonly user: User; - readonly clientState?: ClientState; -} - -const HistoryExport: FC = ({switchContent, user, clientState = container.resolve(ClientState)}) => { - const logger = getLogger('HistoryExport'); - - const [historyState, setHistoryState] = useState(ExportState.PREPARING); - const [hasError, setHasError] = useState(false); - - const [numberOfRecords, setNumberOfRecords] = useState(0); - const [numberOfProcessedRecords, setNumberOfProcessedRecords] = useState(0); - - const [archiveBlob, setArchiveBlob] = useState(null); - - const mainViewModel = useContext(RootContext); - - useEffect(() => { - exportHistory(); - }, []); - - if (!mainViewModel) { - return null; - } - - const {content: contentViewModel} = mainViewModel; - const backupRepository = contentViewModel.repositories.backup; - - const loadingProgress = Math.floor((numberOfProcessedRecords / numberOfRecords) * 100); - - const isPreparing = !hasError && historyState === ExportState.PREPARING; - const isExporting = !hasError && [ExportState.EXPORTING, ExportState.COMPRESSING].includes(historyState); - const isDone = !hasError && historyState === ExportState.DONE; - - const replacements = { - processed: numberOfProcessedRecords.toString(), - progress: loadingProgress.toString(), - total: numberOfRecords.toString(), - }; - - const historyMessages: Partial> = { - [ExportState.PREPARING]: t('backupExportProgressHeadline'), - [ExportState.EXPORTING]: t('backupExportProgressSecondary', replacements), - [ExportState.COMPRESSING]: t('backupExportProgressCompressing'), - }; - - const loadingMessage = historyMessages?.[historyState] || ''; - - const dismissExport = () => { - switchContent(ContentState.PREFERENCES_ACCOUNT); - }; - - const onProgress = (processedNumber: number) => { - setHistoryState(ExportState.EXPORTING); - setNumberOfProcessedRecords(prevState => prevState + processedNumber); - }; - - const onSuccess = (archiveBlob: Blob) => { - setHistoryState(ExportState.DONE); - setHasError(false); - setArchiveBlob(archiveBlob); - }; - - const onError = (error: Error) => { - if (error instanceof CancelError) { - logger.log('History export was cancelled'); - dismissExport(); - - return; - } - - setHasError(true); - logger.error(`Failed to export history: ${error.message}`, error); - }; - - const downloadArchiveFile = () => { - const userName = user.username(); - const fileExtension = CONFIG.FILE_EXTENSION; - const sanitizedBrandName = Config.getConfig().BRAND_NAME.replace(/[^A-Za-z0-9_]/g, ''); - const filename = `${sanitizedBrandName}-${userName}-Backup_${getCurrentDate()}.${fileExtension}`; - - dismissExport(); - - if (archiveBlob) { - downloadBlob(archiveBlob, filename, 'application/octet-stream'); - } - }; - - const onCancel = () => backupRepository.cancelAction(); - - const getBackUpPassword = (): Promise => { - return new Promise(resolve => { - PrimaryModal.show(PrimaryModal.type.PASSWORD_ADVANCED_SECURITY, { - primaryAction: { - action: async (password: string) => { - resolve(password); - }, - text: t('backupEncryptionModalAction'), - }, - secondaryAction: [ - { - action: () => { - resolve(''); - dismissExport(); - }, - text: t('backupEncryptionModalCloseBtn'), - }, - ], - passwordOptional: true, - text: { - closeBtnLabel: t('backupEncryptionModalCloseBtn'), - input: t('backupEncryptionModalPlaceholder'), - message: t('backupEncryptionModalMessage'), - title: t('backupEncryptionModalTitle'), - }, - }); - }); - }; - - const exportHistory = async () => { - const password = await getBackUpPassword(); - setHistoryState(ExportState.PREPARING); - setHasError(false); - - try { - const numberOfRecords = await backupRepository.getBackupInitData(); - logger.log(`Exporting '${numberOfRecords}' records from history`); - - setNumberOfRecords(numberOfRecords); - setNumberOfProcessedRecords(0); - - const archiveBlob = await backupRepository.generateHistory( - user, - clientState.currentClient().id, - onProgress, - password, - ); - - onSuccess(archiveBlob); - logger.log(`Completed export of '${numberOfRecords}' records from history`); - } catch (error) { - onError(error as Error); - } - }; - - return ( -
- {isPreparing && } - - {isExporting && ( - <> - - - - - )} - - {isDone && ( - <> -
-

- {t('backupExportSuccessHeadline')} -

- -

- {t('backupExportSuccessSecondary')} -

- -
- -
-
- - - - )} - - {hasError && ( -
-

- {t('backupExportGenericErrorHeadline')} -

- -

- {t('backupExportGenericErrorSecondary')} -

- -
- - - -
-
- )} -
- ); -}; - -export {HistoryExport}; +export * from './HistoryExport'; diff --git a/src/script/components/HistoryImport/HistoryImport.tsx b/src/script/components/HistoryImport/HistoryImport.tsx new file mode 100644 index 00000000000..6514ff03c6a --- /dev/null +++ b/src/script/components/HistoryImport/HistoryImport.tsx @@ -0,0 +1,271 @@ +/* + * Wire + * Copyright (C) 2022 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import {FC, useEffect, useState} from 'react'; + +import {Button, ButtonVariant} from '@wireapp/react-ui-kit'; + +import {Icon} from 'Components/Icon'; +import {LoadingBar} from 'Components/LoadingBar/LoadingBar'; +import {PrimaryModal} from 'Components/Modals/PrimaryModal'; +import {User} from 'src/script/entity/User'; +import {ContentState} from 'src/script/page/useAppState'; +import {checkBackupEncryption} from 'Util/BackupUtil'; +import {t} from 'Util/LocalizerUtil'; +import {getLogger} from 'Util/Logger'; +import {loadFileBuffer} from 'Util/util'; + +import {BackupFileUpload} from './BackupFileUpload'; + +import {BackupRepository} from '../../backup/BackupRepository'; +import { + CancelError, + DifferentAccountError, + IncompatibleBackupError, + IncompatibleBackupFormatError, + InvalidPassword, +} from '../../backup/Error'; +import {Config} from '../../Config'; +import {MotionDuration} from '../../motion/MotionDuration'; + +export enum HistoryImportState { + DONE = 'HistoryImportState.STATE.DONE', + IMPORTING = 'HistoryImportState.STATE.IMPORTING', + PREPARING = 'HistoryImportState.STATE.PREPARING', +} + +interface HistoryImportProps { + readonly backupRepository: BackupRepository; + file: File; + switchContent: (contentState: ContentState) => void; + user: User; +} + +const HistoryImport: FC = ({user, backupRepository, file, switchContent}) => { + const logger = getLogger('HistoryImportViewModel'); + + const [historyImportState, setHistoryImportState] = useState(HistoryImportState.PREPARING); + const [error, setError] = useState(null); + const [errorHeadline, setErrorHeadline] = useState(''); + const [errorSecondary, setErrorSecondary] = useState(''); + + const [numberOfRecords, setNumberOfRecords] = useState(0); + const [numberOfProcessedRecords, setNumberOfProcessedRecords] = useState(0); + const loadingProgress = Math.floor((numberOfProcessedRecords / numberOfRecords) * 100); + + const isPreparing = !error && historyImportState === HistoryImportState.PREPARING; + const isImporting = !error && historyImportState === HistoryImportState.IMPORTING; + const isDone = !error && historyImportState === HistoryImportState.DONE; + + const replacements = { + processed: numberOfProcessedRecords.toString(), + progress: loadingProgress.toString(), + total: numberOfRecords.toString(), + }; + + const historyImportMessages: Partial> = { + [HistoryImportState.PREPARING]: t('backupImportProgressHeadline'), + [HistoryImportState.IMPORTING]: t('backupImportProgressSecondary', replacements), + }; + + const loadingMessage = historyImportMessages?.[historyImportState] || ''; + + const onCancel = () => backupRepository.cancelAction(); + + const dismissImport = () => { + switchContent(ContentState.PREFERENCES_ACCOUNT); + }; + + const onInit = (numberOfRecords: number) => { + setHistoryImportState(HistoryImportState.IMPORTING); + setNumberOfRecords(numberOfRecords); + setNumberOfProcessedRecords(0); + }; + + const onProgress = (numberProcessed: number) => setNumberOfProcessedRecords(prevState => prevState + numberProcessed); + + const onSuccess = (): void => { + setError(null); + setHistoryImportState(HistoryImportState.DONE); + + window.setTimeout(dismissImport, MotionDuration.X_LONG * 2); + }; + + const onError = (error: Error) => { + if (error instanceof CancelError) { + logger.log('History import was cancelled'); + dismissImport(); + + return; + } + + setError(error); + logger.error(`Failed to import history: ${error.message}`, error); + + if (error instanceof DifferentAccountError) { + setErrorHeadline(t('backupImportAccountErrorHeadline')); + setErrorSecondary(t('backupImportAccountErrorSecondary')); + } else if (error instanceof IncompatibleBackupError) { + setErrorHeadline(t('backupImportVersionErrorHeadline')); + setErrorSecondary(t('backupImportVersionErrorSecondary', Config.getConfig().BRAND_NAME)); + } else if (error instanceof IncompatibleBackupFormatError) { + setErrorHeadline(t('backupImportFormatErrorHeadline')); + setErrorSecondary(t('backupImportFormatErrorSecondary')); + } else if (error instanceof InvalidPassword) { + setErrorHeadline(t('backupImportPasswordErrorHeadline')); + setErrorSecondary(t('backupImportPasswordErrorSecondary')); + } else { + setErrorHeadline(t('backupImportGenericErrorHeadline')); + setErrorSecondary(t('backupImportGenericErrorSecondary')); + } + }; + + const getBackUpPassword = (): Promise => { + return new Promise(resolve => { + PrimaryModal.show(PrimaryModal.type.PASSWORD_ADVANCED_SECURITY, { + primaryAction: { + action: async (password: string) => { + resolve(password); + }, + text: t('backupDecryptionModalAction'), + }, + secondaryAction: [ + { + action: () => { + resolve(''); + dismissImport(); + }, + text: t('backupEncryptionModalCloseBtn'), + }, + ], + passwordOptional: false, + text: { + closeBtnLabel: t('backupEncryptionModalCloseBtn'), + input: t('backupDecryptionModalPlaceholder'), + message: t('backupDecryptionModalMessage'), + title: t('backupDecryptionModalTitle'), + }, + }); + }); + }; + + const importHistory = async (file: File) => { + const isEncrypted = await checkBackupEncryption(file); + + if (isEncrypted) { + const password = await getBackUpPassword(); + + if (password) { + await processHistoryImport(file, password); + } + } else { + await processHistoryImport(file); + } + }; + + const processHistoryImport = async (file: File, password?: string) => { + setHistoryImportState(HistoryImportState.PREPARING); + setError(null); + + const data = await loadFileBuffer(file); + + try { + await backupRepository.importHistory(user, data, onInit, onProgress, password); + onSuccess(); + } catch (error) { + onError(error as Error); + } + }; + + useEffect(() => { + importHistory(file); + }, []); + + const handleFileChange = async (event: React.ChangeEvent) => { + const file = event.target.files?.[0]; + if (file) { + setError(null); + await importHistory(file); + } + }; + + return ( +
+

{t('accessibility.headings.historyImport')}

+ +
+ {isPreparing && } + + {isImporting && ( + <> + + + + + )} + + {isDone && ( +
+ +

+ {t('backupImportSuccessHeadline')} +

+
+ )} + + {error && ( +
+

+ {errorHeadline} +

+ +

+ {errorSecondary} +

+ +
+ + +
+
+ )} +
+
+ ); +}; + +export {HistoryImport}; diff --git a/src/script/components/HistoryImport/index.tsx b/src/script/components/HistoryImport/index.tsx index 6514ff03c6a..bb683232cc1 100644 --- a/src/script/components/HistoryImport/index.tsx +++ b/src/script/components/HistoryImport/index.tsx @@ -1,6 +1,6 @@ /* * Wire - * Copyright (C) 2022 Wire Swiss GmbH + * Copyright (C) 2023 Wire Swiss GmbH * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,255 +17,4 @@ * */ -import {FC, useEffect, useState} from 'react'; - -import {Button, ButtonVariant} from '@wireapp/react-ui-kit'; - -import {Icon} from 'Components/Icon'; -import {LoadingBar} from 'Components/LoadingBar/LoadingBar'; -import {PrimaryModal} from 'Components/Modals/PrimaryModal'; -import {User} from 'src/script/entity/User'; -import {ContentState} from 'src/script/page/useAppState'; -import {checkBackupEncryption} from 'Util/BackupUtil'; -import {t} from 'Util/LocalizerUtil'; -import {getLogger} from 'Util/Logger'; -import {loadFileBuffer} from 'Util/util'; - -import {BackupFileUpload} from './BackupFileUpload'; - -import {BackupRepository} from '../../backup/BackupRepository'; -import { - CancelError, - DifferentAccountError, - IncompatibleBackupError, - IncompatibleBackupFormatError, - InvalidPassword, -} from '../../backup/Error'; -import {Config} from '../../Config'; -import {MotionDuration} from '../../motion/MotionDuration'; - -export enum HistoryImportState { - DONE = 'HistoryImportState.STATE.DONE', - IMPORTING = 'HistoryImportState.STATE.IMPORTING', - PREPARING = 'HistoryImportState.STATE.PREPARING', -} - -interface HistoryImportProps { - readonly backupRepository: BackupRepository; - file: File; - switchContent: (contentState: ContentState) => void; - user: User; -} - -const HistoryImport: FC = ({user, backupRepository, file, switchContent}) => { - const logger = getLogger('HistoryImportViewModel'); - - const [historyImportState, setHistoryImportState] = useState(HistoryImportState.PREPARING); - const [error, setError] = useState(null); - const [errorHeadline, setErrorHeadline] = useState(''); - const [errorSecondary, setErrorSecondary] = useState(''); - - const [numberOfRecords, setNumberOfRecords] = useState(0); - const [numberOfProcessedRecords, setNumberOfProcessedRecords] = useState(0); - const loadingProgress = Math.floor((numberOfProcessedRecords / numberOfRecords) * 100); - - const isPreparing = !error && historyImportState === HistoryImportState.PREPARING; - const isImporting = !error && historyImportState === HistoryImportState.IMPORTING; - const isDone = !error && historyImportState === HistoryImportState.DONE; - - const replacements = { - processed: numberOfProcessedRecords.toString(), - progress: loadingProgress.toString(), - total: numberOfRecords.toString(), - }; - - const historyImportMessages: Partial> = { - [HistoryImportState.PREPARING]: t('backupImportProgressHeadline'), - [HistoryImportState.IMPORTING]: t('backupImportProgressSecondary', replacements), - }; - - const loadingMessage = historyImportMessages?.[historyImportState] || ''; - - const onCancel = () => backupRepository.cancelAction(); - - const dismissImport = () => { - switchContent(ContentState.PREFERENCES_ACCOUNT); - }; - - const onInit = (numberOfRecords: number) => { - setHistoryImportState(HistoryImportState.IMPORTING); - setNumberOfRecords(numberOfRecords); - setNumberOfProcessedRecords(0); - }; - - const onProgress = (numberProcessed: number) => setNumberOfProcessedRecords(prevState => prevState + numberProcessed); - - const onSuccess = (): void => { - setError(null); - setHistoryImportState(HistoryImportState.DONE); - - window.setTimeout(dismissImport, MotionDuration.X_LONG * 2); - }; - - const onError = (error: Error) => { - if (error instanceof CancelError) { - logger.log('History import was cancelled'); - dismissImport(); - - return; - } - - setError(error); - logger.error(`Failed to import history: ${error.message}`, error); - - if (error instanceof DifferentAccountError) { - setErrorHeadline(t('backupImportAccountErrorHeadline')); - setErrorSecondary(t('backupImportAccountErrorSecondary')); - } else if (error instanceof IncompatibleBackupError) { - setErrorHeadline(t('backupImportVersionErrorHeadline')); - setErrorSecondary(t('backupImportVersionErrorSecondary', Config.getConfig().BRAND_NAME)); - } else if (error instanceof IncompatibleBackupFormatError) { - setErrorHeadline(t('backupImportFormatErrorHeadline')); - setErrorSecondary(t('backupImportFormatErrorSecondary')); - } else if (error instanceof InvalidPassword) { - setErrorHeadline(t('backupImportPasswordErrorHeadline')); - setErrorSecondary(t('backupImportPasswordErrorSecondary')); - } else { - setErrorHeadline(t('backupImportGenericErrorHeadline')); - setErrorSecondary(t('backupImportGenericErrorSecondary')); - } - }; - - const getBackUpPassword = (): Promise => { - return new Promise(resolve => { - PrimaryModal.show(PrimaryModal.type.PASSWORD_ADVANCED_SECURITY, { - primaryAction: { - action: async (password: string) => { - resolve(password); - }, - text: t('backupDecryptionModalAction'), - }, - secondaryAction: [ - { - action: () => { - resolve(''); - dismissImport(); - }, - text: t('backupEncryptionModalCloseBtn'), - }, - ], - passwordOptional: false, - text: { - closeBtnLabel: t('backupEncryptionModalCloseBtn'), - input: t('backupDecryptionModalPlaceholder'), - message: t('backupDecryptionModalMessage'), - title: t('backupDecryptionModalTitle'), - }, - }); - }); - }; - - const importHistory = async (file: File) => { - const isEncrypted = await checkBackupEncryption(file); - - if (isEncrypted) { - const password = await getBackUpPassword(); - - if (password) { - await processHistoryImport(file, password); - } - } else { - await processHistoryImport(file); - } - }; - - const processHistoryImport = async (file: File, password?: string) => { - setHistoryImportState(HistoryImportState.PREPARING); - setError(null); - - const data = await loadFileBuffer(file); - - try { - await backupRepository.importHistory(user, data, onInit, onProgress, password); - onSuccess(); - } catch (error) { - onError(error as Error); - } - }; - - useEffect(() => { - importHistory(file); - }, []); - - const handleFileChange = async (event: React.ChangeEvent) => { - const file = event.target.files?.[0]; - if (file) { - setError(null); - await importHistory(file); - } - }; - - return ( -
-

{t('accessibility.headings.historyImport')}

- -
- {isPreparing && } - - {isImporting && ( - <> - - - - - )} - - {isDone && ( -
- -

- {t('backupImportSuccessHeadline')} -

-
- )} - - {error && ( -
-

- {errorHeadline} -

- -

- {errorSecondary} -

- -
- - -
-
- )} -
-
- ); -}; - -export {HistoryImport}; +export * from './HistoryImport';