Further password reset flow enhancements (#9662)
This commit is contained in:
parent
82ad8d5aa2
commit
89439d4f10
10 changed files with 241 additions and 67 deletions
|
@ -19,6 +19,7 @@ limitations under the License.
|
|||
import React, { ReactNode } from 'react';
|
||||
import { logger } from 'matrix-js-sdk/src/logger';
|
||||
import { createClient } from "matrix-js-sdk/src/matrix";
|
||||
import { sleep } from 'matrix-js-sdk/src/utils';
|
||||
|
||||
import { _t, _td } from '../../../languageHandler';
|
||||
import Modal from "../../../Modal";
|
||||
|
@ -43,6 +44,8 @@ import Spinner from '../../views/elements/Spinner';
|
|||
import { formatSeconds } from '../../../DateUtils';
|
||||
import AutoDiscoveryUtils from '../../../utils/AutoDiscoveryUtils';
|
||||
|
||||
const emailCheckInterval = 2000;
|
||||
|
||||
enum Phase {
|
||||
// Show email input
|
||||
EnterEmail = 1,
|
||||
|
@ -60,7 +63,7 @@ enum Phase {
|
|||
|
||||
interface Props {
|
||||
serverConfig: ValidatedServerConfig;
|
||||
onLoginClick?: () => void;
|
||||
onLoginClick: () => void;
|
||||
onComplete: () => void;
|
||||
}
|
||||
|
||||
|
@ -277,22 +280,43 @@ export default class ForgotPassword extends React.Component<Props, State> {
|
|||
{
|
||||
email: this.state.email,
|
||||
errorText: this.state.errorText,
|
||||
onCloseClick: () => {
|
||||
modal.close();
|
||||
this.setState({ phase: Phase.PasswordInput });
|
||||
},
|
||||
onReEnterEmailClick: () => {
|
||||
modal.close();
|
||||
this.setState({ phase: Phase.EnterEmail });
|
||||
},
|
||||
onResendClick: this.sendVerificationMail,
|
||||
},
|
||||
"mx_VerifyEMailDialog",
|
||||
false,
|
||||
false,
|
||||
{
|
||||
// this modal cannot be dismissed except reset is done or forced
|
||||
onBeforeClose: async (reason?: string) => {
|
||||
return this.state.phase === Phase.Done || reason === "force";
|
||||
if (reason === "backgroundClick") {
|
||||
// Modal dismissed by clicking the background.
|
||||
// Go one phase back.
|
||||
this.setState({ phase: Phase.PasswordInput });
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
await this.reset.retrySetNewPassword(this.state.password);
|
||||
this.phase = Phase.Done;
|
||||
modal.close();
|
||||
// Don't retry if the phase changed. For example when going back to email input.
|
||||
while (this.state.phase === Phase.ResettingPassword) {
|
||||
try {
|
||||
await this.reset.setNewPassword(this.state.password);
|
||||
this.setState({ phase: Phase.Done });
|
||||
modal.close();
|
||||
} catch (e) {
|
||||
// Email not confirmed, yet. Retry after a while.
|
||||
await sleep(emailCheckInterval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private onSubmitForm = async (ev: React.FormEvent): Promise<void> => {
|
||||
|
@ -339,6 +363,7 @@ export default class ForgotPassword extends React.Component<Props, State> {
|
|||
homeserver={this.props.serverConfig.hsName}
|
||||
loading={this.state.phase === Phase.SendingEmail}
|
||||
onInputChanged={this.onInputChanged}
|
||||
onLoginClick={this.props.onLoginClick!} // set by default props
|
||||
onSubmitForm={this.onSubmitForm}
|
||||
/>;
|
||||
}
|
||||
|
@ -374,6 +399,7 @@ export default class ForgotPassword extends React.Component<Props, State> {
|
|||
return <CheckEmail
|
||||
email={this.state.email}
|
||||
errorText={this.state.errorText}
|
||||
onReEnterEmailClick={() => this.setState({ phase: Phase.EnterEmail })}
|
||||
onResendClick={this.sendVerificationMail}
|
||||
onSubmitForm={this.onSubmitForm}
|
||||
/>;
|
||||
|
|
|
@ -27,6 +27,7 @@ import { ErrorMessage } from "../../ErrorMessage";
|
|||
interface CheckEmailProps {
|
||||
email: string;
|
||||
errorText: string | ReactNode | null;
|
||||
onReEnterEmailClick: () => void;
|
||||
onResendClick: () => Promise<boolean>;
|
||||
onSubmitForm: (ev: React.FormEvent) => void;
|
||||
}
|
||||
|
@ -37,6 +38,7 @@ interface CheckEmailProps {
|
|||
export const CheckEmail: React.FC<CheckEmailProps> = ({
|
||||
email,
|
||||
errorText,
|
||||
onReEnterEmailClick,
|
||||
onSubmitForm,
|
||||
onResendClick,
|
||||
}) => {
|
||||
|
@ -50,13 +52,32 @@ export const CheckEmail: React.FC<CheckEmailProps> = ({
|
|||
return <>
|
||||
<EMailPromptIcon className="mx_AuthBody_emailPromptIcon--shifted" />
|
||||
<h1>{ _t("Check your email to continue") }</h1>
|
||||
<p>
|
||||
{ _t(
|
||||
"Follow the instructions sent to <b>%(email)s</b>",
|
||||
{ email: email },
|
||||
{ b: t => <b>{ t }</b> },
|
||||
) }
|
||||
</p>
|
||||
<div className="mx_AuthBody_text">
|
||||
<p>
|
||||
{ _t(
|
||||
"Follow the instructions sent to <b>%(email)s</b>",
|
||||
{ email: email },
|
||||
{ b: t => <b>{ t }</b> },
|
||||
) }
|
||||
</p>
|
||||
<div className="mx_AuthBody_did-not-receive">
|
||||
<span className="mx_VerifyEMailDialog_text-light">{ _t("Wrong email address?") }</span>
|
||||
<AccessibleButton
|
||||
className="mx_AuthBody_resend-button"
|
||||
kind="link"
|
||||
onClick={onReEnterEmailClick}
|
||||
>
|
||||
{ _t("Re-enter email address") }
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
</div>
|
||||
{ errorText && <ErrorMessage message={errorText} /> }
|
||||
<input
|
||||
onClick={onSubmitForm}
|
||||
type="button"
|
||||
className="mx_Login_submit"
|
||||
value={_t("Next")}
|
||||
/>
|
||||
<div className="mx_AuthBody_did-not-receive">
|
||||
<span className="mx_VerifyEMailDialog_text-light">{ _t("Did not receive it?") }</span>
|
||||
<AccessibleButton
|
||||
|
@ -73,12 +94,5 @@ export const CheckEmail: React.FC<CheckEmailProps> = ({
|
|||
/>
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
{ errorText && <ErrorMessage message={errorText} /> }
|
||||
<input
|
||||
onClick={onSubmitForm}
|
||||
type="button"
|
||||
className="mx_Login_submit"
|
||||
value={_t("Next")}
|
||||
/>
|
||||
</>;
|
||||
};
|
||||
|
|
|
@ -22,6 +22,7 @@ import EmailField from "../../../views/auth/EmailField";
|
|||
import { ErrorMessage } from "../../ErrorMessage";
|
||||
import Spinner from "../../../views/elements/Spinner";
|
||||
import Field from "../../../views/elements/Field";
|
||||
import AccessibleButton from "../../../views/elements/AccessibleButton";
|
||||
|
||||
interface EnterEmailProps {
|
||||
email: string;
|
||||
|
@ -29,6 +30,7 @@ interface EnterEmailProps {
|
|||
homeserver: string;
|
||||
loading: boolean;
|
||||
onInputChanged: (stateKey: string, ev: React.FormEvent<HTMLInputElement>) => void;
|
||||
onLoginClick: () => void;
|
||||
onSubmitForm: (ev: React.FormEvent) => void;
|
||||
}
|
||||
|
||||
|
@ -41,6 +43,7 @@ export const EnterEmail: React.FC<EnterEmailProps> = ({
|
|||
homeserver,
|
||||
loading,
|
||||
onInputChanged,
|
||||
onLoginClick,
|
||||
onSubmitForm,
|
||||
}) => {
|
||||
const submitButtonChild = loading
|
||||
|
@ -92,6 +95,15 @@ export const EnterEmail: React.FC<EnterEmailProps> = ({
|
|||
>
|
||||
{ submitButtonChild }
|
||||
</button>
|
||||
<div className="mx_AuthBody_button-container">
|
||||
<AccessibleButton
|
||||
className="mx_AuthBody_sign-in-instead-button"
|
||||
element="button"
|
||||
kind="link"
|
||||
onClick={onLoginClick}>
|
||||
{ _t("Sign in instead") }
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</>;
|
||||
|
|
|
@ -27,12 +27,16 @@ import { ErrorMessage } from "../../ErrorMessage";
|
|||
interface Props {
|
||||
email: string;
|
||||
errorText: string | null;
|
||||
onCloseClick: () => void;
|
||||
onReEnterEmailClick: () => void;
|
||||
onResendClick: () => Promise<boolean>;
|
||||
}
|
||||
|
||||
export const VerifyEmailModal: React.FC<Props> = ({
|
||||
email,
|
||||
errorText,
|
||||
onCloseClick,
|
||||
onReEnterEmailClick,
|
||||
onResendClick,
|
||||
}) => {
|
||||
const { toggle: toggleTooltipVisible, value: tooltipVisible } = useTimeoutToggle(false, 2500);
|
||||
|
@ -57,7 +61,8 @@ export const VerifyEmailModal: React.FC<Props> = ({
|
|||
},
|
||||
) }
|
||||
</p>
|
||||
<div className="mx_AuthBody_did-not-receive mx_AuthBody_did-not-receive--centered">
|
||||
|
||||
<div className="mx_AuthBody_did-not-receive">
|
||||
<span className="mx_VerifyEMailDialog_text-light">{ _t("Did not receive it?") }</span>
|
||||
<AccessibleButton
|
||||
className="mx_AuthBody_resend-button"
|
||||
|
@ -74,5 +79,22 @@ export const VerifyEmailModal: React.FC<Props> = ({
|
|||
</AccessibleButton>
|
||||
{ errorText && <ErrorMessage message={errorText} /> }
|
||||
</div>
|
||||
|
||||
<div className="mx_AuthBody_did-not-receive">
|
||||
<span className="mx_VerifyEMailDialog_text-light">{ _t("Wrong email address?") }</span>
|
||||
<AccessibleButton
|
||||
className="mx_AuthBody_resend-button"
|
||||
kind="link"
|
||||
onClick={onReEnterEmailClick}
|
||||
>
|
||||
{ _t("Re-enter email address") }
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
|
||||
<AccessibleButton
|
||||
onClick={onCloseClick}
|
||||
className="mx_Dialog_cancelButton"
|
||||
aria-label={_t("Close dialog")}
|
||||
/>
|
||||
</>;
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue