mirror of
https://github.com/element-hq/element-web.git
synced 2024-12-05 05:38:41 +03:00
Redesign mxID chooser, add availability checking
Requires https://github.com/matrix-org/matrix-js-sdk/pull/432 for availability checking. Changes: - Redesign the dialog to look more like https://github.com/vector-im/riot-web/issues/3604#issuecomment-299226875 - Attempt to fix wrong password being stored by generating one per SetMxIdDialog (there's no issue tracking this for now, I shall open one if it persists) - Backwards compatible with servers that don't support register/availability - a spinner will appear the first time a username is checked because server support can only be determined after a request. - Rate-limited by a 2s debounce - General style improvements
This commit is contained in:
parent
69382d36b1
commit
ad2ed12980
2 changed files with 137 additions and 28 deletions
|
@ -775,7 +775,8 @@ module.exports = React.createClass({
|
|||
const SetMxIdDialog = sdk.getComponent('views.dialogs.SetMxIdDialog');
|
||||
const defered = q.defer();
|
||||
mxIdPromise = defered.promise;
|
||||
Modal.createDialog(SetMxIdDialog, {
|
||||
const close = Modal.createDialog(SetMxIdDialog, {
|
||||
homeserverUrl: cli.getHomeserverUrl(),
|
||||
onFinished: (submitted, credentials) => {
|
||||
if (!submitted) {
|
||||
defered.reject();
|
||||
|
@ -783,8 +784,12 @@ module.exports = React.createClass({
|
|||
}
|
||||
this.props.onRegistered(credentials);
|
||||
defered.resolve();
|
||||
}
|
||||
});
|
||||
},
|
||||
onDifferentServerClicked: (ev) => {
|
||||
dis.dispatch({action: 'start_registration'});
|
||||
close();
|
||||
},
|
||||
}).close;
|
||||
}
|
||||
|
||||
mxIdPromise.then(() => {
|
||||
|
|
|
@ -19,6 +19,11 @@ import q from 'q';
|
|||
import React from 'react';
|
||||
import sdk from '../../../index';
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
import classnames from 'classnames';
|
||||
|
||||
// The amount of time to wait for further changes to the input username before
|
||||
// sending a request to the server
|
||||
const USERNAME_CHECK_DEBOUNCE_MS = 2000;
|
||||
|
||||
/**
|
||||
* Prompt the user to set a display name.
|
||||
|
@ -33,9 +38,20 @@ export default React.createClass({
|
|||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
username : '',
|
||||
// The entered username
|
||||
username: '',
|
||||
// Indicate ongoing work on the username
|
||||
usernameBusy: false,
|
||||
// Indicate error with username
|
||||
usernameError: '',
|
||||
// Assume the homeserver supports username checking until "M_UNRECOGNIZED"
|
||||
usernameCheckSupport: true,
|
||||
|
||||
// Whether the auth UI is currently being used
|
||||
doingUIAuth: false,
|
||||
}
|
||||
// Indicate error with auth
|
||||
authError: '',
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
|
@ -46,7 +62,28 @@ export default React.createClass({
|
|||
|
||||
onValueChange: function(ev) {
|
||||
this.setState({
|
||||
username: ev.target.value
|
||||
username: ev.target.value,
|
||||
usernameBusy: true,
|
||||
usernameError: '',
|
||||
}, () => {
|
||||
if (!this.state.username || !this.state.usernameCheckSupport) {
|
||||
this.setState({
|
||||
usernameBusy: false,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Debounce the username check to limit number of requests sent
|
||||
if (this._usernameCheckTimeout) {
|
||||
clearTimeout(this._usernameCheckTimeout);
|
||||
}
|
||||
this._usernameCheckTimeout = setTimeout(() => {
|
||||
this._doUsernameCheck().finally(() => {
|
||||
this.setState({
|
||||
usernameBusy: false,
|
||||
});
|
||||
});
|
||||
}, USERNAME_CHECK_DEBOUNCE_MS);
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -56,6 +93,40 @@ export default React.createClass({
|
|||
});
|
||||
},
|
||||
|
||||
_doUsernameCheck: function() {
|
||||
// Check if username is available
|
||||
return this._matrixClient.isUsernameAvailable(this.state.username).then(
|
||||
(isAvailable) => {
|
||||
if (isAvailable) {
|
||||
this.setState({usernameError: ''});
|
||||
}
|
||||
},
|
||||
(err) => {
|
||||
// Indicate whether the homeserver supports username checking
|
||||
const newState = {
|
||||
usernameCheckSupport: err.errcode !== "M_UNRECOGNIZED",
|
||||
};
|
||||
switch (err.errcode) {
|
||||
case "M_USER_IN_USE":
|
||||
newState.usernameError = 'Username not available';
|
||||
break;
|
||||
case "M_INVALID_USERNAME":
|
||||
newState.usernameError = 'Username invalid: ' + err.message;
|
||||
break;
|
||||
case "M_UNRECOGNIZED":
|
||||
// This homeserver doesn't support username checking, assume it's
|
||||
// fine and rely on the error appearing in registration step.
|
||||
newState.usernameError = '';
|
||||
break;
|
||||
default:
|
||||
newState.usernameError = 'An error occurred' + err.message;
|
||||
break;
|
||||
}
|
||||
this.setState(newState);
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
_generatePassword: function() {
|
||||
return Math.random().toString(36).slice(2);
|
||||
},
|
||||
|
@ -63,8 +134,9 @@ export default React.createClass({
|
|||
_makeRegisterRequest: function(auth) {
|
||||
// Not upgrading - changing mxids
|
||||
const guestAccessToken = null;
|
||||
if (!this._generatedPassword) {
|
||||
this._generatedPassword = this._generatePassword();
|
||||
|
||||
}
|
||||
return this._matrixClient.register(
|
||||
this.state.username,
|
||||
this._generatedPassword,
|
||||
|
@ -79,10 +151,9 @@ export default React.createClass({
|
|||
this.setState({
|
||||
doingUIAuth: false,
|
||||
});
|
||||
console.info('Auth Finsihed', arguments);
|
||||
|
||||
if (!success) {
|
||||
this.setState({ errorText : response.message });
|
||||
this.setState({ authError: response.message });
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -104,6 +175,7 @@ export default React.createClass({
|
|||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
const InteractiveAuth = sdk.getComponent('structures.InteractiveAuth');
|
||||
const Spinner = sdk.getComponent('elements.Spinner');
|
||||
|
||||
let auth;
|
||||
if (this.state.doingUIAuth) {
|
||||
auth = <InteractiveAuth
|
||||
|
@ -114,36 +186,68 @@ export default React.createClass({
|
|||
poll={true}
|
||||
/>;
|
||||
}
|
||||
const inputClasses = classnames({
|
||||
"mx_SetMxIdDialog_input": true,
|
||||
"error": Boolean(this.state.usernameError),
|
||||
});
|
||||
|
||||
let usernameIndicator = null;
|
||||
let usernameBusyIndicator = null;
|
||||
if (this.state.usernameBusy) {
|
||||
usernameBusyIndicator = <Spinner w="24" h="24"/>;
|
||||
} else {
|
||||
const usernameAvailable = this.state.username &&
|
||||
this.state.usernameCheckSupport && !this.state.usernameError;
|
||||
const usernameIndicatorClasses = classnames({
|
||||
"error": Boolean(this.state.usernameError),
|
||||
"success": usernameAvailable,
|
||||
});
|
||||
usernameIndicator = <div className={usernameIndicatorClasses}>
|
||||
{ usernameAvailable ? 'Username available' : this.state.usernameError }
|
||||
</div>;
|
||||
}
|
||||
|
||||
let authErrorIndicator = null;
|
||||
if (this.state.authError) {
|
||||
authErrorIndicator = <div className="error">
|
||||
{ this.state.authError }
|
||||
</div>;
|
||||
}
|
||||
const canContinue = this.state.username &&
|
||||
!this.state.usernameError &&
|
||||
!this.state.usernameBusy;
|
||||
|
||||
return (
|
||||
<BaseDialog className="mx_SetMxIdDialog"
|
||||
onFinished={this.props.onFinished}
|
||||
title="Choose a Username"
|
||||
title="To get started, please pick a username!"
|
||||
>
|
||||
<div className="mx_Dialog_content">
|
||||
<p>
|
||||
Beyond this point you're going to need to pick a username - your
|
||||
unique identifier in Riot.
|
||||
</p>
|
||||
<p>
|
||||
<small>
|
||||
You can't change your username, but you can always choose how you
|
||||
appear to other people in Riot by changing your display name.
|
||||
</small>
|
||||
</p>
|
||||
<div className="mx_SetMxIdDialog_input_group">
|
||||
<input type="text" ref="input_value" value={this.state.username}
|
||||
autoFocus={true} onChange={this.onValueChange} size="30"
|
||||
className="mx_SetMxIdDialog_input"
|
||||
className={inputClasses}
|
||||
/>
|
||||
{ auth }
|
||||
<div>
|
||||
{ this.state.errorText }
|
||||
{ usernameBusyIndicator }
|
||||
</div>
|
||||
{ usernameIndicator }
|
||||
<p>
|
||||
This will be your account name on
|
||||
the {this.props.homeserverUrl} homeserver,
|
||||
or you can pick a
|
||||
<a href="#" onClick={this.props.onDifferentServerClicked}>
|
||||
different server
|
||||
</a>.
|
||||
</p>
|
||||
{ auth }
|
||||
{ authErrorIndicator }
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<input className="mx_Dialog_primary"
|
||||
type="submit"
|
||||
value="Continue"
|
||||
onClick={this.onSubmit}
|
||||
disabled={!canContinue}
|
||||
/>
|
||||
</div>
|
||||
</BaseDialog>
|
||||
|
|
Loading…
Reference in a new issue