diff --git a/src/@types/common.ts b/src/@types/common.ts index b4d01a75a5..b18fefc253 100644 --- a/src/@types/common.ts +++ b/src/@types/common.ts @@ -49,3 +49,8 @@ export type KeysWithObjectShape = { ? (Input[P] extends Array ? never : P) : never; }[keyof Input]; + +export type KeysStartingWith = { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + [P in keyof Input]: P extends `${Str}${infer _X}` ? P : never; // we don't use _X +}[keyof Input]; diff --git a/src/async-components/views/dialogs/security/ExportE2eKeysDialog.tsx b/src/async-components/views/dialogs/security/ExportE2eKeysDialog.tsx index 2f2bc36ec1..e020dbeea1 100644 --- a/src/async-components/views/dialogs/security/ExportE2eKeysDialog.tsx +++ b/src/async-components/views/dialogs/security/ExportE2eKeysDialog.tsx @@ -1,5 +1,6 @@ /* Copyright 2017 Vector Creations Ltd +Copyright 2022 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,7 +16,7 @@ limitations under the License. */ import FileSaver from 'file-saver'; -import React, { createRef } from 'react'; +import React from 'react'; import { MatrixClient } from 'matrix-js-sdk/src/client'; import { logger } from "matrix-js-sdk/src/logger"; @@ -23,6 +24,8 @@ import { _t } from '../../../../languageHandler'; import * as MegolmExportEncryption from '../../../../utils/MegolmExportEncryption'; import { IDialogProps } from "../../../../components/views/dialogs/IDialogProps"; import BaseDialog from "../../../../components/views/dialogs/BaseDialog"; +import Field from "../../../../components/views/elements/Field"; +import { KeysStartingWith } from "../../../../@types/common"; enum Phase { Edit = "edit", @@ -36,12 +39,14 @@ interface IProps extends IDialogProps { interface IState { phase: Phase; errStr: string; + passphrase1: string; + passphrase2: string; } +type AnyPassphrase = KeysStartingWith; + export default class ExportE2eKeysDialog extends React.Component { private unmounted = false; - private passphrase1 = createRef(); - private passphrase2 = createRef(); constructor(props: IProps) { super(props); @@ -49,6 +54,8 @@ export default class ExportE2eKeysDialog extends React.Component this.state = { phase: Phase.Edit, errStr: null, + passphrase1: "", + passphrase2: "", }; } @@ -59,8 +66,8 @@ export default class ExportE2eKeysDialog extends React.Component private onPassphraseFormSubmit = (ev: React.FormEvent): boolean => { ev.preventDefault(); - const passphrase = this.passphrase1.current.value; - if (passphrase !== this.passphrase2.current.value) { + const passphrase = this.state.passphrase1; + if (passphrase !== this.state.passphrase2) { this.setState({ errStr: _t('Passphrases must match') }); return false; } @@ -112,6 +119,12 @@ export default class ExportE2eKeysDialog extends React.Component return false; }; + private onPassphraseChange = (ev: React.ChangeEvent, phrase: AnyPassphrase) => { + this.setState({ + [phrase]: ev.target.value, + } as Pick); + }; + public render(): JSX.Element { const disableForm = (this.state.phase === Phase.Exporting); @@ -146,36 +159,25 @@ export default class ExportE2eKeysDialog extends React.Component
-
- -
-
- -
+ this.onPassphraseChange(e, "passphrase1")} + autoFocus={true} + size={64} + type="password" + disabled={disableForm} + />
-
- -
-
- -
+ this.onPassphraseChange(e, "passphrase2")} + size={64} + type="password" + disabled={disableForm} + />
diff --git a/src/async-components/views/dialogs/security/ImportE2eKeysDialog.tsx b/src/async-components/views/dialogs/security/ImportE2eKeysDialog.tsx index 65bbe0a70e..7c710cf676 100644 --- a/src/async-components/views/dialogs/security/ImportE2eKeysDialog.tsx +++ b/src/async-components/views/dialogs/security/ImportE2eKeysDialog.tsx @@ -1,5 +1,6 @@ /* Copyright 2017 Vector Creations Ltd +Copyright 2022 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -22,6 +23,7 @@ import * as MegolmExportEncryption from '../../../../utils/MegolmExportEncryptio import { _t } from '../../../../languageHandler'; import { IDialogProps } from "../../../../components/views/dialogs/IDialogProps"; import BaseDialog from "../../../../components/views/dialogs/BaseDialog"; +import Field from "../../../../components/views/elements/Field"; function readFileAsArrayBuffer(file: File): Promise { return new Promise((resolve, reject) => { @@ -48,12 +50,12 @@ interface IState { enableSubmit: boolean; phase: Phase; errStr: string; + passphrase: string; } export default class ImportE2eKeysDialog extends React.Component { private unmounted = false; private file = createRef(); - private passphrase = createRef(); constructor(props: IProps) { super(props); @@ -62,6 +64,7 @@ export default class ImportE2eKeysDialog extends React.Component enableSubmit: false, phase: Phase.Edit, errStr: null, + passphrase: "", }; } @@ -69,16 +72,22 @@ export default class ImportE2eKeysDialog extends React.Component this.unmounted = true; } - private onFormChange = (ev: React.FormEvent): void => { + private onFormChange = (): void => { const files = this.file.current.files || []; this.setState({ - enableSubmit: (this.passphrase.current.value !== "" && files.length > 0), + enableSubmit: (this.state.passphrase !== "" && files.length > 0), }); }; + private onPassphraseChange = (ev: React.ChangeEvent): void => { + this.setState({ passphrase: ev.target.value }); + this.onFormChange(); // update general form state too + }; + private onFormSubmit = (ev: React.FormEvent): boolean => { ev.preventDefault(); - this.startImport(this.file.current.files[0], this.passphrase.current.value); + // noinspection JSIgnoredPromiseFromCall + this.startImport(this.file.current.files[0], this.state.passphrase); return false; }; @@ -161,20 +170,14 @@ export default class ImportE2eKeysDialog extends React.Component
-
- -
-
- -
+