Merge branch 'develop' into sort-imports

Signed-off-by: Aaron Raimist <aaron@raim.ist>
This commit is contained in:
Aaron Raimist 2021-12-09 08:34:20 +00:00
commit 7b94e13a84
642 changed files with 30052 additions and 8035 deletions

View file

@ -17,11 +17,12 @@ limitations under the License.
import React from 'react';
import { _t } from '../../../languageHandler';
import * as sdk from '../../../index';
import { SetupEncryptionStore, Phase } from '../../../stores/SetupEncryptionStore';
import SetupEncryptionBody from "./SetupEncryptionBody";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import AccessibleButton from '../../views/elements/AccessibleButton';
import CompleteSecurityBody from "../../views/auth/CompleteSecurityBody";
import AuthPage from "../../views/auth/AuthPage";
interface IProps {
onFinished: () => void;
@ -59,8 +60,6 @@ export default class CompleteSecurity extends React.Component<IProps, IState> {
}
public render() {
const AuthPage = sdk.getComponent("auth.AuthPage");
const CompleteSecurityBody = sdk.getComponent("auth.CompleteSecurityBody");
const { phase, lostKeys } = this.state;
let icon;
let title;

View file

@ -17,14 +17,11 @@ limitations under the License.
*/
import React from 'react';
import classNames from 'classnames';
import { logger } from "matrix-js-sdk/src/logger";
import { _t, _td } from '../../../languageHandler';
import * as sdk from '../../../index';
import Modal from "../../../Modal";
import PasswordReset from "../../../PasswordReset";
import AutoDiscoveryUtils, { ValidatedServerConfig } from "../../../utils/AutoDiscoveryUtils";
import classNames from 'classnames';
import AuthPage from "../../views/auth/AuthPage";
import CountlyAnalytics from "../../../CountlyAnalytics";
import ServerPicker from "../../views/elements/ServerPicker";
@ -32,8 +29,14 @@ import EmailField from "../../views/auth/EmailField";
import PassphraseField from '../../views/auth/PassphraseField';
import { replaceableComponent } from "../../../utils/replaceableComponent";
import { PASSWORD_MIN_SCORE } from '../../views/auth/RegistrationForm';
import { IValidationResult } from "../../views/elements/Validation";
import InlineSpinner from '../../views/elements/InlineSpinner';
import { logger } from "matrix-js-sdk/src/logger";
import Spinner from "../../views/elements/Spinner";
import QuestionDialog from "../../views/dialogs/QuestionDialog";
import ErrorDialog from "../../views/dialogs/ErrorDialog";
import AuthHeader from "../../views/auth/AuthHeader";
import AuthBody from "../../views/auth/AuthBody";
import PassphraseConfirmField from "../../views/auth/PassphraseConfirmField";
enum Phase {
// Show the forgot password inputs
@ -68,11 +71,15 @@ interface IState {
serverErrorIsFatal: boolean;
serverDeadError: string;
emailFieldValid: boolean;
passwordFieldValid: boolean;
currentHttpRequest?: Promise<any>;
}
enum ForgotPasswordField {
Email = 'field_email',
Password = 'field_password',
PasswordConfirm = 'field_password_confirm',
}
@replaceableComponent("structures.auth.ForgotPassword")
export default class ForgotPassword extends React.Component<IProps, IState> {
private reset: PasswordReset;
@ -91,8 +98,6 @@ export default class ForgotPassword extends React.Component<IProps, IState> {
serverIsAlive: true,
serverErrorIsFatal: false,
serverDeadError: "",
emailFieldValid: false,
passwordFieldValid: false,
};
constructor(props: IProps) {
@ -171,42 +176,58 @@ export default class ForgotPassword extends React.Component<IProps, IState> {
// refresh the server errors, just in case the server came back online
await this.handleHttpRequest(this.checkServerLiveliness(this.props.serverConfig));
await this['email_field'].validate({ allowEmpty: false });
await this['password_field'].validate({ allowEmpty: false });
if (!this.state.email) {
this.showErrorDialog(_t('The email address linked to your account must be entered.'));
} else if (!this.state.emailFieldValid) {
this.showErrorDialog(_t("The email address doesn't appear to be valid."));
} else if (!this.state.password || !this.state.password2) {
this.showErrorDialog(_t('A new password must be entered.'));
} else if (!this.state.passwordFieldValid) {
this.showErrorDialog(_t('Please choose a strong password'));
} else if (this.state.password !== this.state.password2) {
this.showErrorDialog(_t('New passwords must match each other.'));
} else {
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createTrackedDialog('Forgot Password Warning', '', QuestionDialog, {
title: _t('Warning!'),
description:
<div>
{ _t(
"Changing your password will reset any end-to-end encryption keys " +
"on all of your sessions, making encrypted chat history unreadable. Set up " +
"Key Backup or export your room keys from another session before resetting your " +
"password.",
) }
</div>,
button: _t('Continue'),
onFinished: (confirmed) => {
if (confirmed) {
this.submitPasswordReset(this.state.email, this.state.password);
}
},
});
const allFieldsValid = await this.verifyFieldsBeforeSubmit();
if (!allFieldsValid) {
return;
}
Modal.createTrackedDialog('Forgot Password Warning', '', QuestionDialog, {
title: _t('Warning!'),
description:
<div>
{ _t(
"Changing your password will reset any end-to-end encryption keys " +
"on all of your sessions, making encrypted chat history unreadable. Set up " +
"Key Backup or export your room keys from another session before resetting your " +
"password.",
) }
</div>,
button: _t('Continue'),
onFinished: (confirmed) => {
if (confirmed) {
this.submitPasswordReset(this.state.email, this.state.password);
}
},
});
};
private async verifyFieldsBeforeSubmit() {
const fieldIdsInDisplayOrder = [
ForgotPasswordField.Email,
ForgotPasswordField.Password,
ForgotPasswordField.PasswordConfirm,
];
const invalidFields = [];
for (const fieldId of fieldIdsInDisplayOrder) {
const valid = await this[fieldId].validate({ allowEmpty: false });
if (!valid) {
invalidFields.push(this[fieldId]);
}
}
if (invalidFields.length === 0) {
return true;
}
// Focus on the first invalid field, then re-validate,
// which will result in the error tooltip being displayed for that field.
invalidFields[0].focus();
invalidFields[0].validate({ allowEmpty: false, focused: true });
return false;
}
private onInputChanged = (stateKey: string, ev: React.FormEvent<HTMLInputElement>) => {
this.setState({
[stateKey]: ev.currentTarget.value,
@ -220,25 +241,12 @@ export default class ForgotPassword extends React.Component<IProps, IState> {
};
public showErrorDialog(description: string, title?: string) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Forgot Password Error', '', ErrorDialog, {
title,
description,
});
}
private onEmailValidate = (result: IValidationResult) => {
this.setState({
emailFieldValid: result.valid,
});
};
private onPasswordValidate(result: IValidationResult) {
this.setState({
passwordFieldValid: result.valid,
});
}
private handleHttpRequest<T = unknown>(request: Promise<T>): Promise<T> {
this.setState({
currentHttpRequest: request,
@ -251,8 +259,6 @@ export default class ForgotPassword extends React.Component<IProps, IState> {
}
renderForgot() {
const Field = sdk.getComponent('elements.Field');
let errorText = null;
const err = this.state.errorText;
if (err) {
@ -284,11 +290,12 @@ export default class ForgotPassword extends React.Component<IProps, IState> {
<div className="mx_AuthBody_fieldRow">
<EmailField
name="reset_email" // define a name so browser's password autofill gets less confused
labelRequired={_td('The email address linked to your account must be entered.')}
labelInvalid={_td("The email address doesn't appear to be valid.")}
value={this.state.email}
fieldRef={field => this['email_field'] = field}
fieldRef={field => this[ForgotPasswordField.Email] = field}
autoFocus={true}
onChange={this.onInputChanged.bind(this, "email")}
onValidate={this.onEmailValidate}
onFocus={() => CountlyAnalytics.instance.track("onboarding_forgot_password_email_focus")}
onBlur={() => CountlyAnalytics.instance.track("onboarding_forgot_password_email_blur")}
/>
@ -300,18 +307,20 @@ export default class ForgotPassword extends React.Component<IProps, IState> {
label={_td('New Password')}
value={this.state.password}
minScore={PASSWORD_MIN_SCORE}
fieldRef={field => this[ForgotPasswordField.Password] = field}
onChange={this.onInputChanged.bind(this, "password")}
fieldRef={field => this['password_field'] = field}
onValidate={(result) => this.onPasswordValidate(result)}
onFocus={() => CountlyAnalytics.instance.track("onboarding_forgot_password_newPassword_focus")}
onBlur={() => CountlyAnalytics.instance.track("onboarding_forgot_password_newPassword_blur")}
autoComplete="new-password"
/>
<Field
<PassphraseConfirmField
name="reset_password_confirm"
type="password"
label={_t('Confirm')}
label={_td('Confirm')}
labelRequired={_td("A new password must be entered.")}
labelInvalid={_td("New passwords must match each other.")}
value={this.state.password2}
password={this.state.password}
fieldRef={field => this[ForgotPasswordField.PasswordConfirm] = field}
onChange={this.onInputChanged.bind(this, "password2")}
onFocus={() => CountlyAnalytics.instance.track("onboarding_forgot_password_newPassword2_focus")}
onBlur={() => CountlyAnalytics.instance.track("onboarding_forgot_password_newPassword2_blur")}
@ -335,7 +344,6 @@ export default class ForgotPassword extends React.Component<IProps, IState> {
}
renderSendingEmail() {
const Spinner = sdk.getComponent("elements.Spinner");
return <Spinner />;
}
@ -372,9 +380,6 @@ export default class ForgotPassword extends React.Component<IProps, IState> {
}
render() {
const AuthHeader = sdk.getComponent("auth.AuthHeader");
const AuthBody = sdk.getComponent("auth.AuthBody");
let resetPasswordJsx;
switch (this.state.phase) {
case Phase.Forgot:

View file

@ -303,7 +303,7 @@ export default class Registration extends React.Component<IProps, IState> {
errorText = _t('This server does not support authentication with a phone number.');
}
} else if (response.errcode === "M_USER_IN_USE") {
errorText = _t("That username already exists, please try another.");
errorText = _t("Someone already has that username, please try another.");
} else if (response.errcode === "M_THREEPID_IN_USE") {
errorText = _t("That e-mail address is already in use.");
}
@ -509,6 +509,7 @@ export default class Registration extends React.Component<IProps, IState> {
flows={this.state.flows}
serverConfig={this.props.serverConfig}
canSubmit={!this.state.serverErrorIsFatal}
matrixClient={this.state.matrixClient}
/>
</React.Fragment>;
}

View file

@ -39,7 +39,7 @@ function keyHasPassphrase(keyInfo: ISecretStorageKeyInfo): boolean {
}
interface IProps {
onFinished: (boolean) => void;
onFinished: () => void;
}
interface IState {
@ -70,7 +70,7 @@ export default class SetupEncryptionBody extends React.Component<IProps, IState>
private onStoreUpdate = () => {
const store = SetupEncryptionStore.sharedInstance();
if (store.phase === Phase.Finished) {
this.props.onFinished(true);
this.props.onFinished();
return;
}
this.setState({
@ -97,13 +97,16 @@ export default class SetupEncryptionBody extends React.Component<IProps, IState>
const userId = cli.getUserId();
const requestPromise = cli.requestVerification(userId);
this.props.onFinished(true);
// We need to call onFinished now to close this dialog, and
// again later to signal that the verification is complete.
this.props.onFinished();
Modal.createTrackedDialog('New Session Verification', 'Starting dialog', VerificationRequestDialog, {
verificationRequestPromise: requestPromise,
member: cli.getUser(userId),
onFinished: async () => {
const request = await requestPromise;
request.cancel();
this.props.onFinished();
},
});
};
@ -125,6 +128,7 @@ export default class SetupEncryptionBody extends React.Component<IProps, IState>
};
private onResetConfirmClick = () => {
this.props.onFinished();
const store = SetupEncryptionStore.sharedInstance();
store.resetConfirm();
};
@ -140,7 +144,7 @@ export default class SetupEncryptionBody extends React.Component<IProps, IState>
};
private onEncryptionPanelClose = () => {
this.props.onFinished(false);
this.props.onFinished();
};
public render() {
@ -249,7 +253,7 @@ export default class SetupEncryptionBody extends React.Component<IProps, IState>
return (
<div>
<p>{ _t(
"Without verifying, you wont have access to all your messages " +
"Without verifying, you won't have access to all your messages " +
"and may appear as untrusted to others.",
) }</p>
<div className="mx_CompleteSecurity_actionRow">

View file

@ -215,7 +215,7 @@ export default class SoftLogout extends React.Component<IProps, IState> {
if (this.state.keyBackupNeeded) {
introText = _t(
"Regain access to your account and recover encryption keys stored in this session. " +
"Without them, you wont be able to read all of your secure messages in any session.");
"Without them, you won't be able to read all of your secure messages in any session.");
}
if (this.state.loginView === LOGIN_VIEW.PASSWORD) {