Skip to content

Commit 1032334

Browse files
authored
Convert inputs on Export/Import Room Key dialogs to be real Fields (matrix-org#9350)
* Convert inputs on Export/Import Room Key dialogs to be real Fields Fixes element-hq/element-web#18517 * Correctly label the second field * Appease the linter
1 parent 99488b8 commit 1032334

File tree

3 files changed

+61
-51
lines changed

3 files changed

+61
-51
lines changed

src/@types/common.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,8 @@ export type KeysWithObjectShape<Input> = {
4949
? (Input[P] extends Array<unknown> ? never : P)
5050
: never;
5151
}[keyof Input];
52+
53+
export type KeysStartingWith<Input extends object, Str extends string> = {
54+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
55+
[P in keyof Input]: P extends `${Str}${infer _X}` ? P : never; // we don't use _X
56+
}[keyof Input];

src/async-components/views/dialogs/security/ExportE2eKeysDialog.tsx

Lines changed: 35 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/*
22
Copyright 2017 Vector Creations Ltd
3+
Copyright 2022 The Matrix.org Foundation C.I.C.
34
45
Licensed under the Apache License, Version 2.0 (the "License");
56
you may not use this file except in compliance with the License.
@@ -15,14 +16,16 @@ limitations under the License.
1516
*/
1617

1718
import FileSaver from 'file-saver';
18-
import React, { createRef } from 'react';
19+
import React from 'react';
1920
import { MatrixClient } from 'matrix-js-sdk/src/client';
2021
import { logger } from "matrix-js-sdk/src/logger";
2122

2223
import { _t } from '../../../../languageHandler';
2324
import * as MegolmExportEncryption from '../../../../utils/MegolmExportEncryption';
2425
import { IDialogProps } from "../../../../components/views/dialogs/IDialogProps";
2526
import BaseDialog from "../../../../components/views/dialogs/BaseDialog";
27+
import Field from "../../../../components/views/elements/Field";
28+
import { KeysStartingWith } from "../../../../@types/common";
2629

2730
enum Phase {
2831
Edit = "edit",
@@ -36,19 +39,23 @@ interface IProps extends IDialogProps {
3639
interface IState {
3740
phase: Phase;
3841
errStr: string;
42+
passphrase1: string;
43+
passphrase2: string;
3944
}
4045

46+
type AnyPassphrase = KeysStartingWith<IState, "passphrase">;
47+
4148
export default class ExportE2eKeysDialog extends React.Component<IProps, IState> {
4249
private unmounted = false;
43-
private passphrase1 = createRef<HTMLInputElement>();
44-
private passphrase2 = createRef<HTMLInputElement>();
4550

4651
constructor(props: IProps) {
4752
super(props);
4853

4954
this.state = {
5055
phase: Phase.Edit,
5156
errStr: null,
57+
passphrase1: "",
58+
passphrase2: "",
5259
};
5360
}
5461

@@ -59,8 +66,8 @@ export default class ExportE2eKeysDialog extends React.Component<IProps, IState>
5966
private onPassphraseFormSubmit = (ev: React.FormEvent): boolean => {
6067
ev.preventDefault();
6168

62-
const passphrase = this.passphrase1.current.value;
63-
if (passphrase !== this.passphrase2.current.value) {
69+
const passphrase = this.state.passphrase1;
70+
if (passphrase !== this.state.passphrase2) {
6471
this.setState({ errStr: _t('Passphrases must match') });
6572
return false;
6673
}
@@ -112,6 +119,12 @@ export default class ExportE2eKeysDialog extends React.Component<IProps, IState>
112119
return false;
113120
};
114121

122+
private onPassphraseChange = (ev: React.ChangeEvent<HTMLInputElement>, phrase: AnyPassphrase) => {
123+
this.setState({
124+
[phrase]: ev.target.value,
125+
} as Pick<IState, AnyPassphrase>);
126+
};
127+
115128
public render(): JSX.Element {
116129
const disableForm = (this.state.phase === Phase.Exporting);
117130

@@ -146,36 +159,25 @@ export default class ExportE2eKeysDialog extends React.Component<IProps, IState>
146159
</div>
147160
<div className='mx_E2eKeysDialog_inputTable'>
148161
<div className='mx_E2eKeysDialog_inputRow'>
149-
<div className='mx_E2eKeysDialog_inputLabel'>
150-
<label htmlFor='passphrase1'>
151-
{ _t("Enter passphrase") }
152-
</label>
153-
</div>
154-
<div className='mx_E2eKeysDialog_inputCell'>
155-
<input
156-
ref={this.passphrase1}
157-
id='passphrase1'
158-
autoFocus={true}
159-
size={64}
160-
type='password'
161-
disabled={disableForm}
162-
/>
163-
</div>
162+
<Field
163+
label={_t("Enter passphrase")}
164+
value={this.state.passphrase1}
165+
onChange={e => this.onPassphraseChange(e, "passphrase1")}
166+
autoFocus={true}
167+
size={64}
168+
type="password"
169+
disabled={disableForm}
170+
/>
164171
</div>
165172
<div className='mx_E2eKeysDialog_inputRow'>
166-
<div className='mx_E2eKeysDialog_inputLabel'>
167-
<label htmlFor='passphrase2'>
168-
{ _t("Confirm passphrase") }
169-
</label>
170-
</div>
171-
<div className='mx_E2eKeysDialog_inputCell'>
172-
<input ref={this.passphrase2}
173-
id='passphrase2'
174-
size={64}
175-
type='password'
176-
disabled={disableForm}
177-
/>
178-
</div>
173+
<Field
174+
label={_t("Confirm passphrase")}
175+
value={this.state.passphrase2}
176+
onChange={e => this.onPassphraseChange(e, "passphrase2")}
177+
size={64}
178+
type="password"
179+
disabled={disableForm}
180+
/>
179181
</div>
180182
</div>
181183
</div>

src/async-components/views/dialogs/security/ImportE2eKeysDialog.tsx

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/*
22
Copyright 2017 Vector Creations Ltd
3+
Copyright 2022 The Matrix.org Foundation C.I.C.
34
45
Licensed under the Apache License, Version 2.0 (the "License");
56
you may not use this file except in compliance with the License.
@@ -22,6 +23,7 @@ import * as MegolmExportEncryption from '../../../../utils/MegolmExportEncryptio
2223
import { _t } from '../../../../languageHandler';
2324
import { IDialogProps } from "../../../../components/views/dialogs/IDialogProps";
2425
import BaseDialog from "../../../../components/views/dialogs/BaseDialog";
26+
import Field from "../../../../components/views/elements/Field";
2527

2628
function readFileAsArrayBuffer(file: File): Promise<ArrayBuffer> {
2729
return new Promise((resolve, reject) => {
@@ -48,12 +50,12 @@ interface IState {
4850
enableSubmit: boolean;
4951
phase: Phase;
5052
errStr: string;
53+
passphrase: string;
5154
}
5255

5356
export default class ImportE2eKeysDialog extends React.Component<IProps, IState> {
5457
private unmounted = false;
5558
private file = createRef<HTMLInputElement>();
56-
private passphrase = createRef<HTMLInputElement>();
5759

5860
constructor(props: IProps) {
5961
super(props);
@@ -62,23 +64,30 @@ export default class ImportE2eKeysDialog extends React.Component<IProps, IState>
6264
enableSubmit: false,
6365
phase: Phase.Edit,
6466
errStr: null,
67+
passphrase: "",
6568
};
6669
}
6770

6871
public componentWillUnmount(): void {
6972
this.unmounted = true;
7073
}
7174

72-
private onFormChange = (ev: React.FormEvent): void => {
75+
private onFormChange = (): void => {
7376
const files = this.file.current.files || [];
7477
this.setState({
75-
enableSubmit: (this.passphrase.current.value !== "" && files.length > 0),
78+
enableSubmit: (this.state.passphrase !== "" && files.length > 0),
7679
});
7780
};
7881

82+
private onPassphraseChange = (ev: React.ChangeEvent<HTMLInputElement>): void => {
83+
this.setState({ passphrase: ev.target.value });
84+
this.onFormChange(); // update general form state too
85+
};
86+
7987
private onFormSubmit = (ev: React.FormEvent): boolean => {
8088
ev.preventDefault();
81-
this.startImport(this.file.current.files[0], this.passphrase.current.value);
89+
// noinspection JSIgnoredPromiseFromCall
90+
this.startImport(this.file.current.files[0], this.state.passphrase);
8291
return false;
8392
};
8493

@@ -161,20 +170,14 @@ export default class ImportE2eKeysDialog extends React.Component<IProps, IState>
161170
</div>
162171
</div>
163172
<div className='mx_E2eKeysDialog_inputRow'>
164-
<div className='mx_E2eKeysDialog_inputLabel'>
165-
<label htmlFor='passphrase'>
166-
{ _t("Enter passphrase") }
167-
</label>
168-
</div>
169-
<div className='mx_E2eKeysDialog_inputCell'>
170-
<input
171-
ref={this.passphrase}
172-
id='passphrase'
173-
size={64}
174-
type='password'
175-
onChange={this.onFormChange}
176-
disabled={disableForm} />
177-
</div>
173+
<Field
174+
label={_t("Enter passphrase")}
175+
value={this.state.passphrase}
176+
onChange={this.onPassphraseChange}
177+
size={64}
178+
type="password"
179+
disabled={disableForm}
180+
/>
178181
</div>
179182
</div>
180183
</div>

0 commit comments

Comments
 (0)