element-web/src/components/views/login/RegistrationForm.js

366 lines
14 KiB
JavaScript
Raw Normal View History

/*
2016-01-07 07:06:39 +03:00
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations 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.
*/
import React from 'react';
import { field_input_incorrect } from '../../../UiEffects';
import sdk from '../../../index';
import Email from '../../../email';
import { looksValid as phoneNumberLooksValid } from '../../../phonenumber';
import Modal from '../../../Modal';
More i18n strings (#963) * Add i18n for E2E import and Export Dialogs Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add various previous missing i18n strings Signed-off-by: MTRNord <mtrnord1@gmail.com> * Translate CreateRoomButton Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add ChatInviteDialog and fix missing to. Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add ConfitmRedactDialog translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add DeactivateAccountDialog translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add DeviceVerifyDialog translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add SessionRestoreErrorDialog translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add SetDisplayNameDialog translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add UnknownDeviceDialog translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add AddressTile translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add DeviceVerifyButtons translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add Dropdown translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add UserSelector translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add CaptchaForm translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add CasLogin translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add CustomServerDialog translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add InteractiveAuthEntryComponents translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add LoginFooter translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add RegistrationForm translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add ServerConfig translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add MAudioBody translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add MImageBody translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add MVideoBody translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add TextualBody translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add UnknownBody translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add UrlPreviewSettings translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add AuxPanel translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add PresenceLabel translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * fix syntax error * weird space :P * missing ',' * fix missing value * fix json fail * remove acidential added file * fix another json fail
2017-05-30 17:09:57 +03:00
import { _t } from '../../../languageHandler';
const FIELD_EMAIL = 'field_email';
const FIELD_PHONE_COUNTRY = 'field_phone_country';
const FIELD_PHONE_NUMBER = 'field_phone_number';
const FIELD_USERNAME = 'field_username';
const FIELD_PASSWORD = 'field_password';
const FIELD_PASSWORD_CONFIRM = 'field_password_confirm';
/**
* A pure UI component which displays a registration form.
*/
module.exports = React.createClass({
displayName: 'RegistrationForm',
propTypes: {
// Values pre-filled in the input boxes when the component loads
defaultEmail: React.PropTypes.string,
defaultPhoneCountry: React.PropTypes.string,
defaultPhoneNumber: React.PropTypes.string,
defaultUsername: React.PropTypes.string,
defaultPassword: React.PropTypes.string,
teamsConfig: React.PropTypes.shape({
// Email address to request new teams
supportEmail: React.PropTypes.string,
teams: React.PropTypes.arrayOf(React.PropTypes.shape({
// The displayed name of the team
"name": React.PropTypes.string,
// The domain of team email addresses
"domain": React.PropTypes.string,
})).required,
}),
minPasswordLength: React.PropTypes.number,
onError: React.PropTypes.func,
onRegisterClick: React.PropTypes.func.isRequired, // onRegisterClick(Object) => ?Promise
},
getDefaultProps: function() {
return {
minPasswordLength: 6,
onError: function(e) {
console.error(e);
}
};
},
getInitialState: function() {
return {
fieldValid: {},
selectedTeam: null,
// The ISO2 country code selected in the phone number entry
phoneCountry: this.props.defaultPhoneCountry,
};
},
onSubmit: function(ev) {
ev.preventDefault();
// validate everything, in reverse order so
// the error that ends up being displayed
// is the one from the first invalid field.
// It's not super ideal that this just calls
// onError once for each invalid field.
this.validateField(FIELD_PASSWORD_CONFIRM);
this.validateField(FIELD_PASSWORD);
this.validateField(FIELD_USERNAME);
this.validateField(FIELD_PHONE_NUMBER);
this.validateField(FIELD_EMAIL);
2016-03-21 04:15:11 +03:00
var self = this;
if (this.allFieldsValid()) {
2016-03-21 04:15:11 +03:00
if (this.refs.email.value == '') {
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createTrackedDialog('If you don\'t specify an email address...', '', QuestionDialog, {
title: _t("Warning!"),
2016-03-21 04:15:11 +03:00
description:
<div>
More i18n strings (#963) * Add i18n for E2E import and Export Dialogs Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add various previous missing i18n strings Signed-off-by: MTRNord <mtrnord1@gmail.com> * Translate CreateRoomButton Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add ChatInviteDialog and fix missing to. Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add ConfitmRedactDialog translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add DeactivateAccountDialog translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add DeviceVerifyDialog translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add SessionRestoreErrorDialog translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add SetDisplayNameDialog translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add UnknownDeviceDialog translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add AddressTile translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add DeviceVerifyButtons translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add Dropdown translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add UserSelector translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add CaptchaForm translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add CasLogin translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add CustomServerDialog translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add InteractiveAuthEntryComponents translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add LoginFooter translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add RegistrationForm translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add ServerConfig translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add MAudioBody translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add MImageBody translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add MVideoBody translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add TextualBody translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add UnknownBody translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add UrlPreviewSettings translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add AuxPanel translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add PresenceLabel translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * fix syntax error * weird space :P * missing ',' * fix missing value * fix json fail * remove acidential added file * fix another json fail
2017-05-30 17:09:57 +03:00
{_t("If you don't specify an email address, you won't be able to reset your password. " +
"Are you sure?")}
2016-03-21 04:15:11 +03:00
</div>,
More i18n strings (#963) * Add i18n for E2E import and Export Dialogs Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add various previous missing i18n strings Signed-off-by: MTRNord <mtrnord1@gmail.com> * Translate CreateRoomButton Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add ChatInviteDialog and fix missing to. Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add ConfitmRedactDialog translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add DeactivateAccountDialog translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add DeviceVerifyDialog translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add SessionRestoreErrorDialog translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add SetDisplayNameDialog translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add UnknownDeviceDialog translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add AddressTile translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add DeviceVerifyButtons translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add Dropdown translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add UserSelector translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add CaptchaForm translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add CasLogin translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add CustomServerDialog translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add InteractiveAuthEntryComponents translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add LoginFooter translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add RegistrationForm translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add ServerConfig translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add MAudioBody translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add MImageBody translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add MVideoBody translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add TextualBody translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add UnknownBody translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add UrlPreviewSettings translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add AuxPanel translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add PresenceLabel translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * fix syntax error * weird space :P * missing ',' * fix missing value * fix json fail * remove acidential added file * fix another json fail
2017-05-30 17:09:57 +03:00
button: _t("Continue"),
2016-03-21 04:15:11 +03:00
onFinished: function(confirmed) {
if (confirmed) {
self._doSubmit(ev);
2016-03-21 04:15:11 +03:00
}
},
});
} else {
self._doSubmit(ev);
2016-03-21 04:15:11 +03:00
}
}
},
_doSubmit: function(ev) {
let email = this.refs.email.value.trim();
2016-03-21 04:15:11 +03:00
var promise = this.props.onRegisterClick({
username: this.refs.username.value.trim(),
2016-03-21 04:15:11 +03:00
password: this.refs.password.value.trim(),
email: email,
phoneCountry: this.state.phoneCountry,
phoneNumber: this.refs.phoneNumber.value.trim(),
2016-03-21 04:15:11 +03:00
});
if (promise) {
ev.target.disabled = true;
promise.finally(function() {
ev.target.disabled = false;
});
}
2016-03-21 04:15:11 +03:00
},
/**
* Returns true if all fields were valid last time
* they were validated.
*/
allFieldsValid: function() {
var keys = Object.keys(this.state.fieldValid);
for (var i = 0; i < keys.length; ++i) {
if (this.state.fieldValid[keys[i]] == false) {
return false;
}
}
return true;
},
_isUniEmail: function(email) {
return email.endsWith('.ac.uk') || email.endsWith('.edu') || email.endsWith('matrix.org');
},
validateField: function(field_id) {
var pwd1 = this.refs.password.value.trim();
var pwd2 = this.refs.passwordConfirm.value.trim();
switch (field_id) {
case FIELD_EMAIL:
const email = this.refs.email.value;
if (this.props.teamsConfig && this._isUniEmail(email)) {
const matchingTeam = this.props.teamsConfig.teams.find(
(team) => {
return email.split('@').pop() === team.domain;
}
) || null;
this.setState({
selectedTeam: matchingTeam,
showSupportEmail: !matchingTeam,
});
this.props.onTeamSelected(matchingTeam);
} else {
this.props.onTeamSelected(null);
this.setState({
selectedTeam: null,
showSupportEmail: false,
});
}
const emailValid = email === '' || Email.looksValid(email);
this.markFieldValid(field_id, emailValid, "RegistrationForm.ERR_EMAIL_INVALID");
break;
case FIELD_PHONE_NUMBER:
const phoneNumber = this.refs.phoneNumber.value;
const phoneNumberValid = phoneNumber === '' || phoneNumberLooksValid(phoneNumber);
this.markFieldValid(field_id, phoneNumberValid, "RegistrationForm.ERR_PHONE_NUMBER_INVALID");
break;
case FIELD_USERNAME:
// XXX: SPEC-1
var username = this.refs.username.value.trim();
if (encodeURIComponent(username) != username) {
this.markFieldValid(
field_id,
false,
"RegistrationForm.ERR_USERNAME_INVALID"
);
} else if (username == '') {
this.markFieldValid(
field_id,
false,
"RegistrationForm.ERR_USERNAME_BLANK"
);
} else {
this.markFieldValid(field_id, true);
}
break;
case FIELD_PASSWORD:
if (pwd1 == '') {
this.markFieldValid(
field_id,
false,
"RegistrationForm.ERR_PASSWORD_MISSING"
);
} else if (pwd1.length < this.props.minPasswordLength) {
this.markFieldValid(
field_id,
false,
"RegistrationForm.ERR_PASSWORD_LENGTH"
);
} else {
this.markFieldValid(field_id, true);
}
break;
case FIELD_PASSWORD_CONFIRM:
this.markFieldValid(
field_id, pwd1 == pwd2,
"RegistrationForm.ERR_PASSWORD_MISMATCH"
);
break;
}
},
markFieldValid: function(field_id, val, error_code) {
var fieldValid = this.state.fieldValid;
fieldValid[field_id] = val;
this.setState({fieldValid: fieldValid});
if (!val) {
field_input_incorrect(this.fieldElementById(field_id));
this.props.onError(error_code);
}
},
fieldElementById(field_id) {
switch (field_id) {
case FIELD_EMAIL:
return this.refs.email;
case FIELD_PHONE_NUMBER:
return this.refs.phoneNumber;
case FIELD_USERNAME:
return this.refs.username;
case FIELD_PASSWORD:
return this.refs.password;
case FIELD_PASSWORD_CONFIRM:
return this.refs.passwordConfirm;
}
},
_classForField: function(field_id, ...baseClasses) {
let cls = baseClasses.join(' ');
if (this.state.fieldValid[field_id] === false) {
if (cls) cls += ' ';
cls += 'error';
}
return cls;
},
_onPhoneCountryChange(newVal) {
this.setState({
phoneCountry: newVal.iso2,
phonePrefix: newVal.prefix,
});
},
render: function() {
var self = this;
const emailSection = (
<div>
<input type="text" ref="email"
autoFocus={true} placeholder={_t("Email address (optional)")}
defaultValue={this.props.defaultEmail}
className={this._classForField(FIELD_EMAIL, 'mx_Login_field')}
onBlur={function() {self.validateField(FIELD_EMAIL);}}
value={self.state.email}/>
</div>
);
let belowEmailSection;
if (this.props.teamsConfig) {
if (this.props.teamsConfig.supportEmail && this.state.showSupportEmail) {
belowEmailSection = (
<p className="mx_Login_support">
Sorry, but your university is not registered with us just yet.&nbsp;
Email us on&nbsp;
<a href={"mailto:" + this.props.teamsConfig.supportEmail}>
{this.props.teamsConfig.supportEmail}
</a>&nbsp;
to get your university signed up. Or continue to register with Riot to enjoy our open source platform.
</p>
);
} else if (this.state.selectedTeam) {
belowEmailSection = (
<p className="mx_Login_support">
More i18n strings (#963) * Add i18n for E2E import and Export Dialogs Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add various previous missing i18n strings Signed-off-by: MTRNord <mtrnord1@gmail.com> * Translate CreateRoomButton Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add ChatInviteDialog and fix missing to. Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add ConfitmRedactDialog translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add DeactivateAccountDialog translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add DeviceVerifyDialog translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add SessionRestoreErrorDialog translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add SetDisplayNameDialog translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add UnknownDeviceDialog translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add AddressTile translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add DeviceVerifyButtons translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add Dropdown translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add UserSelector translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add CaptchaForm translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add CasLogin translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add CustomServerDialog translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add InteractiveAuthEntryComponents translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add LoginFooter translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add RegistrationForm translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add ServerConfig translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add MAudioBody translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add MImageBody translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add MVideoBody translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add TextualBody translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add UnknownBody translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add UrlPreviewSettings translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add AuxPanel translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * Add PresenceLabel translations Signed-off-by: MTRNord <mtrnord1@gmail.com> * fix syntax error * weird space :P * missing ',' * fix missing value * fix json fail * remove acidential added file * fix another json fail
2017-05-30 17:09:57 +03:00
{_t("You are registering with %(SelectedTeamName)s", {SelectedTeamName: this.state.selectedTeam.name})}
</p>
);
}
}
const CountryDropdown = sdk.getComponent('views.login.CountryDropdown');
const phoneSection = (
<div className="mx_Login_phoneSection">
<CountryDropdown ref="phone_country" onOptionChange={this._onPhoneCountryChange}
className="mx_Login_phoneCountry mx_Login_field_prefix"
value={this.state.phoneCountry}
isSmall={true}
showPrefix={true}
/>
<input type="text" ref="phoneNumber"
placeholder={_t("Mobile phone number (optional)")}
defaultValue={this.props.defaultPhoneNumber}
className={this._classForField(
FIELD_PHONE_NUMBER,
'mx_Login_phoneNumberField',
'mx_Login_field',
'mx_Login_field_has_prefix'
)}
onBlur={function() {self.validateField(FIELD_PHONE_NUMBER);}}
value={self.state.phoneNumber}
/>
</div>
);
const registerButton = (
<input className="mx_Login_submit" type="submit" value={_t("Register")} />
);
let placeholderUserName = _t("User name");
return (
<div>
<form onSubmit={this.onSubmit}>
{emailSection}
{belowEmailSection}
{phoneSection}
<input type="text" ref="username"
placeholder={ placeholderUserName } defaultValue={this.props.defaultUsername}
className={this._classForField(FIELD_USERNAME, 'mx_Login_field')}
onBlur={function() {self.validateField(FIELD_USERNAME);}} />
<br />
<input type="password" ref="password"
className={this._classForField(FIELD_PASSWORD, 'mx_Login_field')}
onBlur={function() {self.validateField(FIELD_PASSWORD);}}
placeholder={_t("Password")} defaultValue={this.props.defaultPassword} />
<br />
<input type="password" ref="passwordConfirm"
placeholder={_t("Confirm password")}
className={this._classForField(FIELD_PASSWORD_CONFIRM, 'mx_Login_field')}
onBlur={function() {self.validateField(FIELD_PASSWORD_CONFIRM);}}
defaultValue={this.props.defaultPassword} />
<br />
{registerButton}
</form>
</div>
);
}
});