Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Redesign mxID chooser, add availability checking #877

Merged
merged 2 commits into from
May 10, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions src/components/structures/RoomView.js
Original file line number Diff line number Diff line change
Expand Up @@ -775,16 +775,21 @@ 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();
return;
}
this.props.onRegistered(credentials);
defered.resolve();
}
});
},
onDifferentServerClicked: (ev) => {
dis.dispatch({action: 'start_registration'});
close();
},
}).close;
}

mxIdPromise.then(() => {
Expand Down
152 changes: 129 additions & 23 deletions src/components/views/dialogs/SetMxIdDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -29,13 +34,26 @@ export default React.createClass({
displayName: 'SetMxIdDialog',
propTypes: {
onFinished: React.PropTypes.func.isRequired,
// Called when the user requests to register with a different homeserver
onDifferentServerClicked: React.PropTypes.func.isRequired,
},

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() {
Expand All @@ -46,7 +64,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);
});
},

Expand All @@ -56,15 +95,50 @@ 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);
},

_makeRegisterRequest: function(auth) {
// Not upgrading - changing mxids
const guestAccessToken = null;
this._generatedPassword = this._generatePassword();

if (!this._generatedPassword) {
this._generatedPassword = this._generatePassword();
}
return this._matrixClient.register(
this.state.username,
this._generatedPassword,
Expand All @@ -79,10 +153,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;
}

Expand All @@ -104,6 +177,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
Expand All @@ -114,36 +188,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">
<div className="mx_SetMxIdDialog_input_group">
<input type="text" ref="input_value" value={this.state.username}
autoFocus={true} onChange={this.onValueChange} size="30"
className={inputClasses}
/>
{ usernameBusyIndicator }
</div>
{ usernameIndicator }
<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>
This will be your account name on
the {this.props.homeserverUrl} homeserver,
or you can pick a&nbsp;
<a href="#" onClick={this.props.onDifferentServerClicked}>
different server
</a>.
</p>
<input type="text" ref="input_value" value={this.state.username}
autoFocus={true} onChange={this.onValueChange} size="30"
className="mx_SetMxIdDialog_input"
/>
{ auth }
<div>
{ this.state.errorText }
</div>
{ authErrorIndicator }
</div>
<div className="mx_Dialog_buttons">
<input className="mx_Dialog_primary"
type="submit"
value="Continue"
onClick={this.onSubmit}
disabled={!canContinue}
/>
</div>
</BaseDialog>
Expand Down