diff --git a/src/components/views/dialogs/security/AccessSecretStorageDialog.js b/src/components/views/dialogs/security/AccessSecretStorageDialog.tsx
similarity index 79%
rename from src/components/views/dialogs/security/AccessSecretStorageDialog.js
rename to src/components/views/dialogs/security/AccessSecretStorageDialog.tsx
index f54a053984..3c09470b39 100644
--- a/src/components/views/dialogs/security/AccessSecretStorageDialog.js
+++ b/src/components/views/dialogs/security/AccessSecretStorageDialog.tsx
@@ -1,6 +1,5 @@
/*
-Copyright 2018, 2019 New Vector Ltd
-Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
+Copyright 2018-2021 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.
@@ -17,14 +16,15 @@ limitations under the License.
import {debounce} from "lodash";
import classNames from 'classnames';
-import React from 'react';
-import PropTypes from "prop-types";
+import React, {ChangeEvent, FormEvent} from 'react';
+import {ISecretStorageKeyInfo} from "matrix-js-sdk/src";
+
import * as sdk from '../../../../index';
import {MatrixClientPeg} from '../../../../MatrixClientPeg';
import Field from '../../elements/Field';
import AccessibleButton from '../../elements/AccessibleButton';
-
-import { _t } from '../../../../languageHandler';
+import {_t} from '../../../../languageHandler';
+import {IDialogProps} from "../IDialogProps";
// Maximum acceptable size of a key file. It's 59 characters including the spaces we encode,
// so this should be plenty and allow for people putting extra whitespace in the file because
@@ -34,22 +34,30 @@ const KEY_FILE_MAX_SIZE = 128;
// Don't shout at the user that their key is invalid every time they type a key: wait a short time
const VALIDATION_THROTTLE_MS = 200;
+interface IProps extends IDialogProps {
+ keyInfo: ISecretStorageKeyInfo;
+ checkPrivateKey: (k: {passphrase?: string, recoveryKey?: string}) => boolean;
+}
+
+interface IState {
+ recoveryKey: string;
+ recoveryKeyValid: boolean | null;
+ recoveryKeyCorrect: boolean | null;
+ recoveryKeyFileError: boolean | null;
+ forceRecoveryKey: boolean;
+ passPhrase: string;
+ keyMatches: boolean | null;
+}
+
/*
* Access Secure Secret Storage by requesting the user's passphrase.
*/
-export default class AccessSecretStorageDialog extends React.PureComponent {
- static propTypes = {
- // { passphrase, pubkey }
- keyInfo: PropTypes.object.isRequired,
- // Function from one of { passphrase, recoveryKey } -> boolean
- checkPrivateKey: PropTypes.func.isRequired,
- }
+export default class AccessSecretStorageDialog extends React.PureComponent {
+ private fileUpload = React.createRef();
constructor(props) {
super(props);
- this._fileUpload = React.createRef();
-
this.state = {
recoveryKey: "",
recoveryKeyValid: null,
@@ -61,21 +69,21 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
};
}
- _onCancel = () => {
+ private onCancel = () => {
this.props.onFinished(false);
- }
+ };
- _onUseRecoveryKeyClick = () => {
+ private onUseRecoveryKeyClick = () => {
this.setState({
forceRecoveryKey: true,
});
- }
+ };
- _validateRecoveryKeyOnChange = debounce(() => {
- this._validateRecoveryKey();
+ private validateRecoveryKeyOnChange = debounce(async () => {
+ await this.validateRecoveryKey();
}, VALIDATION_THROTTLE_MS);
- async _validateRecoveryKey() {
+ private async validateRecoveryKey() {
if (this.state.recoveryKey === '') {
this.setState({
recoveryKeyValid: null,
@@ -102,27 +110,27 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
}
}
- _onRecoveryKeyChange = (e) => {
+ private onRecoveryKeyChange = (ev: ChangeEvent) => {
this.setState({
- recoveryKey: e.target.value,
+ recoveryKey: ev.target.value,
recoveryKeyFileError: null,
});
// also clear the file upload control so that the user can upload the same file
// the did before (otherwise the onchange wouldn't fire)
- if (this._fileUpload.current) this._fileUpload.current.value = null;
+ if (this.fileUpload.current) this.fileUpload.current.value = null;
// We don't use Field's validation here because a) we want it in a separate place rather
// than in a tooltip and b) we want it to display feedback based on the uploaded file
// as well as the text box. Ideally we would refactor Field's validation logic so we could
// re-use some of it.
- this._validateRecoveryKeyOnChange();
- }
+ this.validateRecoveryKeyOnChange();
+ };
- _onRecoveryKeyFileChange = async e => {
- if (e.target.files.length === 0) return;
+ private onRecoveryKeyFileChange = async (ev: ChangeEvent) => {
+ if (ev.target.files.length === 0) return;
- const f = e.target.files[0];
+ const f = ev.target.files[0];
if (f.size > KEY_FILE_MAX_SIZE) {
this.setState({
@@ -140,7 +148,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
recoveryKeyFileError: null,
recoveryKey: contents.trim(),
});
- this._validateRecoveryKey();
+ await this.validateRecoveryKey();
} else {
this.setState({
recoveryKeyFileError: true,
@@ -150,14 +158,14 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
});
}
}
+ };
+
+ private onRecoveryKeyFileUploadClick = () => {
+ this.fileUpload.current.click();
}
- _onRecoveryKeyFileUploadClick = () => {
- this._fileUpload.current.click();
- }
-
- _onPassPhraseNext = async (e) => {
- e.preventDefault();
+ private onPassPhraseNext = async (ev: FormEvent) => {
+ ev.preventDefault();
if (this.state.passPhrase.length <= 0) return;
@@ -169,10 +177,10 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
} else {
this.setState({ keyMatches });
}
- }
+ };
- _onRecoveryKeyNext = async (e) => {
- e.preventDefault();
+ private onRecoveryKeyNext = async (ev: FormEvent) => {
+ ev.preventDefault();
if (!this.state.recoveryKeyValid) return;
@@ -184,16 +192,16 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
} else {
this.setState({ keyMatches });
}
- }
+ };
- _onPassPhraseChange = (e) => {
+ private onPassPhraseChange = (ev: ChangeEvent) => {
this.setState({
- passPhrase: e.target.value,
+ passPhrase: ev.target.value,
keyMatches: null,
});
- }
+ };
- getKeyValidationText() {
+ private getKeyValidationText(): string {
if (this.state.recoveryKeyFileError) {
return _t("Wrong file type");
} else if (this.state.recoveryKeyCorrect) {
@@ -208,7 +216,8 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
}
render() {
- const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
+ // Caution: Making this an import will break tests.
+ const BaseDialog = sdk.getComponent("views.dialogs.BaseDialog");
const hasPassphrase = (
this.props.keyInfo &&
@@ -244,18 +253,18 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
{
button: s =>
{s}
,
},
)}
-