/* eslint-disable react/prop-types */
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import './KeyBackup.scss';
import { twemojify } from '../../../util/twemojify';
import initMatrix from '../../../client/initMatrix';
import { openReusableDialog } from '../../../client/action/navigation';
import { deletePrivateKey } from '../../../client/state/secretStorageKeys';
import Text from '../../atoms/text/Text';
import Button from '../../atoms/button/Button';
import IconButton from '../../atoms/button/IconButton';
import Spinner from '../../atoms/spinner/Spinner';
import InfoCard from '../../atoms/card/InfoCard';
import SettingTile from '../../molecules/setting-tile/SettingTile';
import { accessSecretStorage } from './SecretStorageAccess';
import InfoIC from '../../../../public/res/ic/outlined/info.svg';
import BinIC from '../../../../public/res/ic/outlined/bin.svg';
import DownloadIC from '../../../../public/res/ic/outlined/download.svg';
import { useStore } from '../../hooks/useStore';
import { useCrossSigningStatus } from '../../hooks/useCrossSigningStatus';
function CreateKeyBackupDialog({ keyData }) {
const [done, setDone] = useState(false);
const mx = initMatrix.matrixClient;
const mountStore = useStore();
const doBackup = async () => {
setDone(false);
let info;
try {
info = await mx.prepareKeyBackupVersion(
null,
{ secureSecretStorage: true },
);
info = await mx.createKeyBackupVersion(info);
await mx.scheduleAllGroupSessionsForBackup();
if (!mountStore.getItem()) return;
setDone(true);
} catch (e) {
deletePrivateKey(keyData.keyId);
await mx.deleteKeyBackupVersion(info.version);
if (!mountStore.getItem()) return;
setDone(null);
}
};
useEffect(() => {
mountStore.setItem(true);
doBackup();
}, []);
return (
{done === false && (
Creating backup...
)}
{done === true && (
<>
{twemojify('✅')}
Successfully created backup
>
)}
{done === null && (
<>
Failed to create backup
>
)}
);
}
CreateKeyBackupDialog.propTypes = {
keyData: PropTypes.shape({}).isRequired,
};
function RestoreKeyBackupDialog({ keyData }) {
const [status, setStatus] = useState(false);
const mx = initMatrix.matrixClient;
const mountStore = useStore();
const restoreBackup = async () => {
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 {
const backupInfo = await mx.getKeyBackupVersion();
const info = await mx.restoreKeyBackupWithSecretStorage(
backupInfo,
undefined,
undefined,
{ progressCallback },
);
if (!mountStore.getItem()) return;
setStatus({ done: `Successfully restored backup keys (${info.imported}/${info.total}).` });
} catch (e) {
if (!mountStore.getItem()) return;
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' });
}
}
};
useEffect(() => {
mountStore.setItem(true);
restoreBackup();
}, []);
return (
{(status === false || status.message) && (
{status.message ?? 'Restoring backup keys...'}
)}
{status.done && (
<>
{twemojify('✅')}
{status.done}
>
)}
{status.error && (
<>
{status.error}
>
)}
);
}
RestoreKeyBackupDialog.propTypes = {
keyData: PropTypes.shape({}).isRequired,
};
function DeleteKeyBackupDialog({ requestClose }) {
const [isDeleting, setIsDeleting] = useState(false);
const mx = initMatrix.matrixClient;
const mountStore = useStore();
mountStore.setItem(true);
const deleteBackup = async () => {
setIsDeleting(true);
try {
const backupInfo = await mx.getKeyBackupVersion();
if (backupInfo) await mx.deleteKeyBackupVersion(backupInfo.version);
if (!mountStore.getItem()) return;
requestClose(true);
} catch {
if (!mountStore.getItem()) return;
setIsDeleting(false);
}
};
return (
{twemojify('🗑')}
Deleting key backup is permanent.
All encrypted messages keys stored on server will be deleted.
{
isDeleting
?
:
}
);
}
DeleteKeyBackupDialog.propTypes = {
requestClose: PropTypes.func.isRequired,
};
function KeyBackup() {
const mx = initMatrix.matrixClient;
const isCSEnabled = useCrossSigningStatus();
const [keyBackup, setKeyBackup] = useState(undefined);
const mountStore = useStore();
const fetchKeyBackupVersion = async () => {
const info = await mx.getKeyBackupVersion();
if (!mountStore.getItem()) return;
setKeyBackup(info);
};
useEffect(() => {
mountStore.setItem(true);
fetchKeyBackupVersion();
const handleAccountData = (event) => {
if (event.getType() === 'm.megolm_backup.v1') {
fetchKeyBackupVersion();
}
};
mx.on('accountData', handleAccountData);
return () => {
mx.removeListener('accountData', handleAccountData);
};
}, [isCSEnabled]);
const openCreateKeyBackup = async () => {
const keyData = await accessSecretStorage('Create Key Backup');
if (keyData === null) return;
openReusableDialog(
Create Key Backup,
() => ,
() => fetchKeyBackupVersion(),
);
};
const openRestoreKeyBackup = async () => {
const keyData = await accessSecretStorage('Restore Key Backup');
if (keyData === null) return;
openReusableDialog(
Restore Key Backup,
() => ,
);
};
const openDeleteKeyBackup = () => openReusableDialog(
Delete Key Backup,
(requestClose) => (
{
if (isDone) setKeyBackup(null);
requestClose();
}}
/>
),
);
const renderOptions = () => {
if (keyBackup === undefined) return ;
if (keyBackup === null) return ;
return (
<>
>
);
};
return (
Online backup your encrypted messages keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Security Key.
{!isCSEnabled && (
)}
>
)}
options={isCSEnabled ? renderOptions() : null}
/>
);
}
export default KeyBackup;