diff --git a/src/Signup.js b/src/Signup.js
index ba91ff60b0..02ddaacc6d 100644
--- a/src/Signup.js
+++ b/src/Signup.js
@@ -1,16 +1,272 @@
"use strict";
var MatrixClientPeg = require("./MatrixClientPeg");
+var SignupStages = require("./SignupStages");
var dis = require("./dispatcher");
var q = require("q");
-class Register {
+const EMAIL_STAGE_TYPE = "m.login.email.identity";
-}
-
-class Login {
+/**
+ * A base class for common functionality between Registration and Login e.g.
+ * storage of HS/IS URLs.
+ */
+class Signup {
constructor(hsUrl, isUrl) {
this._hsUrl = hsUrl;
this._isUrl = isUrl;
+ }
+
+ getHomeserverUrl() {
+ return this._hsUrl;
+ }
+
+ getIdentityServerUrl() {
+ return this._isUrl;
+ }
+
+ setHomeserverUrl(hsUrl) {
+ this._hsUrl = hsUrl;
+ }
+
+ setIdentityServerUrl(isUrl) {
+ this._isUrl = isUrl;
+ }
+}
+
+/**
+ * Registration logic class
+ */
+class Register extends Signup {
+ constructor(hsUrl, isUrl) {
+ super(hsUrl, isUrl);
+ this.setStep("START");
+ this.data = null; // from the server
+ // random other stuff (e.g. query params, NOT params from the server)
+ this.params = {};
+ this.credentials = null;
+ this.activeStage = null;
+ this.registrationPromise = null;
+ // These values MUST be undefined else we'll send "username: null" which
+ // will error on Synapse rather than having the key absent.
+ this.username = undefined; // desired
+ this.email = undefined; // desired
+ this.password = undefined; // desired
+ }
+
+ setClientSecret(secret) {
+ this.params.clientSecret = secret;
+ }
+
+ setSessionId(sessionId) {
+ this.params.sessionId = sessionId;
+ }
+
+ setRegistrationUrl(regUrl) {
+ this.params.registrationUrl = regUrl;
+ }
+
+ setIdSid(idSid) {
+ this.params.idSid = idSid;
+ }
+
+ getStep() {
+ return this._step;
+ }
+
+ getCredentials() {
+ return this.credentials;
+ }
+
+ getServerData() {
+ return this.data || {};
+ }
+
+ getPromise() {
+ return this.registrationPromise;
+ }
+
+ setStep(step) {
+ this._step = 'Register.' + step;
+ // TODO:
+ // It's a shame this is going to the global dispatcher, we only really
+ // want things which have an instance of this class to be able to add
+ // listeners...
+ console.log("Dispatching 'registration_step_update' for step %s", this._step);
+ dis.dispatch({
+ action: "registration_step_update"
+ });
+ }
+
+ register(formVals) {
+ var {username, password, email} = formVals;
+ this.email = email;
+ this.username = username;
+ this.password = password;
+
+ // 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 this._tryRegister();
+ }
+
+ _tryRegister(authDict) {
+ var self = this;
+ return MatrixClientPeg.get().register(
+ this.username, this.password, this.params.sessionId, authDict
+ ).then(function(result) {
+ self.credentials = result;
+ self.setStep("COMPLETE");
+ return result; // contains the credentials
+ }, function(error) {
+ if (error.httpStatus === 401 && error.data && error.data.flows) {
+ self.data = error.data || {};
+ var flow = self.chooseFlow(error.data.flows);
+
+ if (flow) {
+ console.log("Active flow => %s", JSON.stringify(flow));
+ var flowStage = self.firstUncompletedStage(flow);
+ return self.startStage(flowStage);
+ }
+ else {
+ throw new Error("Unable to register - missing email address?");
+ }
+ } else {
+ if (error.errcode === 'M_USER_IN_USE') {
+ throw new Error("Username in use");
+ } else if (error.httpStatus == 401) {
+ throw new Error("Authorisation failed!");
+ } else if (error.httpStatus >= 400 && error.httpStatus < 500) {
+ throw new Error(`Registration failed! (${error.httpStatus})`);
+ } else if (error.httpStatus >= 500 && error.httpStatus < 600) {
+ throw new Error(
+ `Server error during registration! (${error.httpStatus})`
+ );
+ } else if (error.name == "M_MISSING_PARAM") {
+ // The HS hasn't remembered the login params from
+ // the first try when the login email was sent.
+ throw new Error(
+ "This home server does not support resuming registration."
+ );
+ }
+ }
+ });
+ }
+
+ firstUncompletedStage(flow) {
+ for (var i = 0; i < flow.stages.length; ++i) {
+ if (!this.hasCompletedStage(flow.stages[i])) {
+ return flow.stages[i];
+ }
+ }
+ }
+
+ hasCompletedStage(stageType) {
+ var completed = (this.data || {}).completed || [];
+ return completed.indexOf(stageType) !== -1;
+ }
+
+ startStage(stageName) {
+ var self = this;
+ this.setStep(`STEP_${stageName}`);
+ var StageClass = SignupStages[stageName];
+ if (!StageClass) {
+ // no idea how to handle this!
+ throw new Error("Unknown stage: " + stageName);
+ }
+
+ var stage = new StageClass(MatrixClientPeg.get(), this);
+ this.activeStage = stage;
+ return stage.complete().then(function(request) {
+ if (request.auth) {
+ console.log("Stage %s is returning an auth dict", stageName);
+ return self._tryRegister(request.auth);
+ }
+ else {
+ // never resolve the promise chain. This is for things like email auth
+ // which display a "check your email" message and relies on the
+ // link in the email to actually register you.
+ console.log("Waiting for external action.");
+ return q.defer().promise;
+ }
+ });
+ }
+
+ chooseFlow(flows) {
+ // If the user gave us an email then we want to pick an email
+ // flow we can do, else any other flow.
+ var emailFlow = null;
+ var otherFlow = null;
+ flows.forEach(function(flow) {
+ var flowHasEmail = false;
+ for (var stageI = 0; stageI < flow.stages.length; ++stageI) {
+ var stage = flow.stages[stageI];
+
+ if (!SignupStages[stage]) {
+ // we can't do this flow, don't have a Stage impl.
+ return;
+ }
+
+ if (stage === EMAIL_STAGE_TYPE) {
+ flowHasEmail = true;
+ }
+ }
+
+ if (flowHasEmail) {
+ emailFlow = flow;
+ } else {
+ otherFlow = flow;
+ }
+ });
+
+ if (this.email || this.hasCompletedStage(EMAIL_STAGE_TYPE)) {
+ // we've been given an email or we've already done an email part
+ return emailFlow;
+ } else {
+ return otherFlow;
+ }
+ }
+
+ recheckState() {
+ // 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
+ );
+ // We've been given a bunch of data from a previous register step,
+ // this only happens for email auth currently. It's kinda ming we need
+ // to know this though. A better solution would be to ask the stages if
+ // they are ready to do something rather than accepting that we know about
+ // email auth and its internals.
+ this.params.hasEmailInfo = (
+ this.params.clientSecret && this.params.sessionId && this.params.idSid
+ );
+
+ if (this.params.hasEmailInfo) {
+ this.registrationPromise = this.startStage(EMAIL_STAGE_TYPE);
+ }
+ return this.registrationPromise;
+ }
+
+ tellStage(stageName, data) {
+ if (this.activeStage && this.activeStage.type === stageName) {
+ console.log("Telling stage %s about something..", stageName);
+ this.activeStage.onReceiveData(data);
+ }
+ }
+}
+
+
+class Login extends Signup {
+ constructor(hsUrl, isUrl) {
+ super(hsUrl, isUrl);
this._currentFlowIndex = 0;
this._flows = [];
}
diff --git a/src/SignupStages.js b/src/SignupStages.js
new file mode 100644
index 0000000000..272a955d95
--- /dev/null
+++ b/src/SignupStages.js
@@ -0,0 +1,196 @@
+"use strict";
+var q = require("q");
+
+/**
+ * An interface class which login types should abide by.
+ */
+class Stage {
+ constructor(type, matrixClient, signupInstance) {
+ this.type = type;
+ this.client = matrixClient;
+ this.signupInstance = signupInstance;
+ }
+
+ complete() {
+ // Return a promise which is:
+ // RESOLVED => With an Object which has an 'auth' key which is the auth dict
+ // to submit.
+ // REJECTED => With an Error if there was a problem with this stage.
+ // Has a "message" string and an "isFatal" flag.
+ return q.reject("NOT IMPLEMENTED");
+ }
+
+ onReceiveData() {
+ // NOP
+ }
+}
+Stage.TYPE = "NOT IMPLEMENTED";
+
+
+/**
+ * This stage requires no auth.
+ */
+class DummyStage extends Stage {
+ constructor(matrixClient, signupInstance) {
+ super(DummyStage.TYPE, matrixClient, signupInstance);
+ }
+
+ complete() {
+ return q({
+ auth: {
+ type: DummyStage.TYPE
+ }
+ });
+ }
+}
+DummyStage.TYPE = "m.login.dummy";
+
+
+/**
+ * This stage uses Google's Recaptcha to do auth.
+ */
+class RecaptchaStage extends Stage {
+ constructor(matrixClient, signupInstance) {
+ super(RecaptchaStage.TYPE, matrixClient, signupInstance);
+ this.defer = q.defer(); // resolved with the captcha response
+ this.publicKey = null; // from the HS
+ this.divId = null; // from the UI component
+ }
+
+ // called when the UI component has loaded the recaptcha
so we can
+ // render to it.
+ onReceiveData(data) {
+ if (!data || !data.divId) {
+ return;
+ }
+ this.divId = data.divId;
+ this._attemptRender();
+ }
+
+ complete() {
+ var publicKey;
+ var serverParams = this.signupInstance.getServerData().params;
+ if (serverParams && serverParams["m.login.recaptcha"]) {
+ publicKey = serverParams["m.login.recaptcha"].public_key;
+ }
+ if (!publicKey) {
+ return q.reject({
+ message: "This server has not supplied enough information for Recaptcha " +
+ "authentication",
+ isFatal: true
+ });
+ }
+ this.publicKey = publicKey;
+ this._attemptRender();
+ return this.defer.promise;
+ }
+
+ _attemptRender() {
+ if (!global.grecaptcha) {
+ console.error("grecaptcha not loaded!");
+ return;
+ }
+ if (!this.publicKey) {
+ console.error("No public key for recaptcha!");
+ return;
+ }
+ if (!this.divId) {
+ console.error("No div ID specified!");
+ return;
+ }
+ console.log("Rendering to %s", this.divId);
+ var self = this;
+ global.grecaptcha.render(this.divId, {
+ sitekey: this.publicKey,
+ callback: function(response) {
+ console.log("Received captcha response");
+ self.defer.resolve({
+ auth: {
+ type: 'm.login.recaptcha',
+ response: response
+ }
+ });
+ }
+ });
+ }
+}
+RecaptchaStage.TYPE = "m.login.recaptcha";
+
+
+/**
+ * This state uses the IS to verify email addresses.
+ */
+class EmailIdentityStage extends Stage {
+ constructor(matrixClient, signupInstance) {
+ super(EmailIdentityStage.TYPE, matrixClient, signupInstance);
+ }
+
+ _completeVerify() {
+ // pull out the host of the IS URL by creating an anchor element
+ var isLocation = document.createElement('a');
+ isLocation.href = this.signupInstance.getIdentityServerUrl();
+
+ return q({
+ auth: {
+ type: 'm.login.email.identity',
+ threepid_creds: {
+ sid: this.signupInstance.params.idSid,
+ client_secret: this.signupInstance.params.clientSecret,
+ id_server: isLocation.host
+ }
+ }
+ });
+ }
+
+ /**
+ * Complete the email stage.
+ *
+ * This is called twice under different circumstances:
+ * 1) When requesting an email token from the IS
+ * 2) When validating query parameters received from the link in the email
+ */
+ complete() {
+ // TODO: The Registration class shouldn't really know this info.
+ if (this.signupInstance.params.hasEmailInfo) {
+ return this._completeVerify();
+ }
+
+ var clientSecret = this.client.generateClientSecret();
+ var nextLink = this.signupInstance.params.registrationUrl +
+ '?client_secret=' +
+ encodeURIComponent(clientSecret) +
+ "&hs_url=" +
+ encodeURIComponent(this.signupInstance.getHomeserverUrl()) +
+ "&is_url=" +
+ encodeURIComponent(this.signupInstance.getIdentityServerUrl()) +
+ "&session_id=" +
+ encodeURIComponent(this.signupInstance.getServerData().session);
+
+ return this.client.requestEmailToken(
+ this.signupInstance.email,
+ clientSecret,
+ 1, // TODO: Multiple send attempts?
+ nextLink
+ ).then(function(response) {
+ return {}; // don't want to make a request
+ }, function(error) {
+ console.error(error);
+ var e = {
+ isFatal: true
+ };
+ if (error.errcode == 'THREEPID_IN_USE') {
+ e.message = "Email in use";
+ } else {
+ e.message = 'Unable to contact the given identity server';
+ }
+ return e;
+ });
+ }
+}
+EmailIdentityStage.TYPE = "m.login.email.identity";
+
+module.exports = {
+ [DummyStage.TYPE]: DummyStage,
+ [RecaptchaStage.TYPE]: RecaptchaStage,
+ [EmailIdentityStage.TYPE]: EmailIdentityStage
+};
\ No newline at end of file
diff --git a/src/components/login/CaptchaForm.js b/src/components/login/CaptchaForm.js
new file mode 100644
index 0000000000..9b722f463b
--- /dev/null
+++ b/src/components/login/CaptchaForm.js
@@ -0,0 +1,67 @@
+/*
+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 DIV_ID = 'mx_recaptcha';
+
+/**
+ * A pure UI component which displays a captcha form.
+ */
+module.exports = React.createClass({
+ displayName: 'CaptchaForm',
+
+ propTypes: {
+ onCaptchaLoaded: React.PropTypes.func.isRequired // called with div id name
+ },
+
+ getDefaultProps: function() {
+ return {
+ onCaptchaLoaded: function() {
+ console.error("Unhandled onCaptchaLoaded");
+ }
+ };
+ },
+
+ componentDidMount: function() {
+ // Just putting a script tag into the returned jsx doesn't work, annoyingly,
+ // so we do this instead.
+ var self = this;
+ if (this.refs.recaptchaContainer) {
+ console.log("Loading recaptcha script...");
+ var scriptTag = document.createElement('script');
+ window.mx_on_recaptcha_loaded = function() {
+ console.log("Loaded recaptcha script.");
+ self.props.onCaptchaLoaded(DIV_ID);
+ };
+ scriptTag.setAttribute(
+ 'src', global.location.protocol+"//www.google.com/recaptcha/api.js?onload=mx_on_recaptcha_loaded&render=explicit"
+ );
+ this.refs.recaptchaContainer.appendChild(scriptTag);
+ }
+ },
+
+ render: function() {
+ // FIXME: Tight coupling with the div id and SignupStages.js
+ return (
+
+ This Home Server would like to make sure you are not a robot
+
+
+ );
+ }
+});
\ No newline at end of file
diff --git a/src/controllers/templates/Register.js b/src/controllers/templates/Register.js
deleted file mode 100644
index a3dac5b9d0..0000000000
--- a/src/controllers/templates/Register.js
+++ /dev/null
@@ -1,352 +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.
-*/
-
-var MatrixClientPeg = require("../../MatrixClientPeg");
-var dis = require("../../dispatcher");
-
-module.exports = {
- FieldErrors: {
- PasswordMismatch: 'PasswordMismatch',
- TooShort: 'TooShort',
- Missing: 'Missing',
- InUse: 'InUse',
- Length: 'Length'
- },
-
- getInitialState: function() {
- return {
- step: 'initial',
- busy: false,
- currentStep: 0,
- totalSteps: 1
- };
- },
-
- componentWillMount: function() {
- this.savedParams = {
- email: '',
- username: '',
- password: '',
- confirmPassword: ''
- };
- this.readNewProps();
- },
-
- componentWillReceiveProps: function() {
- this.readNewProps();
- },
-
- readNewProps: function() {
- if (this.props.clientSecret && this.props.hsUrl &&
- this.props.isUrl && this.props.sessionId &&
- this.props.idSid) {
- this.authSessionId = this.props.sessionId;
- MatrixClientPeg.replaceUsingUrls(
- this.props.hsUrl,
- this.props.isUrl
- );
- this.setState({
- hs_url: this.props.hsUrl,
- is_url: this.props.isUrl
- });
- this.savedParams = {client_secret: this.props.clientSecret};
- this.setState({busy: true});
-
- var isLocation = document.createElement('a');
- isLocation.href = this.props.isUrl;
-
- var auth = {
- type: 'm.login.email.identity',
- threepid_creds: {
- sid: this.props.idSid,
- client_secret: this.savedParams.client_secret,
- id_server: isLocation.host
- }
- };
- this.tryRegister(auth);
- }
- },
-
- componentDidUpdate: function() {
- // Just putting a script tag into the returned jsx doesn't work, annoyingly,
- // so we do this instead.
- if (this.refs.recaptchaContainer) {
- var scriptTag = document.createElement('script');
- window.mx_on_recaptcha_loaded = this.onCaptchaLoaded;
- scriptTag.setAttribute('src', global.location.protocol+"//www.google.com/recaptcha/api.js?onload=mx_on_recaptcha_loaded&render=explicit");
- this.refs.recaptchaContainer.appendChild(scriptTag);
- }
- },
-
- 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 != '' ||
- this.completedStages.indexOf('m.login.email.identity') > -1
- ) {
- return emailFlow;
- } else {
- return otherFlow;
- }
- },
-
- firstUncompletedStageIndex: function(flow) {
- if (this.completedStages === undefined) return 0;
- for (var i = 0; i < flow.stages.length; ++i) {
- if (this.completedStages.indexOf(flow.stages[i]) == -1) {
- return i;
- }
- }
- },
-
- numCompletedStages: function(flow) {
- if (this.completedStages === undefined) return 0;
- var nCompleted = 0;
- for (var i = 0; i < flow.stages.length; ++i) {
- if (this.completedStages.indexOf(flow.stages[i]) > -1) {
- ++nCompleted;
- }
- }
- return nCompleted;
- },
-
- onInitialStageSubmit: function(ev) {
- ev.preventDefault();
-
- var formVals = this.getRegFormVals();
- this.savedParams = formVals;
-
- var badFields = {};
- if (formVals.password != formVals.confirmPassword) {
- badFields.confirmPassword = this.FieldErrors.PasswordMismatch;
- }
- if (formVals.password == '') {
- badFields.password = this.FieldErrors.Missing;
- } else if (formVals.password.length < 6) {
- badFields.password = this.FieldErrors.Length;
- }
- if (formVals.username == '') {
- badFields.username = this.FieldErrors.Missing;
- }
- if (formVals.email == '') {
- badFields.email = this.FieldErrors.Missing;
- }
- if (Object.keys(badFields).length > 0) {
- this.onBadFields(badFields);
- return;
- }
-
- MatrixClientPeg.replaceUsingUrls(
- this.getHsUrl(),
- this.getIsUrl()
- );
- this.setState({
- hs_url: this.getHsUrl(),
- is_url: this.getIsUrl()
- });
- this.setState({busy: true});
-
- this.tryRegister();
- },
-
- startStage: function(stageName) {
- var self = this;
- this.setStep('stage_'+stageName);
- switch(stageName) {
- case 'm.login.email.identity':
- self.setState({
- busy: true
- });
- var cli = MatrixClientPeg.get();
- this.savedParams.client_secret = cli.generateClientSecret();
- this.savedParams.send_attempt = 1;
-
- var nextLink = this.props.registrationUrl +
- '?client_secret=' +
- encodeURIComponent(this.savedParams.client_secret) +
- "&hs_url=" +
- encodeURIComponent(this.state.hs_url) +
- "&is_url=" +
- encodeURIComponent(this.state.is_url) +
- "&session_id=" +
- encodeURIComponent(this.authSessionId);
-
- cli.requestEmailToken(
- this.savedParams.email,
- this.savedParams.client_secret,
- this.savedParams.send_attempt,
- nextLink
- ).done(function(response) {
- self.setState({
- busy: false,
- });
- self.setStep('stage_m.login.email.identity');
- }, function(error) {
- console.error(error);
- self.setStep('initial');
- var newState = {busy: false};
- if (error.errcode == 'THREEPID_IN_USE') {
- self.onBadFields({email: self.FieldErrors.InUse});
- } else {
- newState.errorText = 'Unable to contact the given identity server';
- }
- self.setState(newState);
- });
- break;
- case 'm.login.recaptcha':
- if (!this.authParams || !this.authParams['m.login.recaptcha'].public_key) {
- this.setState({
- errorText: "This server has not supplied enough information for Recaptcha authentication"
- });
- }
- break;
- }
- },
-
- onRegistered: function(user_id, access_token) {
- MatrixClientPeg.replaceUsingAccessToken(
- this.state.hs_url, this.state.is_url, user_id, access_token
- );
- if (this.props.onLoggedIn) {
- this.props.onLoggedIn();
- }
- },
-
- onCaptchaLoaded: function() {
- if (this.refs.recaptchaContainer) {
- var sitekey = this.authParams['m.login.recaptcha'].public_key;
- global.grecaptcha.render('mx_recaptcha', {
- 'sitekey': sitekey,
- 'callback': this.onCaptchaDone
- });
- }
- },
-
- onCaptchaDone: function(captcha_response) {
- this.tryRegister({
- type: 'm.login.recaptcha',
- response: captcha_response
- });
- },
-
- tryRegister: function(auth) {
- var self = this;
- MatrixClientPeg.get().register(
- this.savedParams.username,
- this.savedParams.password,
- this.authSessionId,
- auth
- ).done(function(result) {
- self.onRegistered(result.user_id, result.access_token);
- }, function(error) {
- if (error.httpStatus == 401 && error.data.flows) {
- self.authParams = error.data.params;
- self.authSessionId = error.data.session;
-
- self.completedStages = error.data.completed || [];
-
- var flow = self.chooseFlow(error.data.flows);
-
- if (flow) {
- var flowStage = self.firstUncompletedStageIndex(flow);
- var numDone = self.numCompletedStages(flow);
-
- self.setState({
- busy: false,
- flows: flow,
- currentStep: 1+numDone,
- totalSteps: flow.stages.length+1,
- flowStage: flowStage
- });
- self.startStage(flow.stages[flowStage]);
- }
- else {
- self.setState({
- busy: false,
- errorText: "Unable to register - missing email address?"
- });
- }
- } else {
- console.log(error);
- self.setStep("initial");
- var newState = {
- busy: false,
- errorText: "Unable to contact the given Home Server"
- };
- if (error.name == 'M_USER_IN_USE') {
- delete newState.errorText;
- self.onBadFields({
- username: self.FieldErrors.InUse
- });
- } else if (error.httpStatus == 401) {
- newState.errorText = "Authorisation failed!";
- } else if (error.httpStatus >= 400 && error.httpStatus < 500) {
- newState.errorText = "Registration failed!";
- } else if (error.httpStatus >= 500 && error.httpStatus < 600) {
- newState.errorText = "Server error during registration!";
- } else if (error.name == "M_MISSING_PARAM") {
- // The HS hasn't remembered the login params from
- // the first try when the login email was sent.
- newState.errorText = "This home server does not support resuming registration.";
- }
- self.setState(newState);
- }
- });
- },
-
- showLogin: function(ev) {
- ev.preventDefault();
- dis.dispatch({
- action: 'start_login'
- });
- }
-};