2015-11-30 21:11:04 +03:00
|
|
|
/*
|
2016-01-07 07:06:39 +03:00
|
|
|
Copyright 2015, 2016 OpenMarket Ltd
|
2015-11-30 21:11:04 +03:00
|
|
|
|
|
|
|
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 sdk = require('../../../index');
|
|
|
|
var dis = require('../../../dispatcher');
|
|
|
|
var Signup = require("../../../Signup");
|
|
|
|
var ServerConfig = require("../../views/login/ServerConfig");
|
2016-06-02 15:14:52 +03:00
|
|
|
var MatrixClientPeg = require("../../../MatrixClientPeg");
|
2015-11-30 21:11:04 +03:00
|
|
|
var RegistrationForm = require("../../views/login/RegistrationForm");
|
|
|
|
var CaptchaForm = require("../../views/login/CaptchaForm");
|
|
|
|
|
|
|
|
var MIN_PASSWORD_LENGTH = 6;
|
|
|
|
|
2016-10-12 16:27:21 +03:00
|
|
|
/**
|
|
|
|
* TODO: It would be nice to make use of the InteractiveAuthEntryComponents
|
|
|
|
* here, rather than inventing our own.
|
|
|
|
*/
|
2015-11-30 21:11:04 +03:00
|
|
|
module.exports = React.createClass({
|
|
|
|
displayName: 'Registration',
|
|
|
|
|
|
|
|
propTypes: {
|
|
|
|
onLoggedIn: React.PropTypes.func.isRequired,
|
|
|
|
clientSecret: React.PropTypes.string,
|
|
|
|
sessionId: React.PropTypes.string,
|
|
|
|
registrationUrl: React.PropTypes.string,
|
|
|
|
idSid: React.PropTypes.string,
|
2016-03-15 16:48:46 +03:00
|
|
|
customHsUrl: React.PropTypes.string,
|
|
|
|
customIsUrl: React.PropTypes.string,
|
2016-03-06 22:33:36 +03:00
|
|
|
defaultHsUrl: React.PropTypes.string,
|
|
|
|
defaultIsUrl: React.PropTypes.string,
|
2016-06-02 13:50:00 +03:00
|
|
|
brand: React.PropTypes.string,
|
2015-12-17 17:56:55 +03:00
|
|
|
email: React.PropTypes.string,
|
2016-01-07 20:23:32 +03:00
|
|
|
username: React.PropTypes.string,
|
|
|
|
guestAccessToken: React.PropTypes.string,
|
2016-08-11 18:15:42 +03:00
|
|
|
|
|
|
|
defaultDeviceDisplayName: React.PropTypes.string,
|
|
|
|
|
2015-11-30 21:11:04 +03:00
|
|
|
// registration shouldn't know or care how login is done.
|
2016-03-16 00:04:11 +03:00
|
|
|
onLoginClick: React.PropTypes.func.isRequired,
|
|
|
|
onCancelClick: React.PropTypes.func
|
2015-11-30 21:11:04 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
getInitialState: function() {
|
|
|
|
return {
|
|
|
|
busy: false,
|
|
|
|
errorText: null,
|
2016-07-07 13:26:35 +03:00
|
|
|
// We remember the values entered by the user because
|
|
|
|
// the registration form will be unmounted during the
|
|
|
|
// course of registration, but if there's an error we
|
|
|
|
// want to bring back the registration form with the
|
2016-07-07 15:03:27 +03:00
|
|
|
// values the user entered still in it. We can keep
|
2016-07-07 13:26:35 +03:00
|
|
|
// them in this component's state since this component
|
|
|
|
// persist for the duration of the registration process.
|
2016-07-06 17:22:06 +03:00
|
|
|
formVals: {
|
|
|
|
email: this.props.email,
|
|
|
|
},
|
2015-11-30 21:11:04 +03:00
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
componentWillMount: function() {
|
|
|
|
this.dispatcherRef = dis.register(this.onAction);
|
|
|
|
// attach this to the instance rather than this.state since it isn't UI
|
|
|
|
this.registerLogic = new Signup.Register(
|
2016-08-11 18:15:42 +03:00
|
|
|
this.props.customHsUrl, this.props.customIsUrl, {
|
|
|
|
defaultDeviceDisplayName: this.props.defaultDeviceDisplayName,
|
|
|
|
}
|
2015-11-30 21:11:04 +03:00
|
|
|
);
|
|
|
|
this.registerLogic.setClientSecret(this.props.clientSecret);
|
|
|
|
this.registerLogic.setSessionId(this.props.sessionId);
|
|
|
|
this.registerLogic.setRegistrationUrl(this.props.registrationUrl);
|
|
|
|
this.registerLogic.setIdSid(this.props.idSid);
|
2016-01-07 20:23:32 +03:00
|
|
|
this.registerLogic.setGuestAccessToken(this.props.guestAccessToken);
|
2015-11-30 21:11:04 +03:00
|
|
|
this.registerLogic.recheckState();
|
|
|
|
},
|
|
|
|
|
|
|
|
componentWillUnmount: function() {
|
|
|
|
dis.unregister(this.dispatcherRef);
|
|
|
|
},
|
|
|
|
|
|
|
|
componentDidMount: function() {
|
|
|
|
// may have already done an HTTP hit (e.g. redirect from an email) so
|
|
|
|
// check for any pending response
|
|
|
|
var promise = this.registerLogic.getPromise();
|
|
|
|
if (promise) {
|
|
|
|
this.onProcessingRegistration(promise);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
onHsUrlChanged: function(newHsUrl) {
|
|
|
|
this.registerLogic.setHomeserverUrl(newHsUrl);
|
|
|
|
},
|
|
|
|
|
|
|
|
onIsUrlChanged: function(newIsUrl) {
|
|
|
|
this.registerLogic.setIdentityServerUrl(newIsUrl);
|
|
|
|
},
|
|
|
|
|
|
|
|
onAction: function(payload) {
|
|
|
|
if (payload.action !== "registration_step_update") {
|
|
|
|
return;
|
|
|
|
}
|
2016-01-27 18:44:12 +03:00
|
|
|
// If the registration state has changed, this means the
|
|
|
|
// user now needs to do something. It would be better
|
|
|
|
// to expose the explicitly in the register logic.
|
|
|
|
this.setState({
|
|
|
|
busy: false
|
|
|
|
});
|
2015-11-30 21:11:04 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
onFormSubmit: function(formVals) {
|
|
|
|
var self = this;
|
|
|
|
this.setState({
|
|
|
|
errorText: "",
|
2016-07-06 17:22:06 +03:00
|
|
|
busy: true,
|
|
|
|
formVals: formVals,
|
2015-11-30 21:11:04 +03:00
|
|
|
});
|
2016-03-15 21:35:09 +03:00
|
|
|
|
|
|
|
if (formVals.username !== this.props.username) {
|
|
|
|
// don't try to upgrade if we changed our username
|
|
|
|
this.registerLogic.setGuestAccessToken(null);
|
|
|
|
}
|
|
|
|
|
2015-11-30 21:11:04 +03:00
|
|
|
this.onProcessingRegistration(this.registerLogic.register(formVals));
|
|
|
|
},
|
|
|
|
|
|
|
|
// Promise is resolved when the registration process is FULLY COMPLETE
|
|
|
|
onProcessingRegistration: function(promise) {
|
|
|
|
var self = this;
|
|
|
|
promise.done(function(response) {
|
2016-02-15 23:39:02 +03:00
|
|
|
self.setState({
|
|
|
|
busy: false
|
|
|
|
});
|
2015-11-30 21:11:04 +03:00
|
|
|
if (!response || !response.access_token) {
|
|
|
|
console.warn(
|
|
|
|
"FIXME: Register fulfilled without a final response, " +
|
|
|
|
"did you break the promise chain?"
|
|
|
|
);
|
|
|
|
// no matter, we'll grab it direct
|
|
|
|
response = self.registerLogic.getCredentials();
|
|
|
|
}
|
|
|
|
if (!response || !response.user_id || !response.access_token) {
|
|
|
|
console.error("Final response is missing keys.");
|
|
|
|
self.setState({
|
2016-02-15 22:58:37 +03:00
|
|
|
errorText: "Registration failed on server"
|
2015-11-30 21:11:04 +03:00
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
self.props.onLoggedIn({
|
|
|
|
userId: response.user_id,
|
2016-08-11 16:21:52 +03:00
|
|
|
deviceId: response.device_id,
|
2015-11-30 21:11:04 +03:00
|
|
|
homeserverUrl: self.registerLogic.getHomeserverUrl(),
|
|
|
|
identityServerUrl: self.registerLogic.getIdentityServerUrl(),
|
|
|
|
accessToken: response.access_token
|
|
|
|
});
|
2016-06-02 15:14:52 +03:00
|
|
|
|
|
|
|
if (self.props.brand) {
|
|
|
|
MatrixClientPeg.get().getPushers().done((resp)=>{
|
|
|
|
var pushers = resp.pushers;
|
|
|
|
for (var i = 0; i < pushers.length; ++i) {
|
|
|
|
if (pushers[i].kind == 'email') {
|
|
|
|
var emailPusher = pushers[i];
|
|
|
|
emailPusher.data = { brand: self.props.brand };
|
|
|
|
MatrixClientPeg.get().setPusher(emailPusher).done(() => {
|
|
|
|
console.log("Set email branding to " + self.props.brand);
|
|
|
|
}, (error) => {
|
|
|
|
console.error("Couldn't set email branding: " + error);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, (error) => {
|
|
|
|
console.error("Couldn't get pushers: " + error);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-11-30 21:11:04 +03:00
|
|
|
}, function(err) {
|
|
|
|
if (err.message) {
|
|
|
|
self.setState({
|
|
|
|
errorText: err.message
|
|
|
|
});
|
|
|
|
}
|
|
|
|
self.setState({
|
|
|
|
busy: false
|
|
|
|
});
|
|
|
|
console.log(err);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
onFormValidationFailed: function(errCode) {
|
|
|
|
var errMsg;
|
|
|
|
switch (errCode) {
|
|
|
|
case "RegistrationForm.ERR_PASSWORD_MISSING":
|
|
|
|
errMsg = "Missing password.";
|
|
|
|
break;
|
|
|
|
case "RegistrationForm.ERR_PASSWORD_MISMATCH":
|
|
|
|
errMsg = "Passwords don't match.";
|
|
|
|
break;
|
|
|
|
case "RegistrationForm.ERR_PASSWORD_LENGTH":
|
|
|
|
errMsg = `Password too short (min ${MIN_PASSWORD_LENGTH}).`;
|
|
|
|
break;
|
2016-01-15 16:31:41 +03:00
|
|
|
case "RegistrationForm.ERR_EMAIL_INVALID":
|
|
|
|
errMsg = "This doesn't look like a valid email address";
|
|
|
|
break;
|
|
|
|
case "RegistrationForm.ERR_USERNAME_INVALID":
|
|
|
|
errMsg = "User names may only contain letters, numbers, dots, hyphens and underscores.";
|
|
|
|
break;
|
|
|
|
case "RegistrationForm.ERR_USERNAME_BLANK":
|
|
|
|
errMsg = "You need to enter a user name";
|
|
|
|
break;
|
2015-11-30 21:11:04 +03:00
|
|
|
default:
|
|
|
|
console.error("Unknown error code: %s", errCode);
|
|
|
|
errMsg = "An unknown error occurred.";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
this.setState({
|
|
|
|
errorText: errMsg
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2016-10-11 20:00:47 +03:00
|
|
|
onCaptchaResponse: function(response) {
|
2015-11-30 21:11:04 +03:00
|
|
|
this.registerLogic.tellStage("m.login.recaptcha", {
|
2016-10-11 20:00:47 +03:00
|
|
|
response: response
|
2015-11-30 21:11:04 +03:00
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
_getRegisterContentJsx: function() {
|
|
|
|
var currStep = this.registerLogic.getStep();
|
|
|
|
var registerStep;
|
|
|
|
switch (currStep) {
|
|
|
|
case "Register.COMPLETE":
|
|
|
|
break; // NOP
|
|
|
|
case "Register.START":
|
|
|
|
case "Register.STEP_m.login.dummy":
|
2016-07-06 17:22:06 +03:00
|
|
|
// NB. Our 'username' prop is specifically for upgrading
|
|
|
|
// a guest account
|
2015-11-30 21:11:04 +03:00
|
|
|
registerStep = (
|
|
|
|
<RegistrationForm
|
|
|
|
showEmail={true}
|
2016-07-06 17:22:06 +03:00
|
|
|
defaultUsername={this.state.formVals.username}
|
|
|
|
defaultEmail={this.state.formVals.email}
|
|
|
|
defaultPassword={this.state.formVals.password}
|
|
|
|
guestUsername={this.props.username}
|
2015-11-30 21:11:04 +03:00
|
|
|
minPasswordLength={MIN_PASSWORD_LENGTH}
|
|
|
|
onError={this.onFormValidationFailed}
|
|
|
|
onRegisterClick={this.onFormSubmit} />
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
case "Register.STEP_m.login.email.identity":
|
|
|
|
registerStep = (
|
|
|
|
<div>
|
|
|
|
Please check your email to continue registration.
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
case "Register.STEP_m.login.recaptcha":
|
2016-10-11 20:00:47 +03:00
|
|
|
var publicKey;
|
|
|
|
var serverParams = this.registerLogic.getServerData().params;
|
|
|
|
if (serverParams && serverParams["m.login.recaptcha"]) {
|
|
|
|
publicKey = serverParams["m.login.recaptcha"].public_key;
|
|
|
|
}
|
2016-12-24 06:15:30 +03:00
|
|
|
|
2015-11-30 21:11:04 +03:00
|
|
|
registerStep = (
|
2016-10-11 20:00:47 +03:00
|
|
|
<CaptchaForm sitePublicKey={publicKey}
|
|
|
|
onCaptchaResponse={this.onCaptchaResponse}
|
|
|
|
/>
|
2015-11-30 21:11:04 +03:00
|
|
|
);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
console.error("Unknown register state: %s", currStep);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
var busySpinner;
|
|
|
|
if (this.state.busy) {
|
|
|
|
var Spinner = sdk.getComponent("elements.Spinner");
|
|
|
|
busySpinner = (
|
|
|
|
<Spinner />
|
|
|
|
);
|
|
|
|
}
|
2016-03-16 00:04:11 +03:00
|
|
|
|
|
|
|
var returnToAppJsx;
|
|
|
|
if (this.props.onCancelClick) {
|
2016-08-11 16:21:52 +03:00
|
|
|
returnToAppJsx =
|
2016-03-16 00:04:11 +03:00
|
|
|
<a className="mx_Login_create" onClick={this.props.onCancelClick} href="#">
|
|
|
|
Return to app
|
|
|
|
</a>
|
|
|
|
}
|
|
|
|
|
2015-11-30 21:11:04 +03:00
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
<h2>Create an account</h2>
|
|
|
|
{registerStep}
|
|
|
|
<div className="mx_Login_error">{this.state.errorText}</div>
|
|
|
|
{busySpinner}
|
|
|
|
<ServerConfig ref="serverConfig"
|
2016-03-16 22:14:28 +03:00
|
|
|
withToggleButton={ this.registerLogic.getStep() === "Register.START" }
|
2016-03-15 16:48:46 +03:00
|
|
|
customHsUrl={this.props.customHsUrl}
|
|
|
|
customIsUrl={this.props.customIsUrl}
|
2016-03-06 22:33:36 +03:00
|
|
|
defaultHsUrl={this.props.defaultHsUrl}
|
|
|
|
defaultIsUrl={this.props.defaultIsUrl}
|
2015-11-30 21:11:04 +03:00
|
|
|
onHsUrlChanged={this.onHsUrlChanged}
|
|
|
|
onIsUrlChanged={this.onIsUrlChanged}
|
|
|
|
delayTimeMs={1000} />
|
2016-03-06 22:33:36 +03:00
|
|
|
<div className="mx_Login_error">
|
|
|
|
</div>
|
2015-11-30 21:11:04 +03:00
|
|
|
<a className="mx_Login_create" onClick={this.props.onLoginClick} href="#">
|
|
|
|
I already have an account
|
|
|
|
</a>
|
2016-03-16 00:04:11 +03:00
|
|
|
{ returnToAppJsx }
|
2015-11-30 21:11:04 +03:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
render: function() {
|
2015-12-01 19:29:58 +03:00
|
|
|
var LoginHeader = sdk.getComponent('login.LoginHeader');
|
2016-03-06 22:33:36 +03:00
|
|
|
var LoginFooter = sdk.getComponent('login.LoginFooter');
|
2015-11-30 21:11:04 +03:00
|
|
|
return (
|
|
|
|
<div className="mx_Login">
|
|
|
|
<div className="mx_Login_box">
|
2015-12-01 19:29:58 +03:00
|
|
|
<LoginHeader />
|
2015-11-30 21:11:04 +03:00
|
|
|
{this._getRegisterContentJsx()}
|
2016-03-06 22:33:36 +03:00
|
|
|
<LoginFooter />
|
2015-11-30 21:11:04 +03:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|