import React, { FormEventHandler, useCallback, useEffect, useState } from 'react'; import { Box, Button, color, Icon, Icons, Spinner, Text, toRem } from 'folds'; import FileSaver from 'file-saver'; import { SequenceCard } from '../../../components/sequence-card'; import { SettingTile } from '../../../components/setting-tile'; import { SequenceCardStyle } from '../styles.css'; import { PasswordInput } from '../../../components/password-input'; import { ConfirmPasswordMatch } from '../../../components/ConfirmPasswordMatch'; import { useMatrixClient } from '../../../hooks/useMatrixClient'; import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback'; import { decryptMegolmKeyFile, encryptMegolmKeyFile } from '../../../../util/cryptE2ERoomKeys'; import { useAlive } from '../../../hooks/useAlive'; import { useFilePicker } from '../../../hooks/useFilePicker'; function ExportKeys() { const mx = useMatrixClient(); const alive = useAlive(); const [exportState, exportKeys] = useAsyncCallback( useCallback( async (password) => { const crypto = mx.getCrypto(); if (!crypto) throw new Error('Unexpected Error! Crypto module not found!'); const keysJSON = await crypto.exportRoomKeysAsJson(); const encKeys = await encryptMegolmKeyFile(keysJSON, password); const blob = new Blob([encKeys], { type: 'text/plain;charset=us-ascii', }); FileSaver.saveAs(blob, 'cinny-keys.txt'); }, [mx] ) ); const exporting = exportState.status === AsyncStatus.Loading; const handleSubmit: FormEventHandler = (evt) => { evt.preventDefault(); if (exporting) return; const { passwordInput, confirmPasswordInput } = evt.target as HTMLFormElement & { passwordInput: HTMLInputElement; confirmPasswordInput: HTMLInputElement; }; const password = passwordInput.value; const confirmPassword = confirmPasswordInput.value; if (password !== confirmPassword) return; exportKeys(password).then(() => { if (alive()) { passwordInput.value = ''; confirmPasswordInput.value = ''; } }); }; return ( {(match, doMatch, passRef, confPassRef) => ( <> New Password Confirm Password )} {exportState.status === AsyncStatus.Error && ( {exportState.error.message} )} ); } function ExportKeysTile() { const [expand, setExpand] = useState(false); return ( <> } /> {expand && } ); } type ImportKeysProps = { file: File; onDone?: () => void; }; function ImportKeys({ file, onDone }: ImportKeysProps) { const mx = useMatrixClient(); const alive = useAlive(); const [decryptState, decryptFile] = useAsyncCallback( useCallback( async (password) => { const crypto = mx.getCrypto(); if (!crypto) throw new Error('Unexpected Error! Crypto module not found!'); const arrayBuffer = await file.arrayBuffer(); const keys = await decryptMegolmKeyFile(arrayBuffer, password); await crypto.importRoomKeysAsJson(keys); }, [file, mx] ) ); const decrypting = decryptState.status === AsyncStatus.Loading; useEffect(() => { if (decryptState.status === AsyncStatus.Success) { onDone?.(); } }, [onDone, decryptState]); const handleSubmit: FormEventHandler = (evt) => { evt.preventDefault(); if (decrypting) return; const { passwordInput } = evt.target as HTMLFormElement & { passwordInput: HTMLInputElement; }; const password = passwordInput.value; if (!password) return; decryptFile(password).then(() => { if (alive()) { passwordInput.value = ''; } }); }; return ( Password {decryptState.status === AsyncStatus.Error && ( {decryptState.error.message} )} ); } function ImportKeysTile() { const [file, setFile] = useState(); const pickFile = useFilePicker(setFile); const handleDone = useCallback(() => { setFile(undefined); }, []); return ( <> {file ? ( ) : ( )} } /> {file && } ); } export function LocalBackup() { return ( Local Backup ); }