Merge pull request #31 from matrix-org/kegan/login-refactor

Refactor login page
This commit is contained in:
Kegsay 2015-11-17 10:47:47 +00:00
commit 9f7a504a20
6 changed files with 178 additions and 188 deletions

90
src/Signup.js Normal file
View file

@ -0,0 +1,90 @@
"use strict";
var MatrixClientPeg = require("./MatrixClientPeg");
var dis = require("./dispatcher");
var q = require("q");
class Register {
}
class Login {
constructor(hsUrl, isUrl) {
this._hsUrl = hsUrl;
this._isUrl = isUrl;
this._currentFlowIndex = 0;
this._flows = [];
}
getFlows() {
var self = this;
// feels a bit wrong to be clobbering the global client for something we
// don't even know if it'll work, but we'll leave this here for now to
// not complicate matters further. It would be nicer to isolate this
// logic entirely from the rest of the app though.
MatrixClientPeg.replaceUsingUrls(
this._hsUrl,
this._isUrl
);
return MatrixClientPeg.get().loginFlows().then(function(result) {
self._flows = result.flows;
self._currentFlowIndex = 0;
// technically the UI should display options for all flows for the
// user to then choose one, so return all the flows here.
return self._flows;
});
}
chooseFlow(flowIndex) {
this._currentFlowIndex = flowIndex;
}
getCurrentFlowStep() {
// technically the flow can have multiple steps, but no one does this
// for login so we can ignore it.
var flowStep = this._flows[this._currentFlowIndex];
return flowStep ? flowStep.type : null;
}
loginViaPassword(username, pass) {
var self = this;
var isEmail = username.indexOf("@") > 0;
var loginParams = {
password: pass
};
if (isEmail) {
loginParams.medium = 'email';
loginParams.address = username;
} else {
loginParams.user = username;
}
return MatrixClientPeg.get().login('m.login.password', loginParams).then(function(data) {
return q({
homeserverUrl: self._hsUrl,
identityServerUrl: self._isUrl,
userId: data.user_id,
accessToken: data.access_token
});
}, function(error) {
if (error.httpStatus == 400 && loginParams.medium) {
error.friendlyText = (
'This Home Server does not support login using email address.'
);
}
else if (error.httpStatus === 403) {
error.friendlyText = (
'Incorrect username and/or password.'
);
}
else {
error.friendlyText = (
'There was a problem logging in. (HTTP ' + error.httpStatus + ")"
);
}
throw error;
});
}
}
module.exports.Register = Register;
module.exports.Login = Login;

View file

