From a7e4a2847eaadcd3ca1882078cb6815598adcfb4 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 13 Jul 2015 19:14:02 +0100 Subject: [PATCH] Start of registration support. --- skins/base/views/pages/MatrixChat.js | 5 + skins/base/views/templates/Login.js | 1 + skins/base/views/templates/Register.js | 55 ++++++++ src/ComponentBroker.js | 1 + src/controllers/pages/MatrixChat.js | 20 ++- src/controllers/templates/Login.js | 10 +- src/controllers/templates/Register.js | 179 +++++++++++++++++++++++++ 7 files changed, 267 insertions(+), 4 deletions(-) create mode 100644 skins/base/views/templates/Register.js create mode 100644 src/controllers/templates/Register.js diff --git a/skins/base/views/pages/MatrixChat.js b/skins/base/views/pages/MatrixChat.js index f9e199e6..f2480ddc 100644 --- a/skins/base/views/pages/MatrixChat.js +++ b/skins/base/views/pages/MatrixChat.js @@ -23,6 +23,7 @@ var RoomList = ComponentBroker.get('organisms/RoomList'); var RoomView = ComponentBroker.get('organisms/RoomView'); var MatrixToolbar = ComponentBroker.get('molecules/MatrixToolbar'); var Login = ComponentBroker.get('templates/Login'); +var Register = ComponentBroker.get('templates/Register'); var MatrixChatController = require("../../../../src/controllers/pages/MatrixChat"); @@ -51,6 +52,10 @@ module.exports = React.createClass({ return ( ); + } else if (this.state.screen == 'register') { + return ( + + ); } else { return ( diff --git a/skins/base/views/templates/Login.js b/skins/base/views/templates/Login.js index 6cd721e2..ceae07ec 100644 --- a/skins/base/views/templates/Login.js +++ b/skins/base/views/templates/Login.js @@ -40,6 +40,7 @@ module.exports = React.createClass({

Please log in:

{this.componentForStep(this.state.step)}
{this.state.errorText}
+ Create a new account ); } diff --git a/skins/base/views/templates/Register.js b/skins/base/views/templates/Register.js new file mode 100644 index 00000000..676fa2e2 --- /dev/null +++ b/skins/base/views/templates/Register.js @@ -0,0 +1,55 @@ +/* +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'); + +var ComponentBroker = require("../../../../src/ComponentBroker"); + +var Loader = require("react-loader"); + +var RegisterController = require("../../../../src/controllers/templates/Register"); + +module.exports = React.createClass({ + displayName: 'Register', + mixins: [RegisterController], + + registerContent: function() { + if (this.state.busy) { + return ( + + ); + } else { + return ( +
+

Create a new account:

+ {this.componentForStep(this.state.step)} +
{this.state.errorText}
+ Sign in with existing account +
+ ); + } + }, + + render: function() { + return ( +
+ {this.registerContent()} +
+ ); + } +}); diff --git a/src/ComponentBroker.js b/src/ComponentBroker.js index 32b07a03..c9f9eefb 100644 --- a/src/ComponentBroker.js +++ b/src/ComponentBroker.js @@ -81,5 +81,6 @@ require('../skins/base/views/molecules/MemberTile'); require('../skins/base/views/organisms/RoomList'); require('../skins/base/views/organisms/RoomView'); require('../skins/base/views/templates/Login'); +require('../skins/base/views/templates/Register'); require('../skins/base/views/organisms/Notifier'); } diff --git a/src/controllers/pages/MatrixChat.js b/src/controllers/pages/MatrixChat.js index 717f91e7..1ac1c679 100644 --- a/src/controllers/pages/MatrixChat.js +++ b/src/controllers/pages/MatrixChat.js @@ -67,10 +67,25 @@ module.exports = { logged_in: false, ready: false }); + localStorage.removeItem("mx_hs_url"); + localStorage.removeItem("mx_user_id"); + localStorage.removeItem("mx_access_token"); Notifier.stop(); MatrixClientPeg.get().removeAllListeners(); MatrixClientPeg.replace(null); break; + case 'start_registration': + if (this.state.logged_in) return; + this.setState({ + screen: 'register' + }); + break; + case 'start_login': + if (this.state.logged_in) return; + this.setState({ + screen: 'login' + }); + break; case 'view_room': this.focusComposer = true; this.setState({ @@ -99,7 +114,10 @@ module.exports = { }, onLoggedIn: function() { - this.setState({logged_in: true}); + this.setState({ + screen: 'register', + logged_in: true + }); this.startMatrixClient(); }, diff --git a/src/controllers/templates/Login.js b/src/controllers/templates/Login.js index f3c58cf2..a5bd43cc 100644 --- a/src/controllers/templates/Login.js +++ b/src/controllers/templates/Login.js @@ -20,6 +20,7 @@ var React = require('react'); var MatrixClientPeg = require("../../MatrixClientPeg"); var Matrix = require("matrix-js-sdk"); +var dis = require("../../dispatcher"); var ComponentBroker = require("../../ComponentBroker"); @@ -85,9 +86,6 @@ module.exports = { if (that.props.onLoggedIn) { that.props.onLoggedIn(); } - /*dis.dispatch({ - 'action': 'logged_in' - });*/ }, function(error) { that.setStep("stage_m.login.password"); that.setState({errorText: 'Login failed.'}); @@ -118,4 +116,10 @@ module.exports = { ); } }, + + showRegister: function() { + dis.dispatch({ + action: 'start_registration' + }); + } }; diff --git a/src/controllers/templates/Register.js b/src/controllers/templates/Register.js new file mode 100644 index 00000000..12e10c0d --- /dev/null +++ b/src/controllers/templates/Register.js @@ -0,0 +1,179 @@ +/* +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'); + +var MatrixClientPeg = require("../../MatrixClientPeg"); +var Matrix = require("matrix-js-sdk"); +var dis = require("../../dispatcher"); + +var ComponentBroker = require("../../ComponentBroker"); + +var ServerConfig = ComponentBroker.get("molecules/ServerConfig"); + +module.exports = { + getInitialState: function() { + return { + step: 'initial', + busy: false, + currentStep: 0, + totalSteps: 1 + }; + }, + + setStep: function(step) { + this.setState({ step: step, errorText: '', busy: false }); + }, + + getSupportedStageTypes: function() { + return ['m.login.email.identity', 'm.login.recaptcha']; + }, + + chooseFlow: function(flows) { + // this is fairly simple right now + var supportedTypes = this.getSupportedStageTypes(); + + var emailFlow = null; + var otherFlow = null; + for (var flowI = 0; flowI < flows.length; ++flowI) { + var flow = flows[flowI]; + var flowHasEmail = false; + var flowSupported = true; + for (var stageI = 0; stageI < flow.stages.length; ++stageI) { + var stage = flow.stages[stageI]; + + if (supportedTypes.indexOf(stage) == -1) { + flowSupported = false; + } + + if (stage == 'm.login.email.identity') { + flowHasEmail = true; + } + } + if (flowSupported) { + if (flowHasEmail) { + emailFlow = flow; + } else { + otherFlow = flow; + } + } + } + + if (this.savedParams.email != '') { + return emailFlow; + } else { + return otherFlow; + } + }, + + onInitialStageSubmit: function(ev) { + ev.preventDefault(); + MatrixClientPeg.replaceUsingUrl(this.refs.serverConfig.getHsUrl()); + this.setState({hs_url: this.refs.serverConfig.getHsUrl()}); + var cli = MatrixClientPeg.get(); + this.setState({busy: true}); + var self = this; + + var email = this.refs.email.getDOMNode().value; + var username = this.refs.username.getDOMNode().value; + var password = this.refs.password.getDOMNode().value; + + this.savedParams = { + email: email, + username: username, + password: password + }; + + cli.register(username, password).done(function(result) { + self.onRegistered(); + }, function(error) { + if (error.httpStatus == 401) { + var flow = self.chooseFlow(error.data.flows); + self.setState({ + busy: false, + flows: flow, + currentStep: 1, + totalSteps: flow.stages.length+1, + flowStage: 0 + }); + self.setStep('stage_'+flow.stages[0]); + } else { + self.setStep("initial"); + self.setState({ + busy: false, + errorText: 'Unable to contact the given Home Server' + }); + } + }); + }, + + onRegistered: function(user_id, access_token) { + MatrixClientPeg.replace(Matrix.createClient({ + baseUrl: this.state.hs_url, + userId: data.user_id, + accessToken: data.access_token + })); + var localStorage = window.localStorage; + if (localStorage) { + localStorage.setItem("mx_hs_url", this.state.hs_url); + localStorage.setItem("mx_user_id", user_id); + localStorage.setItem("mx_access_token", access_token); + } else { + console.warn("No local storage available: can't persist session!"); + } + if (that.props.onLoggedIn) { + that.props.onLoggedIn(); + } + }, + + componentForStep: function(step) { + switch (step) { + case 'initial': + return ( +
+
+ Email:
+ Username:
+ Password:
+ + + +
+ ); + // XXX: clearly these should be separate organisms + case 'stage_m.login.email.identity': + return ( +
+ Please check your email to continue registration. +
+ ); + case 'stage_m.login.recaptcha': + return ( +
+ This is the recaptcha stage. Sucks, doesn't it. +
+ ); + } + }, + + showLogin: function() { + dis.dispatch({ + action: 'start_login' + }); + } +};