Show progress when restoring key backup

This commit is contained in:
Ajay Bura 2022-04-24 09:49:48 +05:30
parent 848cbb1066
commit 89f69ed5b4
2 changed files with 76 additions and 47 deletions

View file

@ -6,8 +6,7 @@ import { twemojify } from '../../../util/twemojify';
import initMatrix from '../../../client/initMatrix';
import { openReusableDialog } from '../../../client/action/navigation';
import { getDefaultSSKey } from '../../../util/matrixUtil';
import { storePrivateKey, hasPrivateKey, getPrivateKey } from '../../../client/state/secretStorageKeys';
import { deletePrivateKey } from '../../../client/state/secretStorageKeys';
import Text from '../../atoms/text/Text';
import Button from '../../atoms/button/Button';
@ -16,7 +15,7 @@ import Spinner from '../../atoms/spinner/Spinner';
import InfoCard from '../../atoms/card/InfoCard';
import SettingTile from '../../molecules/setting-tile/SettingTile';
import SecretStorageAccess from './SecretStorageAccess';
import { accessSecretStorage } from './SecretStorageAccess';
import InfoIC from '../../../../public/res/ic/outlined/info.svg';
import BinIC from '../../../../public/res/ic/outlined/bin.svg';
@ -44,6 +43,7 @@ function CreateKeyBackupDialog({ keyData }) {
if (!mountStore.getItem()) return;
setDone(true);
} catch (e) {
deletePrivateKey(keyData.keyId);
await mx.deleteKeyBackupVersion(info.version);
if (!mountStore.getItem()) return;
setDone(null);
@ -83,24 +83,42 @@ CreateKeyBackupDialog.propTypes = {
};
function RestoreKeyBackupDialog({ keyData, backupInfo }) {
const [done, setDone] = useState(false);
const [status, setStatus] = useState(false);
const mx = initMatrix.matrixClient;
const mountStore = useStore();
const restoreBackup = async () => {
setDone(false);
setStatus(false);
let meBreath = true;
const progressCallback = (progress) => {
if (!progress.successes) return;
if (meBreath === false) return;
meBreath = false;
setTimeout(() => {
meBreath = true;
}, 200);
setStatus({ message: `Restoring backup keys... (${progress.successes}/${progress.total})` });
};
try {
await mx.restoreKeyBackupWithSecretStorage(
const info = await mx.restoreKeyBackupWithSecretStorage(
backupInfo,
undefined,
undefined,
{ progressCallback },
);
if (!mountStore.getItem()) return;
setDone(true);
setStatus({ done: `Successfully restored backup keys (${info.imported}/${info.total}).` });
} catch (e) {
if (!mountStore.getItem()) return;
setDone(null);
if (e.errcode === 'RESTORE_BACKUP_ERROR_BAD_KEY') {
deletePrivateKey(keyData.keyId);
setStatus({ error: 'Failed to restore backup. Key is invalid', errorCode: 'BAD_KEY' });
} else {
setStatus({ error: 'Failed to restore backup.', errCode: 'UNKNOWN' });
}
}
};
@ -111,21 +129,21 @@ function RestoreKeyBackupDialog({ keyData, backupInfo }) {
return (
<div className="key-backup__restore">
{done === false && (
{(status === false || status.message) && (
<div>
<Spinner size="small" />
<Text>Restoring backup...</Text>
<Text>{status.message ?? 'Restoring backup keys...'}</Text>
</div>
)}
{done === true && (
{status.done && (
<>
<Text variant="h1">{twemojify('✅')}</Text>
<Text>Successfully restored backup</Text>
<Text>{status.done}</Text>
</>
)}
{done === null && (
{status.error && (
<>
<Text>Failed to restore backup</Text>
<Text>{status.error}</Text>
<Button onClick={restoreBackup}>Retry</Button>
</>
)}
@ -201,43 +219,26 @@ function KeyBackup() {
};
}, []);
const accessSecretStorage = (title, onComplete) => {
const defaultSSKey = getDefaultSSKey();
if (hasPrivateKey(defaultSSKey)) {
onComplete({ decodedKey: getPrivateKey(defaultSSKey) });
return;
}
const handleComplete = (keyData) => {
storePrivateKey(keyData.keyId, keyData.decodedKey);
onComplete(keyData);
};
const openCreateKeyBackup = async () => {
const keyData = await accessSecretStorage('Create Key Backup');
if (keyData === null) return;
openReusableDialog(
<Text variant="s1" weight="medium">{title}</Text>,
() => <SecretStorageAccess onComplete={handleComplete} />,
);
};
const openCreateKeyBackup = () => {
const createKeyBackup = (keyData) => {
openReusableDialog(
<Text variant="s1" weight="medium">Create Key Backup</Text>,
() => <CreateKeyBackupDialog keyData={keyData} />,
() => fetchKeyBackupVersion(),
);
};
accessSecretStorage('Create Key Backup', createKeyBackup);
};
const openRestoreKeyBackup = () => {
const restoreKeyBackup = (keyData) => {
const openRestoreKeyBackup = async () => {
const keyData = await accessSecretStorage('Restore Key Backup');
if (keyData === null) return;
openReusableDialog(
<Text variant="s1" weight="medium">Restore Key Backup</Text>,
() => <RestoreKeyBackupDialog keyData={keyData} backupInfo={keyBackup} />,
);
};
accessSecretStorage('Restore Key Backup', restoreKeyBackup);
};
const openDeleteKeyBackup = () => openReusableDialog(
<Text variant="s1" weight="medium">Delete Key Backup</Text>,

View file

@ -4,7 +4,9 @@ import './SecretStorageAccess.scss';
import { deriveKey } from 'matrix-js-sdk/lib/crypto/key_passphrase';
import initMatrix from '../../../client/initMatrix';
import { openReusableDialog } from '../../../client/action/navigation';
import { getDefaultSSKey, getSSKeyInfo } from '../../../util/matrixUtil';
import { storePrivateKey, hasPrivateKey, getPrivateKey } from '../../../client/state/secretStorageKeys';
import Text from '../../atoms/text/Text';
import Button from '../../atoms/button/Button';
@ -30,10 +32,10 @@ function SecretStorageAccess({ onComplete }) {
setProcess(true);
try {
const { salt, iterations } = sSKeyInfo.passphrase;
const decodedKey = key
const privateKey = key
? mx.keyBackupKeyFromRecoveryKey(key)
: await deriveKey(phrase, salt, iterations);
const isCorrect = await mx.checkSecretStorageKey(decodedKey, sSKeyInfo);
const isCorrect = await mx.checkSecretStorageKey(privateKey, sSKeyInfo);
if (!mountStore.getItem()) return;
if (!isCorrect) {
@ -45,7 +47,7 @@ function SecretStorageAccess({ onComplete }) {
keyId: sSKeyId,
key,
phrase,
decodedKey,
privateKey,
});
} catch (e) {
if (!mountStore.getItem()) return;
@ -95,4 +97,30 @@ SecretStorageAccess.propTypes = {
onComplete: PropTypes.func.isRequired,
};
/**
* @param {string} title Title of secret storage access dialog
* @returns {Promise<keyData | null>} resolve to keyData or null
*/
export const accessSecretStorage = (title) => new Promise((resolve) => {
let isCompleted = false;
const defaultSSKey = getDefaultSSKey();
if (hasPrivateKey(defaultSSKey)) {
resolve({ keyId: defaultSSKey, privateKey: getPrivateKey(defaultSSKey) });
return;
}
const handleComplete = (keyData) => {
isCompleted = true;
storePrivateKey(keyData.keyId, keyData.privateKey);
resolve(keyData);
};
openReusableDialog(
<Text variant="s1" weight="medium">{title}</Text>,
() => <SecretStorageAccess onComplete={handleComplete} />,
() => {
if (!isCompleted) resolve(null);
},
);
});
export default SecretStorageAccess;