@ -16,10 +16,12 @@ limitations under the License.
'use strict'; 'use strict';
var MatrixClientPeg = require("../../MatrixClientPeg"); var MatrixClientPeg = require("../MatrixClientPeg");
var React = require('react');
var url = require("url"); var url = require("url");
module.exports = { module.exports = React.createClass({
displayName: 'CasLogin',
onCasClicked: function(ev) { onCasClicked: function(ev) {
var cli = MatrixClientPeg.get(); var cli = MatrixClientPeg.get();
@ -30,4 +32,12 @@ module.exports = {
window.location.href = casUrl; window.location.href = casUrl;
}, },
}; render: function() {
return (
<div>
<button onClick={this.onCasClicked}>Sign in with CAS</button>
</div>
);
}
});

View file

@ -0,0 +1,65 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
var React = require('react');
var ReactDOM = require('react-dom');
/**
* A pure UI component which displays a username/password form.
*/
module.exports = React.createClass({displayName: 'PasswordLogin',
propTypes: {
onSubmit: React.PropTypes.func.isRequired // fn(username, password)
},
getInitialState: function() {
return {
username: "",
password: ""
};
},
onSubmitForm: function(ev) {
ev.preventDefault();
this.props.onSubmit(this.state.username, this.state.password);
},
onUsernameChanged: function(ev) {
this.setState({username: ev.target.value});
},
onPasswordChanged: function(ev) {
this.setState({password: ev.target.value});
},
render: function() {
return (
<div>
<form onSubmit={this.onSubmitForm}>
<input className="mx_Login_field" ref="user" type="text"
value={this.state.username} onChange={this.onUsernameChanged}
placeholder="Email or user name" />
<br />
<input className="mx_Login_field" ref="pass" type="password"
value={this.state.password} onChange={this.onPasswordChanged}
placeholder="Password" />
<br />
<input className="mx_Login_submit" type="submit" value="Log in" />
</form>
</div>
);
}
});

View file

@ -1,67 +0,0 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
'use strict';
var React = require("react");
module.exports = {
propTypes: {
onHsUrlChanged: React.PropTypes.func,
onIsUrlChanged: React.PropTypes.func,
defaultHsUrl: React.PropTypes.string,
defaultIsUrl: React.PropTypes.string
},
getDefaultProps: function() {
return {
onHsUrlChanged: function() {},
onIsUrlChanged: function() {},
defaultHsUrl: 'https://matrix.org/',
defaultIsUrl: 'https://matrix.org/'
};
},
getInitialState: function() {
return {
hs_url: this.props.defaultHsUrl,
is_url: this.props.defaultIsUrl,
original_hs_url: this.props.defaultHsUrl,
original_is_url: this.props.defaultIsUrl,
}
},
hsChanged: function(ev) {
this.setState({hs_url: ev.target.value}, function() {
this.props.onHsUrlChanged(this.state.hs_url);
});
},
// XXX: horrible naming due to potential confusion between the word 'is' and the acronym 'IS'
isChanged: function(ev) {
this.setState({is_url: ev.target.value}, function() {
this.props.onIsUrlChanged(this.state.is_url);
});
},
getHsUrl: function() {
return this.state.hs_url;
},
getIsUrl: function() {
return this.state.is_url;
},
};

View file

@ -295,7 +295,14 @@ module.exports = {
} }
}, },
onLoggedIn: function() { onLoggedIn: function(credentials) {
if (credentials) { // registration doesn't do this yet
console.log("onLoggedIn => %s", credentials.userId);
MatrixClientPeg.replaceUsingAccessToken(
credentials.homeserverUrl, credentials.identityServerUrl,
credentials.userId, credentials.accessToken
);
}
this.setState({ this.setState({
screen: undefined, screen: undefined,
logged_in: true logged_in: true
@ -309,7 +316,8 @@ module.exports = {
var cli = MatrixClientPeg.get(); var cli = MatrixClientPeg.get();
var self = this; var self = this;
cli.on('sync', function(state) { cli.on('sync', function(state) {
if (self.sdkReady || state !== "PREPARED") { return; } console.log("MatrixClient sync state => %s", state);
if (state !== "PREPARED") { return; }
self.sdkReady = true; self.sdkReady = true;
if (self.starting_room_alias) { if (self.starting_room_alias) {

View file

@ -1,116 +0,0 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
'use strict';
var MatrixClientPeg = require("../../MatrixClientPeg");
var dis = require("../../dispatcher");
module.exports = {
getInitialState: function() {
return {
step: 'choose_hs',
busy: false,
currentStep: 0,
totalSteps: 1
};
},
setStep: function(step) {
this.setState({ step: step, busy: false });
},
onHSChosen: function() {
MatrixClientPeg.replaceUsingUrls(
// XXX: why is the controller invoking methods from the view? :( -matthew
this.getHsUrl(),
this.getIsUrl()
);
this.setState({
hs_url: this.getHsUrl(),
is_url: this.getIsUrl(),
});
this.setStep("fetch_stages");
var cli = MatrixClientPeg.get();
this.setState({
busy: true,
errorText: "",
});
var self = this;
cli.loginFlows().done(function(result) {
self.setState({
flows: result.flows,
currentStep: 1,
totalSteps: result.flows.length+1
});
self.setStep('stage_'+result.flows[0].type);
}, function(error) {
self.setStep("choose_hs");
self.setState({errorText: 'Unable to contact the given home server'});
});
},
onUserPassEntered: function(ev) {
ev.preventDefault();
this.setState({
busy: true,
errorText: "",
});
var self = this;
var formVals = this.getFormVals();
var loginParams = {
password: formVals.password
};
if (formVals.username.indexOf('@') > 0) {
loginParams.medium = 'email';
loginParams.address = formVals.username;
} else {
loginParams.user = formVals.username;
}
MatrixClientPeg.get().login('m.login.password', loginParams).done(function(data) {
MatrixClientPeg.replaceUsingAccessToken(
self.state.hs_url, self.state.is_url,
data.user_id, data.access_token
);
if (self.props.onLoggedIn) {
self.props.onLoggedIn();
}
}, function(error) {
self.setStep("stage_m.login.password");
if (error.httpStatus == 400 && loginParams.medium) {
self.setState({errorText: 'This Home Server does not support login using email address.'});
}
else if (error.httpStatus === 403) {
self.setState({errorText: 'Incorrect username and/or password.'});
}
else {
self.setState({
errorText: 'There was a problem logging in. (HTTP ' + error.httpStatus + ")"
});
}
});
},
showRegister: function(ev) {
ev.preventDefault();
dis.dispatch({
action: 'start_registration'
});
}
};