From 92ee1ad3cc125b758f217531f985eeb2ba61785e Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 25 Mar 2021 23:34:45 -0600 Subject: [PATCH] Convert AccessSecretStorageDialog to TypeScript This is to make upcoming changes a bit easier to do. --- ...ialog.js => AccessSecretStorageDialog.tsx} | 125 +++++++++--------- 1 file changed, 66 insertions(+), 59 deletions(-) rename src/components/views/dialogs/security/{AccessSecretStorageDialog.js => AccessSecretStorageDialog.tsx} (80%) diff --git a/src/components/views/dialogs/security/AccessSecretStorageDialog.js b/src/components/views/dialogs/security/AccessSecretStorageDialog.tsx similarity index 80% rename from src/components/views/dialogs/security/AccessSecretStorageDialog.js rename to src/components/views/dialogs/security/AccessSecretStorageDialog.tsx index f54a053984..0b0fd73496 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,16 @@ 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"; +import BaseDialog from "../BaseDialog"; // 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 +35,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 +70,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 +111,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 +149,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 +159,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 +178,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 +193,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,8 +217,6 @@ export default class AccessSecretStorageDialog extends React.PureComponent { } render() { - const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); - const hasPassphrase = ( this.props.keyInfo && this.props.keyInfo.passphrase && @@ -244,18 +251,18 @@ export default class AccessSecretStorageDialog extends React.PureComponent { { button: s => {s} , }, )}

-
+ @@ -291,7 +298,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent { @@ -301,7 +308,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent { type="password" label={_t('Security Key')} value={this.state.recoveryKey} - onChange={this._onRecoveryKeyChange} + onChange={this.onRecoveryKeyChange} forceValidity={this.state.recoveryKeyCorrect} autoComplete="off" /> @@ -312,10 +319,10 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
- + {_t("Upload")}
@@ -323,11 +330,11 @@ export default class AccessSecretStorageDialog extends React.PureComponent { {recoveryKeyFeedback}