11/*
2- Copyright 2018, 2019 New Vector Ltd
3- Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
2+ Copyright 2018-2021 The Matrix.org Foundation C.I.C.
43
54Licensed under the Apache License, Version 2.0 (the "License");
65you may not use this file except in compliance with the License.
@@ -17,14 +16,16 @@ limitations under the License.
1716
1817import { debounce } from "lodash" ;
1918import classNames from 'classnames' ;
20- import React from 'react' ;
21- import PropTypes from "prop-types" ;
19+ import React , { ChangeEvent , FormEvent } from 'react' ;
20+ import { ISecretStorageKeyInfo } from "matrix-js-sdk/src" ;
21+
2222import * as sdk from '../../../../index' ;
2323import { MatrixClientPeg } from '../../../../MatrixClientPeg' ;
2424import Field from '../../elements/Field' ;
2525import AccessibleButton from '../../elements/AccessibleButton' ;
26-
27- import { _t } from '../../../../languageHandler' ;
26+ import { _t } from '../../../../languageHandler' ;
27+ import { IDialogProps } from "../IDialogProps" ;
28+ import BaseDialog from "../BaseDialog" ;
2829
2930// Maximum acceptable size of a key file. It's 59 characters including the spaces we encode,
3031// 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;
3435// Don't shout at the user that their key is invalid every time they type a key: wait a short time
3536const VALIDATION_THROTTLE_MS = 200 ;
3637
38+ interface IProps extends IDialogProps {
39+ keyInfo : ISecretStorageKeyInfo ;
40+ checkPrivateKey : ( k : { passphrase ?: string , recoveryKey ?: string } ) => boolean ;
41+ }
42+
43+ interface IState {
44+ recoveryKey : string ;
45+ recoveryKeyValid : boolean | null ;
46+ recoveryKeyCorrect : boolean | null ;
47+ recoveryKeyFileError : boolean | null ;
48+ forceRecoveryKey : boolean ;
49+ passPhrase : string ;
50+ keyMatches : boolean | null ;
51+ }
52+
3753/*
3854 * Access Secure Secret Storage by requesting the user's passphrase.
3955 */
40- export default class AccessSecretStorageDialog extends React . PureComponent {
41- static propTypes = {
42- // { passphrase, pubkey }
43- keyInfo : PropTypes . object . isRequired ,
44- // Function from one of { passphrase, recoveryKey } -> boolean
45- checkPrivateKey : PropTypes . func . isRequired ,
46- }
56+ export default class AccessSecretStorageDialog extends React . PureComponent < IProps , IState > {
57+ private fileUpload = React . createRef < HTMLInputElement > ( ) ;
4758
4859 constructor ( props ) {
4960 super ( props ) ;
5061
51- this . _fileUpload = React . createRef ( ) ;
52-
5362 this . state = {
5463 recoveryKey : "" ,
5564 recoveryKeyValid : null ,
@@ -61,21 +70,21 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
6170 } ;
6271 }
6372
64- _onCancel = ( ) => {
73+ private onCancel = ( ) => {
6574 this . props . onFinished ( false ) ;
66- }
75+ } ;
6776
68- _onUseRecoveryKeyClick = ( ) => {
77+ private onUseRecoveryKeyClick = ( ) => {
6978 this . setState ( {
7079 forceRecoveryKey : true ,
7180 } ) ;
72- }
81+ } ;
7382
74- _validateRecoveryKeyOnChange = debounce ( ( ) => {
75- this . _validateRecoveryKey ( ) ;
83+ private validateRecoveryKeyOnChange = debounce ( async ( ) => {
84+ await this . validateRecoveryKey ( ) ;
7685 } , VALIDATION_THROTTLE_MS ) ;
7786
78- async _validateRecoveryKey ( ) {
87+ private async validateRecoveryKey ( ) {
7988 if ( this . state . recoveryKey === '' ) {
8089 this . setState ( {
8190 recoveryKeyValid : null ,
@@ -102,27 +111,27 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
102111 }
103112 }
104113
105- _onRecoveryKeyChange = ( e ) => {
114+ private onRecoveryKeyChange = ( ev : ChangeEvent < HTMLInputElement > ) => {
106115 this . setState ( {
107- recoveryKey : e . target . value ,
116+ recoveryKey : ev . target . value ,
108117 recoveryKeyFileError : null ,
109118 } ) ;
110119
111120 // also clear the file upload control so that the user can upload the same file
112121 // the did before (otherwise the onchange wouldn't fire)
113- if ( this . _fileUpload . current ) this . _fileUpload . current . value = null ;
122+ if ( this . fileUpload . current ) this . fileUpload . current . value = null ;
114123
115124 // We don't use Field's validation here because a) we want it in a separate place rather
116125 // than in a tooltip and b) we want it to display feedback based on the uploaded file
117126 // as well as the text box. Ideally we would refactor Field's validation logic so we could
118127 // re-use some of it.
119- this . _validateRecoveryKeyOnChange ( ) ;
120- }
128+ this . validateRecoveryKeyOnChange ( ) ;
129+ } ;
121130
122- _onRecoveryKeyFileChange = async e => {
123- if ( e . target . files . length === 0 ) return ;
131+ private onRecoveryKeyFileChange = async ( ev : ChangeEvent < HTMLInputElement > ) => {
132+ if ( ev . target . files . length === 0 ) return ;
124133
125- const f = e . target . files [ 0 ] ;
134+ const f = ev . target . files [ 0 ] ;
126135
127136 if ( f . size > KEY_FILE_MAX_SIZE ) {
128137 this . setState ( {
@@ -140,7 +149,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
140149 recoveryKeyFileError : null ,
141150 recoveryKey : contents . trim ( ) ,
142151 } ) ;
143- this . _validateRecoveryKey ( ) ;
152+ await this . validateRecoveryKey ( ) ;
144153 } else {
145154 this . setState ( {
146155 recoveryKeyFileError : true ,
@@ -150,14 +159,14 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
150159 } ) ;
151160 }
152161 }
153- }
162+ } ;
154163
155- _onRecoveryKeyFileUploadClick = ( ) => {
156- this . _fileUpload . current . click ( ) ;
164+ private onRecoveryKeyFileUploadClick = ( ) => {
165+ this . fileUpload . current . click ( ) ;
157166 }
158167
159- _onPassPhraseNext = async ( e ) => {
160- e . preventDefault ( ) ;
168+ private onPassPhraseNext = async ( ev : FormEvent < HTMLFormElement > ) => {
169+ ev . preventDefault ( ) ;
161170
162171 if ( this . state . passPhrase . length <= 0 ) return ;
163172
@@ -169,10 +178,10 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
169178 } else {
170179 this . setState ( { keyMatches } ) ;
171180 }
172- }
181+ } ;
173182
174- _onRecoveryKeyNext = async ( e ) => {
175- e . preventDefault ( ) ;
183+ private onRecoveryKeyNext = async ( ev : FormEvent < HTMLFormElement > ) => {
184+ ev . preventDefault ( ) ;
176185
177186 if ( ! this . state . recoveryKeyValid ) return ;
178187
@@ -184,16 +193,16 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
184193 } else {
185194 this . setState ( { keyMatches } ) ;
186195 }
187- }
196+ } ;
188197
189- _onPassPhraseChange = ( e ) => {
198+ private onPassPhraseChange = ( ev : ChangeEvent < HTMLInputElement > ) => {
190199 this . setState ( {
191- passPhrase : e . target . value ,
200+ passPhrase : ev . target . value ,
192201 keyMatches : null ,
193202 } ) ;
194- }
203+ } ;
195204
196- getKeyValidationText ( ) {
205+ private getKeyValidationText ( ) : string {
197206 if ( this . state . recoveryKeyFileError ) {
198207 return _t ( "Wrong file type" ) ;
199208 } else if ( this . state . recoveryKeyCorrect ) {
@@ -208,8 +217,6 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
208217 }
209218
210219 render ( ) {
211- const BaseDialog = sdk . getComponent ( 'views.dialogs.BaseDialog' ) ;
212-
213220 const hasPassphrase = (
214221 this . props . keyInfo &&
215222 this . props . keyInfo . passphrase &&
@@ -244,18 +251,18 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
244251 {
245252 button : s => < AccessibleButton className = "mx_linkButton"
246253 element = "span"
247- onClick = { this . _onUseRecoveryKeyClick }
254+ onClick = { this . onUseRecoveryKeyClick }
248255 >
249256 { s }
250257 </ AccessibleButton > ,
251258 } ,
252259 ) } </ p >
253260
254- < form className = "mx_AccessSecretStorageDialog_primaryContainer" onSubmit = { this . _onPassPhraseNext } >
261+ < form className = "mx_AccessSecretStorageDialog_primaryContainer" onSubmit = { this . onPassPhraseNext } >
255262 < input
256263 type = "password"
257264 className = "mx_AccessSecretStorageDialog_passPhraseInput"
258- onChange = { this . _onPassPhraseChange }
265+ onChange = { this . onPassPhraseChange }
259266 value = { this . state . passPhrase }
260267 autoFocus = { true }
261268 autoComplete = "new-password"
@@ -264,9 +271,9 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
264271 { keyStatus }
265272 < DialogButtons
266273 primaryButton = { _t ( 'Continue' ) }
267- onPrimaryButtonClick = { this . _onPassPhraseNext }
274+ onPrimaryButtonClick = { this . onPassPhraseNext }
268275 hasCancel = { true }
269- onCancel = { this . _onCancel }
276+ onCancel = { this . onCancel }
270277 focus = { false }
271278 primaryDisabled = { this . state . passPhrase . length === 0 }
272279 />
@@ -291,7 +298,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
291298
292299 < form
293300 className = "mx_AccessSecretStorageDialog_primaryContainer"
294- onSubmit = { this . _onRecoveryKeyNext }
301+ onSubmit = { this . onRecoveryKeyNext }
295302 spellCheck = { false }
296303 autoComplete = "off"
297304 >
@@ -301,7 +308,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
301308 type = "password"
302309 label = { _t ( 'Security Key' ) }
303310 value = { this . state . recoveryKey }
304- onChange = { this . _onRecoveryKeyChange }
311+ onChange = { this . onRecoveryKeyChange }
305312 forceValidity = { this . state . recoveryKeyCorrect }
306313 autoComplete = "off"
307314 />
@@ -312,22 +319,22 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
312319 < div >
313320 < input type = "file"
314321 className = "mx_AccessSecretStorageDialog_recoveryKeyEntry_fileInput"
315- ref = { this . _fileUpload }
316- onChange = { this . _onRecoveryKeyFileChange }
322+ ref = { this . fileUpload }
323+ onChange = { this . onRecoveryKeyFileChange }
317324 />
318- < AccessibleButton kind = "primary" onClick = { this . _onRecoveryKeyFileUploadClick } >
325+ < AccessibleButton kind = "primary" onClick = { this . onRecoveryKeyFileUploadClick } >
319326 { _t ( "Upload" ) }
320327 </ AccessibleButton >
321328 </ div >
322329 </ div >
323330 { recoveryKeyFeedback }
324331 < DialogButtons
325332 primaryButton = { _t ( 'Continue' ) }
326- onPrimaryButtonClick = { this . _onRecoveryKeyNext }
333+ onPrimaryButtonClick = { this . onRecoveryKeyNext }
327334 hasCancel = { true }
328335 cancelButton = { _t ( "Go Back" ) }
329336 cancelButtonClass = 'danger'
330- onCancel = { this . _onCancel }
337+ onCancel = { this . onCancel }
331338 focus = { false }
332339 primaryDisabled = { ! this . state . recoveryKeyValid }
333340 />
0 commit comments