diff --git a/res/css/views/dialogs/keybackup/_CreateKeyBackupDialog.scss b/res/css/views/dialogs/keybackup/_CreateKeyBackupDialog.scss index 04ee575867..b9babd05f5 100644 --- a/res/css/views/dialogs/keybackup/_CreateKeyBackupDialog.scss +++ b/res/css/views/dialogs/keybackup/_CreateKeyBackupDialog.scss @@ -85,3 +85,9 @@ limitations under the License. flex: 1; white-space: nowrap; } + +.mx_CreateKeyBackupDialog { + details .mx_AccessibleButton { + margin: 1em 0; // emulate paragraph spacing because we can't put this button in a paragraph due to HTML rules + } +} diff --git a/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js b/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js index 8fe9373218..f89fb17448 100644 --- a/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js +++ b/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js @@ -24,6 +24,7 @@ import { scorePassword } from '../../../../utils/PasswordScorer'; import { _t } from '../../../../languageHandler'; import { accessSecretStorage } from '../../../../CrossSigningManager'; import SettingsStore from '../../../../settings/SettingsStore'; +import AccessibleButton from "../../../../components/views/elements/AccessibleButton"; const PHASE_PASSPHRASE = 0; const PHASE_PASSPHRASE_CONFIRM = 1; @@ -191,44 +192,38 @@ export default class CreateKeyBackupDialog extends React.PureComponent { }); } - _onPassPhraseNextClick = () => { - this.setState({phase: PHASE_PASSPHRASE_CONFIRM}); - } + _onPassPhraseNextClick = async (e) => { + e.preventDefault(); - _onPassPhraseKeyPress = async (e) => { - if (e.key === 'Enter') { - // If we're waiting for the timeout before updating the result at this point, - // skip ahead and do it now, otherwise we'll deny the attempt to proceed - // even if the user entered a valid passphrase - if (this._setZxcvbnResultTimeout !== null) { - clearTimeout(this._setZxcvbnResultTimeout); - this._setZxcvbnResultTimeout = null; - await new Promise((resolve) => { - this.setState({ - zxcvbnResult: scorePassword(this.state.passPhrase), - }, resolve); - }); - } - if (this._passPhraseIsValid()) { - this._onPassPhraseNextClick(); - } + // If we're waiting for the timeout before updating the result at this point, + // skip ahead and do it now, otherwise we'll deny the attempt to proceed + // even if the user entered a valid passphrase + if (this._setZxcvbnResultTimeout !== null) { + clearTimeout(this._setZxcvbnResultTimeout); + this._setZxcvbnResultTimeout = null; + await new Promise((resolve) => { + this.setState({ + zxcvbnResult: scorePassword(this.state.passPhrase), + }, resolve); + }); } - } + if (this._passPhraseIsValid()) { + this.setState({phase: PHASE_PASSPHRASE_CONFIRM}); + } + }; + + _onPassPhraseConfirmNextClick = async (e) => { + e.preventDefault(); + + if (this.state.passPhrase !== this.state.passPhraseConfirm) return; - _onPassPhraseConfirmNextClick = async () => { this._keyBackupInfo = await MatrixClientPeg.get().prepareKeyBackupVersion(this.state.passPhrase); this.setState({ copied: false, downloaded: false, phase: PHASE_SHOWKEY, }); - } - - _onPassPhraseConfirmKeyPress = (e) => { - if (e.key === 'Enter' && this.state.passPhrase === this.state.passPhraseConfirm) { - this._onPassPhraseConfirmNextClick(); - } - } + }; _onSetAgainClick = () => { this.setState({ @@ -299,7 +294,7 @@ export default class CreateKeyBackupDialog extends React.PureComponent { ; } - return
+ return

{_t( "Warning: You should only set up key backup from a trusted computer.", {}, { b: sub => {sub} }, @@ -314,7 +309,6 @@ export default class CreateKeyBackupDialog extends React.PureComponent {

- {_t("Advanced")} -

+ -
; + ; } _renderPhasePassPhraseConfirm() { @@ -371,7 +366,7 @@ export default class CreateKeyBackupDialog extends React.PureComponent { ; } const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); - return
+ return

{_t( "Please enter your passphrase a second time to confirm.", )}

@@ -380,7 +375,6 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
- -
; + ; } _renderPhaseShowKey() { diff --git a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js index 679b3907d1..f68ee32a15 100644 --- a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js +++ b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js @@ -289,31 +289,31 @@ export default class CreateSecretStorageDialog extends React.PureComponent { }); } - _onPassPhraseNextClick = () => { - this.setState({phase: PHASE_PASSPHRASE_CONFIRM}); - } + _onPassPhraseNextClick = async (e) => { + e.preventDefault(); - _onPassPhraseKeyPress = async (e) => { - if (e.key === 'Enter') { - // If we're waiting for the timeout before updating the result at this point, - // skip ahead and do it now, otherwise we'll deny the attempt to proceed - // even if the user entered a valid passphrase - if (this._setZxcvbnResultTimeout !== null) { - clearTimeout(this._setZxcvbnResultTimeout); - this._setZxcvbnResultTimeout = null; - await new Promise((resolve) => { - this.setState({ - zxcvbnResult: scorePassword(this.state.passPhrase), - }, resolve); - }); - } - if (this._passPhraseIsValid()) { - this._onPassPhraseNextClick(); - } + // If we're waiting for the timeout before updating the result at this point, + // skip ahead and do it now, otherwise we'll deny the attempt to proceed + // even if the user entered a valid passphrase + if (this._setZxcvbnResultTimeout !== null) { + clearTimeout(this._setZxcvbnResultTimeout); + this._setZxcvbnResultTimeout = null; + await new Promise((resolve) => { + this.setState({ + zxcvbnResult: scorePassword(this.state.passPhrase), + }, resolve); + }); } - } + if (this._passPhraseIsValid()) { + this.setState({phase: PHASE_PASSPHRASE_CONFIRM}); + } + }; + + _onPassPhraseConfirmNextClick = async (e) => { + e.preventDefault(); + + if (this.state.passPhrase !== this.state.passPhraseConfirm) return; - _onPassPhraseConfirmNextClick = async () => { const [keyInfo, encodedRecoveryKey] = await MatrixClientPeg.get().createRecoveryKeyFromPassphrase(this.state.passPhrase); this._keyInfo = keyInfo; @@ -325,12 +325,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent { }); } - _onPassPhraseConfirmKeyPress = (e) => { - if (e.key === 'Enter' && this.state.passPhrase === this.state.passPhraseConfirm) { - this._onPassPhraseConfirmNextClick(); - } - } - _onSetAgainClick = () => { this.setState({ passPhrase: '', @@ -400,7 +394,8 @@ export default class CreateSecretStorageDialog extends React.PureComponent { } else if (this.state.canUploadKeysWithPasswordOnly) { authPrompt =
{_t("Enter your account password to confirm the upgrade:")}
-
{authPrompt}
- @@ -467,7 +462,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
; } - return
+ return

{_t( "Set up encryption on this session to allow it to verify other sessions, " + "granting them access to encrypted messages and marking them as trusted for other users.", @@ -483,10 +478,10 @@ export default class CreateSecretStorageDialog extends React.PureComponent { id="mx_CreateSecretStorageDialog_passPhraseField" className="mx_CreateSecretStorageDialog_passPhraseField" onChange={this._onPassPhraseChange} - onKeyPress={this._onPassPhraseKeyPress} value={this.state.passPhrase} label={_t("Enter a passphrase")} autoFocus={true} + autoComplete="new-password" />

{strengthMeter} @@ -499,7 +494,8 @@ export default class CreateSecretStorageDialog extends React.PureComponent { onChange={this._onUseKeyBackupChange} value={this.state.useKeyBackup} /> - -
; +
; } _renderPhasePassPhraseConfirm() { @@ -549,25 +545,27 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
; } const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); - return
+ return

{_t( "Enter your passphrase a second time to confirm it.", )}

-
{passPhraseMatch}
- {_t("Skip")} -
; + ; } _renderPhaseShowKey() { diff --git a/src/components/structures/SearchBox.js b/src/components/structures/SearchBox.js index 873efb64c2..e169e09752 100644 --- a/src/components/structures/SearchBox.js +++ b/src/components/structures/SearchBox.js @@ -160,6 +160,7 @@ export default createReactClass({ onKeyDown={ this._onKeyDown } onBlur={this._onBlur} placeholder={ placeholder } + autoComplete="off" /> { clearButton }
diff --git a/src/components/views/auth/RegistrationForm.js b/src/components/views/auth/RegistrationForm.js index 91f8e1b226..8ca454dabd 100644 --- a/src/components/views/auth/RegistrationForm.js +++ b/src/components/views/auth/RegistrationForm.js @@ -486,6 +486,7 @@ export default createReactClass({ id="mx_RegistrationForm_password" ref={field => this[FIELD_PASSWORD] = field} type="password" + autoComplete="new-password" label={_t("Password")} value={this.state.password} onChange={this.onPasswordChange} @@ -499,6 +500,7 @@ export default createReactClass({ id="mx_RegistrationForm_passwordConfirm" ref={field => this[FIELD_PASSWORD_CONFIRM] = field} type="password" + autoComplete="new-password" label={_t("Confirm")} value={this.state.passwordConfirm} onChange={this.onPasswordConfirmChange} diff --git a/src/components/views/dialogs/DeactivateAccountDialog.js b/src/components/views/dialogs/DeactivateAccountDialog.js index 7e36232eb0..d7468933df 100644 --- a/src/components/views/dialogs/DeactivateAccountDialog.js +++ b/src/components/views/dialogs/DeactivateAccountDialog.js @@ -118,6 +118,7 @@ export default class DeactivateAccountDialog extends React.Component { const Field = sdk.getComponent('elements.Field'); + // this is on purpose not a
to prevent Enter triggering submission, to further prevent accidents return ( { + if (!this.state.recoveryKeyValid) return; + this.setState({ loading: true, restoreError: null, @@ -157,18 +158,6 @@ export default class RestoreKeyBackupDialog extends React.PureComponent { }); } - _onPassPhraseKeyPress = (e) => { - if (e.key === Key.ENTER) { - this._onPassPhraseNext(); - } - } - - _onRecoveryKeyKeyPress = (e) => { - if (e.key === Key.ENTER && this.state.recoveryKeyValid) { - this._onRecoveryKeyNext(); - } - } - async _restoreWithSecretStorage() { this.setState({ loading: true, @@ -305,21 +294,22 @@ export default class RestoreKeyBackupDialog extends React.PureComponent { "messaging by entering your recovery passphrase.", )}

-
+ - -
+ {_t( "If you've forgotten your recovery passphrase you can "+ "use your recovery key or " + @@ -371,7 +361,6 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
diff --git a/src/components/views/dialogs/secretstorage/AccessSecretStorageDialog.js b/src/components/views/dialogs/secretstorage/AccessSecretStorageDialog.js index 08d9f8dd5e..68ae8281c4 100644 --- a/src/components/views/dialogs/secretstorage/AccessSecretStorageDialog.js +++ b/src/components/views/dialogs/secretstorage/AccessSecretStorageDialog.js @@ -1,6 +1,6 @@ /* Copyright 2018, 2019 New Vector Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. +Copyright 2019, 2020 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. @@ -21,7 +21,6 @@ import * as sdk from '../../../../index'; import {MatrixClientPeg} from '../../../../MatrixClientPeg'; import { _t } from '../../../../languageHandler'; -import { Key } from "../../../../Keyboard"; /* * Access Secure Secret Storage by requesting the user's passphrase. @@ -68,7 +67,11 @@ export default class AccessSecretStorageDialog extends React.PureComponent { }); } - _onPassPhraseNext = async () => { + _onPassPhraseNext = async (e) => { + e.preventDefault(); + + if (this.state.passPhrase.length <= 0) return; + this.setState({ keyMatches: null }); const input = { passphrase: this.state.passPhrase }; const keyMatches = await this.props.checkPrivateKey(input); @@ -79,7 +82,11 @@ export default class AccessSecretStorageDialog extends React.PureComponent { } } - _onRecoveryKeyNext = async () => { + _onRecoveryKeyNext = async (e) => { + e.preventDefault(); + + if (!this.state.recoveryKeyValid) return; + this.setState({ keyMatches: null }); const input = { recoveryKey: this.state.recoveryKey }; const keyMatches = await this.props.checkPrivateKey(input); @@ -97,18 +104,6 @@ export default class AccessSecretStorageDialog extends React.PureComponent { }); } - _onPassPhraseKeyPress = (e) => { - if (e.key === Key.ENTER && this.state.passPhrase.length > 0) { - this._onPassPhraseNext(); - } - } - - _onRecoveryKeyKeyPress = (e) => { - if (e.key === Key.ENTER && this.state.recoveryKeyValid) { - this._onRecoveryKeyNext(); - } - } - render() { const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); @@ -135,7 +130,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent { )}
; } else { - keyStatus =
; + keyStatus =
; } content =
@@ -149,23 +144,25 @@ export default class AccessSecretStorageDialog extends React.PureComponent { "identity for verifying other sessions by entering your passphrase.", )}

-
- + {keyStatus} - -
+ {_t( "If you've forgotten your passphrase you can "+ "use your recovery key or " + @@ -192,7 +189,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent { let keyStatus; if (this.state.recoveryKey.length === 0) { - keyStatus =
; + keyStatus =
; } else if (this.state.recoveryKeyValid) { keyStatus =
{"\uD83D\uDC4D "}{_t("This looks like a valid recovery key!")} @@ -221,22 +218,22 @@ export default class AccessSecretStorageDialog extends React.PureComponent { "identity for verifying other sessions by entering your recovery key.", )}

-
+
{keyStatus} - -
+ {_t( "If you've forgotten your recovery key you can "+ "." diff --git a/src/components/views/settings/ChangePassword.js b/src/components/views/settings/ChangePassword.js index 291313a463..3ba276d1fa 100644 --- a/src/components/views/settings/ChangePassword.js +++ b/src/components/views/settings/ChangePassword.js @@ -253,20 +253,24 @@ export default createReactClass({
{ currentPassword }
-
-