Skip to content

Commit 1e50246

Browse files
authored
Merge pull request matrix-org#6636 from matrix-org/palid/fix/forgot-password-ux
Make ForgotPassword UX slightly more user friendly
2 parents 9dee3eb + f50d9b7 commit 1e50246

File tree

2 files changed

+31
-3
lines changed

2 files changed

+31
-3
lines changed

res/css/structures/auth/_Login.scss

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,10 @@ div.mx_AccessibleButton_kind_link.mx_Login_forgot {
9696
cursor: not-allowed;
9797
}
9898
}
99+
.mx_Login_spinner {
100+
display: flex;
101+
justify-content: center;
102+
align-items: center;
103+
align-content: center;
104+
padding: 14px;
105+
}

src/components/structures/auth/ForgotPassword.tsx

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { replaceableComponent } from "../../../utils/replaceableComponent";
3131
import { PASSWORD_MIN_SCORE } from '../../views/auth/RegistrationForm';
3232

3333
import { IValidationResult } from "../../views/elements/Validation";
34+
import InlineSpinner from '../../views/elements/InlineSpinner';
3435

3536
enum Phase {
3637
// Show the forgot password inputs
@@ -66,13 +67,14 @@ interface IState {
6667
serverDeadError: string;
6768

6869
passwordFieldValid: boolean;
70+
currentHttpRequest?: Promise<any>;
6971
}
7072

7173
@replaceableComponent("structures.auth.ForgotPassword")
7274
export default class ForgotPassword extends React.Component<IProps, IState> {
7375
private reset: PasswordReset;
7476

75-
state = {
77+
state: IState = {
7678
phase: Phase.Forgot,
7779
email: "",
7880
password: "",
@@ -148,8 +150,10 @@ export default class ForgotPassword extends React.Component<IProps, IState> {
148150
console.error("onVerify called before submitPasswordReset!");
149151
return;
150152
}
153+
if (this.state.currentHttpRequest) return;
154+
151155
try {
152-
await this.reset.checkEmailLinkClicked();
156+
await this.handleHttpRequest(this.reset.checkEmailLinkClicked());
153157
this.setState({ phase: Phase.Done });
154158
} catch (err) {
155159
this.showErrorDialog(err.message);
@@ -158,9 +162,10 @@ export default class ForgotPassword extends React.Component<IProps, IState> {
158162

159163
private onSubmitForm = async (ev: React.FormEvent): Promise<void> => {
160164
ev.preventDefault();
165+
if (this.state.currentHttpRequest) return;
161166

162167
// refresh the server errors, just in case the server came back online
163-
await this.checkServerLiveliness(this.props.serverConfig);
168+
await this.handleHttpRequest(this.checkServerLiveliness(this.props.serverConfig));
164169

165170
await this['password_field'].validate({ allowEmpty: false });
166171

@@ -221,6 +226,17 @@ export default class ForgotPassword extends React.Component<IProps, IState> {
221226
});
222227
}
223228

229+
private handleHttpRequest<T = unknown>(request: Promise<T>): Promise<T> {
230+
this.setState({
231+
currentHttpRequest: request,
232+
});
233+
return request.finally(() => {
234+
this.setState({
235+
currentHttpRequest: undefined,
236+
});
237+
});
238+
}
239+
224240
renderForgot() {
225241
const Field = sdk.getComponent('elements.Field');
226242

@@ -320,6 +336,9 @@ export default class ForgotPassword extends React.Component<IProps, IState> {
320336
type="button"
321337
onClick={this.onVerify}
322338
value={_t('I have verified my email address')} />
339+
{ this.state.currentHttpRequest && (
340+
<div className="mx_Login_spinner"><InlineSpinner w={64} h={64} /></div>)
341+
}
323342
</div>;
324343
}
325344

@@ -357,6 +376,8 @@ export default class ForgotPassword extends React.Component<IProps, IState> {
357376
case Phase.Done:
358377
resetPasswordJsx = this.renderDone();
359378
break;
379+
default:
380+
resetPasswordJsx = <div className="mx_Login_spinner"><InlineSpinner w={64} h={64} /></div>;
360381
}
361382

362383
return (

0 commit comments

Comments
 (0)