Files
pezkuwi-apps/packages/page-accounts/src/modals/Backup.tsx
T
Satoshi Qazi Muhammed 45d479d2ec Move to @pezkuwi/dev 0.86.4 (eslint 10 toolchain)
- dev ^0.86.4 at the root and in packages/apps; directives renamed by
  pezkuwi-dev-migrate-eslint10.
- eslint --fix with the 0.86.4 config; one fix broke syntax
  (typescript-eslint prefer-optional-chain turned
  `(isStarted && onStart) && onStart()` into `(isStarted && onStart?.();`
  in TxButton) and is corrected by hand.
- The remaining findings fixed rather than disabled: optional chains
  (each suggestion reviewed), `cause` on rethrown errors, `if` instead of
  `a && promise` in effects, parallel instead of sequential awaits in
  Promise.all, Error rejections, no `delete` on arrays (the storage query
  cache is a Map; an upload's file list no longer keeps a hole), no
  `[object Object]` stringification (TFunction's second argument is text
  or options), dead code removed (referenda's never-shown expand button,
  react-query's unused CoreDescriptor).
2026-10-10 02:06:12 +04:00

99 lines
3.0 KiB
TypeScript

// Copyright 2017-2026 @pezkuwi/app-accounts authors & contributors
// SPDX-License-Identifier: Apache-2.0
import FileSaver from 'file-saver';
import React, { useCallback, useState } from 'react';
import { AddressRow, Button, Modal, Password } from '@pezkuwi/react-components';
import { keyring } from '@pezkuwi/ui-keyring';
import { nextTick } from '@pezkuwi/util';
import { useTranslation } from '../translate.js';
interface Props {
onClose: () => void;
address: string;
}
function Backup ({ address, onClose }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const [isBusy, setIsBusy] = useState(false);
const [{ isPassTouched, password }, setPassword] = useState({ isPassTouched: false, password: '' });
const [backupFailed, setBackupFailed] = useState(false);
const isPassValid = !backupFailed && keyring.isPassValid(password);
const _onChangePass = useCallback(
(password: string): void => {
setBackupFailed(false);
setPassword({ isPassTouched: true, password });
},
[]
);
const _doBackup = useCallback(
(): void => {
setIsBusy(true);
nextTick((): void => {
try {
const addressKeyring = address && keyring.getPair(address);
const json = addressKeyring && keyring.backupAccount(addressKeyring, password);
const blob = new Blob([JSON.stringify(json)], { type: 'application/json; charset=utf-8' });
// eslint-disable-next-line @typescript-eslint/no-deprecated
FileSaver.saveAs(blob, `${address}.json`);
} catch (error) {
setBackupFailed(true);
setIsBusy(false);
console.error(error);
return;
}
setIsBusy(false);
onClose();
});
},
[address, onClose, password]
);
return (
<Modal
className='app--accounts-Modal'
header={t('Backup account')}
onClose={onClose}
>
<Modal.Content>
<AddressRow
isInline
value={address}
>
<p>{t('An encrypted backup file will be created once you have pressed the "Download" button. This can be used to re-import your account on any other machine.')}</p>
<p>{t('Save this backup file in a secure location. Additionally, the password associated with this account is needed together with this backup file in order to restore your account.')}</p>
<div>
<Password
autoFocus
isError={isPassTouched && !isPassValid}
label={t('password')}
onChange={_onChangePass}
onEnter={_doBackup}
tabIndex={0}
value={password}
/>
</div>
</AddressRow>
</Modal.Content>
<Modal.Actions>
<Button
icon='download'
isBusy={isBusy}
isDisabled={!isPassValid}
label={t('Download')}
onClick={_doBackup}
/>
</Modal.Actions>
</Modal>
);
}
export default React.memo(Backup);