Merge pull request #2527 from jryans/auth-registration
Style registration flow
This commit is contained in:
commit
4d2a93eaaf
12 changed files with 324 additions and 152 deletions
|
@ -30,6 +30,7 @@
|
||||||
@import "./views/auth/_AuthHeader.scss";
|
@import "./views/auth/_AuthHeader.scss";
|
||||||
@import "./views/auth/_AuthHeaderLogo.scss";
|
@import "./views/auth/_AuthHeaderLogo.scss";
|
||||||
@import "./views/auth/_AuthPage.scss";
|
@import "./views/auth/_AuthPage.scss";
|
||||||
|
@import "./views/auth/_CountryDropdown.scss";
|
||||||
@import "./views/auth/_InteractiveAuthEntryComponents.scss";
|
@import "./views/auth/_InteractiveAuthEntryComponents.scss";
|
||||||
@import "./views/auth/_LanguageSelector.scss";
|
@import "./views/auth/_LanguageSelector.scss";
|
||||||
@import "./views/auth/_ServerConfig.scss";
|
@import "./views/auth/_ServerConfig.scss";
|
||||||
|
|
|
@ -15,13 +15,6 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.mx_Login_support {
|
|
||||||
text-align: center;
|
|
||||||
font-size: 13px;
|
|
||||||
margin-top: 0px;
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_Login_field {
|
.mx_Login_field {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
@ -33,14 +26,6 @@ limitations under the License.
|
||||||
margin-bottom: 14px;
|
margin-bottom: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_Login_fieldLabel {
|
|
||||||
margin-top: -10px;
|
|
||||||
margin-left: 8px;
|
|
||||||
margin-bottom: 14px;
|
|
||||||
font-size: 13px;
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_Login_submit {
|
.mx_Login_submit {
|
||||||
@mixin mx_DialogButton;
|
@mixin mx_DialogButton;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -58,16 +43,6 @@ limitations under the License.
|
||||||
opacity: 0.3;
|
opacity: 0.3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_Login_label {
|
|
||||||
font-size: 13px;
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_Login_checkbox,
|
|
||||||
.mx_Login_radio {
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_AuthBody a.mx_Login_sso_link:link,
|
.mx_AuthBody a.mx_Login_sso_link:link,
|
||||||
.mx_AuthBody a.mx_Login_sso_link:hover,
|
.mx_AuthBody a.mx_Login_sso_link:hover,
|
||||||
.mx_AuthBody a.mx_Login_sso_link:visited {
|
.mx_AuthBody a.mx_Login_sso_link:visited {
|
||||||
|
@ -119,10 +94,6 @@ limitations under the License.
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_Login_field_group {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_Login_field_prefix {
|
.mx_Login_field_prefix {
|
||||||
height: 38px;
|
height: 38px;
|
||||||
padding: 0px 5px;
|
padding: 0px 5px;
|
||||||
|
@ -147,7 +118,6 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_Login_phoneCountry {
|
.mx_Login_phoneCountry {
|
||||||
margin-bottom: 14px;
|
margin-bottom: 14px;
|
||||||
width: 150px;
|
|
||||||
|
|
||||||
/* To override mx_Login_field_prefix */
|
/* To override mx_Login_field_prefix */
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
|
|
@ -47,6 +47,24 @@ limitations under the License.
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_Auth_fieldRow {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_Auth_fieldRow > * {
|
||||||
|
margin: 0 5px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_Auth_fieldRow > *:first-child {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_Auth_fieldRow > *:last-child {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_AuthBody a:link,
|
.mx_AuthBody a:link,
|
||||||
.mx_AuthBody a:hover,
|
.mx_AuthBody a:hover,
|
||||||
.mx_AuthBody a:visited {
|
.mx_AuthBody a:visited {
|
||||||
|
|
34
res/css/views/auth/_CountryDropdown.scss
Normal file
34
res/css/views/auth/_CountryDropdown.scss
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
Copyright 2019 New Vector 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.mx_CountryDropdown .mx_Dropdown_input .mx_Dropdown_option {
|
||||||
|
padding: 0 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CountryDropdown .mx_Dropdown_arrow {
|
||||||
|
padding-right: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CountryDropdown_shortOption {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CountryDropdown_option {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
|
@ -25,22 +25,6 @@ limitations under the License.
|
||||||
color: $authpage-lang-color;
|
color: $authpage-lang-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: Consider using this new arrow for all dropdowns */
|
|
||||||
.mx_Auth_language .mx_Dropdown_arrow {
|
|
||||||
width: 10px;
|
|
||||||
height: 6px;
|
|
||||||
border: none;
|
|
||||||
right: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_Auth_language .mx_Dropdown_arrow::before {
|
.mx_Auth_language .mx_Dropdown_arrow::before {
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
mask: url('$(res)/img/feather-icons/dropdown-arrow.svg');
|
|
||||||
mask-repeat: no-repeat;
|
|
||||||
background: $authpage-lang-color;
|
background: $authpage-lang-color;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,11 +23,12 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_Dropdown_input {
|
.mx_Dropdown_input {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
border: 1px solid $strong-input-border-color;
|
border: 1px solid $strong-input-border-color;
|
||||||
font-weight: 300;
|
font-size: 12px;
|
||||||
font-size: 13px;
|
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,19 +42,23 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_Dropdown_arrow {
|
.mx_Dropdown_arrow {
|
||||||
border-color: $primary-fg-color transparent transparent;
|
|
||||||
border-style: solid;
|
|
||||||
border-width: 5px 5px 0;
|
|
||||||
display: block;
|
display: block;
|
||||||
height: 0;
|
position: relative;
|
||||||
position: absolute;
|
width: 10px;
|
||||||
right: 10px;
|
height: 6px;
|
||||||
top: 14px;
|
padding-right: 8px;
|
||||||
width: 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_Dropdown.left_aligned .mx_Dropdown_arrow {
|
.mx_Dropdown_arrow::before {
|
||||||
left: 10px;
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
mask: url('$(res)/img/feather-icons/dropdown-arrow.svg');
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
background: $primary-fg-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_Dropdown_input > .mx_Dropdown_option {
|
.mx_Dropdown_input > .mx_Dropdown_option {
|
||||||
|
@ -62,10 +67,6 @@ limitations under the License.
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_Dropdown.left_aligned .mx_Dropdown_input > .mx_Dropdown_option {
|
|
||||||
padding-left: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_Dropdown_option {
|
.mx_Dropdown_option {
|
||||||
height: 35px;
|
height: 35px;
|
||||||
line-height: 35px;
|
line-height: 35px;
|
||||||
|
@ -81,7 +82,7 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_Dropdown_option img {
|
.mx_Dropdown_option img {
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
width: 27px;
|
width: 16px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -539,7 +539,7 @@ module.exports = React.createClass({
|
||||||
return errorText;
|
return errorText;
|
||||||
},
|
},
|
||||||
|
|
||||||
serverComponentForStep() {
|
renderServerComponentForStep() {
|
||||||
const ServerTypeSelector = sdk.getComponent("auth.ServerTypeSelector");
|
const ServerTypeSelector = sdk.getComponent("auth.ServerTypeSelector");
|
||||||
const ServerConfig = sdk.getComponent("auth.ServerConfig");
|
const ServerConfig = sdk.getComponent("auth.ServerConfig");
|
||||||
const ModularServerConfig = sdk.getComponent("auth.ModularServerConfig");
|
const ModularServerConfig = sdk.getComponent("auth.ModularServerConfig");
|
||||||
|
@ -605,7 +605,7 @@ module.exports = React.createClass({
|
||||||
</div>;
|
</div>;
|
||||||
},
|
},
|
||||||
|
|
||||||
loginComponentForStep() {
|
renderLoginComponentForStep() {
|
||||||
if (PHASES_ENABLED && this.state.phase !== PHASE_LOGIN) {
|
if (PHASES_ENABLED && this.state.phase !== PHASE_LOGIN) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -707,8 +707,8 @@ module.exports = React.createClass({
|
||||||
{loader}
|
{loader}
|
||||||
</h2>
|
</h2>
|
||||||
{ errorTextSection }
|
{ errorTextSection }
|
||||||
{ this.serverComponentForStep() }
|
{ this.renderServerComponentForStep() }
|
||||||
{ this.loginComponentForStep() }
|
{ this.renderLoginComponentForStep() }
|
||||||
<a className="mx_Auth_changeFlow" onClick={this.onRegisterClick} href="#">
|
<a className="mx_Auth_changeFlow" onClick={this.onRegisterClick} href="#">
|
||||||
{ _t('Create account') }
|
{ _t('Create account') }
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -23,13 +23,22 @@ import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import RegistrationForm from '../../views/auth/RegistrationForm';
|
|
||||||
import { _t, _td } from '../../../languageHandler';
|
import { _t, _td } from '../../../languageHandler';
|
||||||
import SdkConfig from '../../../SdkConfig';
|
import SdkConfig from '../../../SdkConfig';
|
||||||
import { messageForResourceLimitError } from '../../../utils/ErrorUtils';
|
import { messageForResourceLimitError } from '../../../utils/ErrorUtils';
|
||||||
|
import * as ServerType from '../../views/auth/ServerTypeSelector';
|
||||||
|
|
||||||
const MIN_PASSWORD_LENGTH = 6;
|
const MIN_PASSWORD_LENGTH = 6;
|
||||||
|
|
||||||
|
// Phases
|
||||||
|
// Show controls to configure server details
|
||||||
|
const PHASE_SERVER_DETAILS = 0;
|
||||||
|
// Show the appropriate registration flow(s) for the server
|
||||||
|
const PHASE_REGISTRATION = 1;
|
||||||
|
|
||||||
|
// Enable phases for registration
|
||||||
|
const PHASES_ENABLED = true;
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'Registration',
|
displayName: 'Registration',
|
||||||
|
|
||||||
|
@ -82,6 +91,7 @@ module.exports = React.createClass({
|
||||||
// If we've been given a session ID, we're resuming
|
// If we've been given a session ID, we're resuming
|
||||||
// straight back into UI auth
|
// straight back into UI auth
|
||||||
doingUIAuth: Boolean(this.props.sessionId),
|
doingUIAuth: Boolean(this.props.sessionId),
|
||||||
|
serverType: null,
|
||||||
hsUrl: this.props.customHsUrl,
|
hsUrl: this.props.customHsUrl,
|
||||||
isUrl: this.props.customIsUrl,
|
isUrl: this.props.customIsUrl,
|
||||||
flows: null,
|
flows: null,
|
||||||
|
@ -107,6 +117,39 @@ module.exports = React.createClass({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onServerTypeChange(type) {
|
||||||
|
this.setState({
|
||||||
|
serverType: type,
|
||||||
|
});
|
||||||
|
|
||||||
|
// When changing server types, set the HS / IS URLs to reasonable defaults for the
|
||||||
|
// the new type.
|
||||||
|
switch (type) {
|
||||||
|
case ServerType.FREE: {
|
||||||
|
const { hsUrl, isUrl } = ServerType.TYPES.FREE;
|
||||||
|
this.onServerConfigChange({
|
||||||
|
hsUrl,
|
||||||
|
isUrl,
|
||||||
|
});
|
||||||
|
// Move directly to the registration phase since the server details are fixed.
|
||||||
|
this.setState({
|
||||||
|
phase: PHASE_REGISTRATION,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ServerType.PREMIUM:
|
||||||
|
case ServerType.ADVANCED:
|
||||||
|
this.onServerConfigChange({
|
||||||
|
hsUrl: this.props.defaultHsUrl,
|
||||||
|
isUrl: this.props.defaultIsUrl,
|
||||||
|
});
|
||||||
|
this.setState({
|
||||||
|
phase: PHASE_SERVER_DETAILS,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
_replaceClient: async function() {
|
_replaceClient: async function() {
|
||||||
this._matrixClient = Matrix.createClient({
|
this._matrixClient = Matrix.createClient({
|
||||||
baseUrl: this.state.hsUrl,
|
baseUrl: this.state.hsUrl,
|
||||||
|
@ -273,6 +316,21 @@ module.exports = React.createClass({
|
||||||
this.props.onLoginClick();
|
this.props.onLoginClick();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onServerDetailsNextPhaseClick(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
this.setState({
|
||||||
|
phase: PHASE_REGISTRATION,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onEditServerDetailsClick(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
this.setState({
|
||||||
|
phase: PHASE_SERVER_DETAILS,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
_makeRegisterRequest: function(auth) {
|
_makeRegisterRequest: function(auth) {
|
||||||
// Only send the bind params if we're sending username / pw params
|
// Only send the bind params if we're sending username / pw params
|
||||||
// (Since we need to send no params at all to use the ones saved in the
|
// (Since we need to send no params at all to use the ones saved in the
|
||||||
|
@ -300,62 +358,127 @@ module.exports = React.createClass({
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
renderServerComponent() {
|
||||||
|
const ServerTypeSelector = sdk.getComponent("auth.ServerTypeSelector");
|
||||||
|
const ServerConfig = sdk.getComponent("auth.ServerConfig");
|
||||||
|
const ModularServerConfig = sdk.getComponent("auth.ModularServerConfig");
|
||||||
|
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
|
||||||
|
|
||||||
|
// TODO: May need to adjust the behavior of this config option
|
||||||
|
if (SdkConfig.get()['disable_custom_urls']) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're on a different phase, we only show the server type selector,
|
||||||
|
// which is always shown if we allow custom URLs at all.
|
||||||
|
if (PHASES_ENABLED && this.state.phase !== PHASE_SERVER_DETAILS) {
|
||||||
|
return <div>
|
||||||
|
<ServerTypeSelector
|
||||||
|
defaultHsUrl={this.props.defaultHsUrl}
|
||||||
|
onChange={this.onServerTypeChange}
|
||||||
|
/>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
let serverDetails = null;
|
||||||
|
switch (this.state.serverType) {
|
||||||
|
case ServerType.FREE:
|
||||||
|
break;
|
||||||
|
case ServerType.PREMIUM:
|
||||||
|
serverDetails = <ModularServerConfig
|
||||||
|
customHsUrl={this.state.discoveredHsUrl || this.props.customHsUrl}
|
||||||
|
defaultHsUrl={this.props.defaultHsUrl}
|
||||||
|
defaultIsUrl={this.props.defaultIsUrl}
|
||||||
|
onServerConfigChange={this.onServerConfigChange}
|
||||||
|
delayTimeMs={1000}
|
||||||
|
/>;
|
||||||
|
break;
|
||||||
|
case ServerType.ADVANCED:
|
||||||
|
serverDetails = <ServerConfig
|
||||||
|
customHsUrl={this.state.discoveredHsUrl || this.props.customHsUrl}
|
||||||
|
customIsUrl={this.state.discoveredIsUrl || this.props.customIsUrl}
|
||||||
|
defaultHsUrl={this.props.defaultHsUrl}
|
||||||
|
defaultIsUrl={this.props.defaultIsUrl}
|
||||||
|
onServerConfigChange={this.onServerConfigChange}
|
||||||
|
delayTimeMs={1000}
|
||||||
|
/>;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let nextButton = null;
|
||||||
|
if (PHASES_ENABLED) {
|
||||||
|
nextButton = <AccessibleButton className="mx_Login_submit"
|
||||||
|
onClick={this.onServerDetailsNextPhaseClick}
|
||||||
|
>
|
||||||
|
{_t("Next")}
|
||||||
|
</AccessibleButton>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div>
|
||||||
|
<ServerTypeSelector
|
||||||
|
defaultHsUrl={this.props.defaultHsUrl}
|
||||||
|
onChange={this.onServerTypeChange}
|
||||||
|
/>
|
||||||
|
{serverDetails}
|
||||||
|
{nextButton}
|
||||||
|
</div>;
|
||||||
|
},
|
||||||
|
|
||||||
|
renderRegisterComponent() {
|
||||||
|
if (PHASES_ENABLED && this.state.phase !== PHASE_REGISTRATION) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const InteractiveAuth = sdk.getComponent('structures.InteractiveAuth');
|
||||||
|
const Spinner = sdk.getComponent('elements.Spinner');
|
||||||
|
const RegistrationForm = sdk.getComponent('auth.RegistrationForm');
|
||||||
|
|
||||||
|
if (this.state.doingUIAuth) {
|
||||||
|
return <InteractiveAuth
|
||||||
|
matrixClient={this._matrixClient}
|
||||||
|
makeRequest={this._makeRegisterRequest}
|
||||||
|
onAuthFinished={this._onUIAuthFinished}
|
||||||
|
inputs={this._getUIAuthInputs()}
|
||||||
|
makeRegistrationUrl={this.props.makeRegistrationUrl}
|
||||||
|
sessionId={this.props.sessionId}
|
||||||
|
clientSecret={this.props.clientSecret}
|
||||||
|
emailSid={this.props.idSid}
|
||||||
|
poll={true}
|
||||||
|
/>;
|
||||||
|
} else if (this.state.busy || !this.state.flows) {
|
||||||
|
return <Spinner />;
|
||||||
|
} else {
|
||||||
|
let onEditServerDetailsClick = null;
|
||||||
|
// If custom URLs are allowed and we haven't selected the Free server type, wire
|
||||||
|
// up the server details edit link.
|
||||||
|
if (
|
||||||
|
PHASES_ENABLED &&
|
||||||
|
!SdkConfig.get()['disable_custom_urls'] &&
|
||||||
|
this.state.serverType !== ServerType.FREE
|
||||||
|
) {
|
||||||
|
onEditServerDetailsClick = this.onEditServerDetailsClick;
|
||||||
|
}
|
||||||
|
return <RegistrationForm
|
||||||
|
defaultUsername={this.state.formVals.username}
|
||||||
|
defaultEmail={this.state.formVals.email}
|
||||||
|
defaultPhoneCountry={this.state.formVals.phoneCountry}
|
||||||
|
defaultPhoneNumber={this.state.formVals.phoneNumber}
|
||||||
|
defaultPassword={this.state.formVals.password}
|
||||||
|
minPasswordLength={MIN_PASSWORD_LENGTH}
|
||||||
|
onError={this.onFormValidationFailed}
|
||||||
|
onRegisterClick={this.onFormSubmit}
|
||||||
|
onEditServerDetailsClick={onEditServerDetailsClick}
|
||||||
|
flows={this.state.flows}
|
||||||
|
hsUrl={this.state.hsUrl}
|
||||||
|
hsName={this.props.defaultServerName}
|
||||||
|
/>;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
const AuthHeader = sdk.getComponent('auth.AuthHeader');
|
const AuthHeader = sdk.getComponent('auth.AuthHeader');
|
||||||
const AuthBody = sdk.getComponent("auth.AuthBody");
|
const AuthBody = sdk.getComponent("auth.AuthBody");
|
||||||
const AuthPage = sdk.getComponent('auth.AuthPage');
|
const AuthPage = sdk.getComponent('auth.AuthPage');
|
||||||
const InteractiveAuth = sdk.getComponent('structures.InteractiveAuth');
|
|
||||||
const Spinner = sdk.getComponent("elements.Spinner");
|
|
||||||
const ServerConfig = sdk.getComponent('views.auth.ServerConfig');
|
|
||||||
|
|
||||||
let registerBody;
|
|
||||||
if (this.state.doingUIAuth) {
|
|
||||||
registerBody = (
|
|
||||||
<InteractiveAuth
|
|
||||||
matrixClient={this._matrixClient}
|
|
||||||
makeRequest={this._makeRegisterRequest}
|
|
||||||
onAuthFinished={this._onUIAuthFinished}
|
|
||||||
inputs={this._getUIAuthInputs()}
|
|
||||||
makeRegistrationUrl={this.props.makeRegistrationUrl}
|
|
||||||
sessionId={this.props.sessionId}
|
|
||||||
clientSecret={this.props.clientSecret}
|
|
||||||
emailSid={this.props.idSid}
|
|
||||||
poll={true}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
} else if (this.state.busy || !this.state.flows) {
|
|
||||||
registerBody = <Spinner />;
|
|
||||||
} else {
|
|
||||||
let serverConfigSection;
|
|
||||||
if (!SdkConfig.get()['disable_custom_urls']) {
|
|
||||||
serverConfigSection = (
|
|
||||||
<ServerConfig ref="serverConfig"
|
|
||||||
customHsUrl={this.props.customHsUrl}
|
|
||||||
customIsUrl={this.props.customIsUrl}
|
|
||||||
defaultHsUrl={this.props.defaultHsUrl}
|
|
||||||
defaultIsUrl={this.props.defaultIsUrl}
|
|
||||||
onServerConfigChange={this.onServerConfigChange}
|
|
||||||
delayTimeMs={1000}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
registerBody = (
|
|
||||||
<div>
|
|
||||||
<RegistrationForm
|
|
||||||
defaultUsername={this.state.formVals.username}
|
|
||||||
defaultEmail={this.state.formVals.email}
|
|
||||||
defaultPhoneCountry={this.state.formVals.phoneCountry}
|
|
||||||
defaultPhoneNumber={this.state.formVals.phoneNumber}
|
|
||||||
defaultPassword={this.state.formVals.password}
|
|
||||||
minPasswordLength={MIN_PASSWORD_LENGTH}
|
|
||||||
onError={this.onFormValidationFailed}
|
|
||||||
onRegisterClick={this.onFormSubmit}
|
|
||||||
flows={this.state.flows}
|
|
||||||
/>
|
|
||||||
{ serverConfigSection }
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let errorText;
|
let errorText;
|
||||||
const err = this.state.errorText || this.props.defaultServerDiscoveryError;
|
const err = this.state.errorText || this.props.defaultServerDiscoveryError;
|
||||||
|
@ -377,9 +500,10 @@ module.exports = React.createClass({
|
||||||
<AuthHeader />
|
<AuthHeader />
|
||||||
<AuthBody>
|
<AuthBody>
|
||||||
<h2>{ _t('Create your account') }</h2>
|
<h2>{ _t('Create your account') }</h2>
|
||||||
{ registerBody }
|
|
||||||
{ signIn }
|
|
||||||
{ errorText }
|
{ errorText }
|
||||||
|
{ this.renderServerComponent() }
|
||||||
|
{ this.renderRegisterComponent() }
|
||||||
|
{ signIn }
|
||||||
</AuthBody>
|
</AuthBody>
|
||||||
</AuthPage>
|
</AuthPage>
|
||||||
);
|
);
|
||||||
|
|
|
@ -81,7 +81,7 @@ export default class CountryDropdown extends React.Component {
|
||||||
if (this.props.showPrefix) {
|
if (this.props.showPrefix) {
|
||||||
countryPrefix = '+' + COUNTRIES_BY_ISO2[iso2].prefix;
|
countryPrefix = '+' + COUNTRIES_BY_ISO2[iso2].prefix;
|
||||||
}
|
}
|
||||||
return <span>
|
return <span className="mx_CountryDropdown_shortOption">
|
||||||
{ this._flagImgForIso2(iso2) }
|
{ this._flagImgForIso2(iso2) }
|
||||||
{ countryPrefix }
|
{ countryPrefix }
|
||||||
</span>;
|
</span>;
|
||||||
|
@ -111,7 +111,7 @@ export default class CountryDropdown extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
const options = displayedCountries.map((country) => {
|
const options = displayedCountries.map((country) => {
|
||||||
return <div key={country.iso2}>
|
return <div className="mx_CountryDropdown_option" key={country.iso2}>
|
||||||
{ this._flagImgForIso2(country.iso2) }
|
{ this._flagImgForIso2(country.iso2) }
|
||||||
{ country.name } <span>(+{ country.prefix })</span>
|
{ country.name } <span>(+{ country.prefix })</span>
|
||||||
</div>;
|
</div>;
|
||||||
|
@ -121,7 +121,7 @@ export default class CountryDropdown extends React.Component {
|
||||||
// values between mounting and the initial value propgating
|
// values between mounting and the initial value propgating
|
||||||
const value = this.props.value || COUNTRIES[0].iso2;
|
const value = this.props.value || COUNTRIES[0].iso2;
|
||||||
|
|
||||||
return <Dropdown className={this.props.className + " left_aligned"}
|
return <Dropdown className={this.props.className + " mx_CountryDropdown"}
|
||||||
onOptionChange={this._onOptionChange} onSearchChange={this._onSearchChange}
|
onOptionChange={this._onOptionChange} onSearchChange={this._onSearchChange}
|
||||||
menuWidth={298} getShortOption={this._getShortOption}
|
menuWidth={298} getShortOption={this._getShortOption}
|
||||||
value={value} searchEnabled={true} disabled={this.props.disabled}
|
value={value} searchEnabled={true} disabled={this.props.disabled}
|
||||||
|
|
|
@ -40,7 +40,7 @@ class PasswordLogin extends React.Component {
|
||||||
initialPhoneNumber: "",
|
initialPhoneNumber: "",
|
||||||
initialPassword: "",
|
initialPassword: "",
|
||||||
loginIncorrect: false,
|
loginIncorrect: false,
|
||||||
hsDomain: "",
|
hsUrl: "",
|
||||||
hsName: null,
|
hsName: null,
|
||||||
disableSubmit: false,
|
disableSubmit: false,
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,7 @@ module.exports = React.createClass({
|
||||||
minPasswordLength: PropTypes.number,
|
minPasswordLength: PropTypes.number,
|
||||||
onError: PropTypes.func,
|
onError: PropTypes.func,
|
||||||
onRegisterClick: PropTypes.func.isRequired, // onRegisterClick(Object) => ?Promise
|
onRegisterClick: PropTypes.func.isRequired, // onRegisterClick(Object) => ?Promise
|
||||||
|
onEditServerDetailsClick: PropTypes.func,
|
||||||
flows: PropTypes.arrayOf(PropTypes.object).isRequired,
|
flows: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -256,9 +257,34 @@ module.exports = React.createClass({
|
||||||
render: function() {
|
render: function() {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
|
let yourMatrixAccountText = _t('Create your account');
|
||||||
|
if (this.props.hsName) {
|
||||||
|
yourMatrixAccountText = _t('Create your %(serverName)s account', {
|
||||||
|
serverName: this.props.hsName,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
const parsedHsUrl = new URL(this.props.hsUrl);
|
||||||
|
yourMatrixAccountText = _t('Create your %(serverName)s account', {
|
||||||
|
serverName: parsedHsUrl.hostname,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let editLink = null;
|
||||||
|
if (this.props.onEditServerDetailsClick) {
|
||||||
|
editLink = <a className="mx_Auth_editServerDetails"
|
||||||
|
href="#" onClick={this.props.onEditServerDetailsClick}
|
||||||
|
>
|
||||||
|
{_t('Edit')}
|
||||||
|
</a>;
|
||||||
|
}
|
||||||
|
|
||||||
const emailPlaceholder = this._authStepIsRequired('m.login.email.identity') ?
|
const emailPlaceholder = this._authStepIsRequired('m.login.email.identity') ?
|
||||||
_t("Email address") :
|
_t("Email") :
|
||||||
_t("Email address (optional)");
|
_t("Email (optional)");
|
||||||
|
|
||||||
const emailSection = (
|
const emailSection = (
|
||||||
<div>
|
<div>
|
||||||
|
@ -275,8 +301,8 @@ module.exports = React.createClass({
|
||||||
let phoneSection;
|
let phoneSection;
|
||||||
if (!SdkConfig.get().disable_3pid_login) {
|
if (!SdkConfig.get().disable_3pid_login) {
|
||||||
const phonePlaceholder = this._authStepIsRequired('m.login.msisdn') ?
|
const phonePlaceholder = this._authStepIsRequired('m.login.msisdn') ?
|
||||||
_t("Mobile phone number") :
|
_t("Phone") :
|
||||||
_t("Mobile phone number (optional)");
|
_t("Phone (optional)");
|
||||||
phoneSection = (
|
phoneSection = (
|
||||||
<div className="mx_Login_phoneSection">
|
<div className="mx_Login_phoneSection">
|
||||||
<CountryDropdown ref="phone_country" onOptionChange={this._onPhoneCountryChange}
|
<CountryDropdown ref="phone_country" onOptionChange={this._onPhoneCountryChange}
|
||||||
|
@ -309,25 +335,36 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<h3>
|
||||||
|
{yourMatrixAccountText}
|
||||||
|
{editLink}
|
||||||
|
</h3>
|
||||||
<form onSubmit={this.onSubmit}>
|
<form onSubmit={this.onSubmit}>
|
||||||
{ emailSection }
|
<div className="mx_Auth_fieldRow">
|
||||||
{ phoneSection }
|
<input type="text" ref="username"
|
||||||
<input type="text" ref="username"
|
placeholder={placeholderUsername} defaultValue={this.props.defaultUsername}
|
||||||
placeholder={placeholderUsername} defaultValue={this.props.defaultUsername}
|
className={this._classForField(FIELD_USERNAME, 'mx_Login_field')}
|
||||||
className={this._classForField(FIELD_USERNAME, 'mx_Login_field')}
|
onBlur={function() {self.validateField(FIELD_USERNAME);}} />
|
||||||
onBlur={function() {self.validateField(FIELD_USERNAME);}} />
|
</div>
|
||||||
<br />
|
<div className="mx_Auth_fieldRow">
|
||||||
<input type="password" ref="password"
|
<input type="password" ref="password"
|
||||||
className={this._classForField(FIELD_PASSWORD, 'mx_Login_field')}
|
className={this._classForField(FIELD_PASSWORD, 'mx_Login_field')}
|
||||||
onBlur={function() {self.validateField(FIELD_PASSWORD);}}
|
onBlur={function() {self.validateField(FIELD_PASSWORD);}}
|
||||||
placeholder={_t("Password")} defaultValue={this.props.defaultPassword} />
|
placeholder={_t("Password")} defaultValue={this.props.defaultPassword} />
|
||||||
<br />
|
<input type="password" ref="passwordConfirm"
|
||||||
<input type="password" ref="passwordConfirm"
|
placeholder={_t("Confirm")}
|
||||||
placeholder={_t("Confirm password")}
|
className={this._classForField(FIELD_PASSWORD_CONFIRM, 'mx_Login_field')}
|
||||||
className={this._classForField(FIELD_PASSWORD_CONFIRM, 'mx_Login_field')}
|
onBlur={function() {self.validateField(FIELD_PASSWORD_CONFIRM);}}
|
||||||
onBlur={function() {self.validateField(FIELD_PASSWORD_CONFIRM);}}
|
defaultValue={this.props.defaultPassword} />
|
||||||
defaultValue={this.props.defaultPassword} />
|
</div>
|
||||||
<br />
|
<div className="mx_Auth_fieldRow">
|
||||||
|
{ emailSection }
|
||||||
|
{ phoneSection }
|
||||||
|
</div>
|
||||||
|
{_t(
|
||||||
|
"Use an email address to receover your account. Other users " +
|
||||||
|
"can invite you to rooms using your contact details.",
|
||||||
|
)}
|
||||||
{ registerButton }
|
{ registerButton }
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1210,8 +1210,13 @@
|
||||||
"Sign in with": "Sign in with",
|
"Sign in with": "Sign in with",
|
||||||
"Sign in": "Sign in",
|
"Sign in": "Sign in",
|
||||||
"If you don't specify an email address, you won't be able to reset your password. Are you sure?": "If you don't specify an email address, you won't be able to reset your password. Are you sure?",
|
"If you don't specify an email address, you won't be able to reset your password. Are you sure?": "If you don't specify an email address, you won't be able to reset your password. Are you sure?",
|
||||||
"Email address (optional)": "Email address (optional)",
|
"Create your account": "Create your account",
|
||||||
"Mobile phone number (optional)": "Mobile phone number (optional)",
|
"Create your %(serverName)s account": "Create your %(serverName)s account",
|
||||||
|
"Email": "Email",
|
||||||
|
"Email (optional)": "Email (optional)",
|
||||||
|
"Phone (optional)": "Phone (optional)",
|
||||||
|
"Confirm": "Confirm",
|
||||||
|
"Use an email address to receover your account. Other users can invite you to rooms using your contact details.": "Use an email address to receover your account. Other users can invite you to rooms using your contact details.",
|
||||||
"Other servers": "Other servers",
|
"Other servers": "Other servers",
|
||||||
"Enter custom server URLs <a>What does this mean?</a>": "Enter custom server URLs <a>What does this mean?</a>",
|
"Enter custom server URLs <a>What does this mean?</a>": "Enter custom server URLs <a>What does this mean?</a>",
|
||||||
"Homeserver URL": "Homeserver URL",
|
"Homeserver URL": "Homeserver URL",
|
||||||
|
@ -1372,7 +1377,6 @@
|
||||||
"Desktop specific": "Desktop specific",
|
"Desktop specific": "Desktop specific",
|
||||||
"Missing Media Permissions, click here to request.": "Missing Media Permissions, click here to request.",
|
"Missing Media Permissions, click here to request.": "Missing Media Permissions, click here to request.",
|
||||||
"VoIP": "VoIP",
|
"VoIP": "VoIP",
|
||||||
"Email": "Email",
|
|
||||||
"Add email address": "Add email address",
|
"Add email address": "Add email address",
|
||||||
"Display name": "Display name",
|
"Display name": "Display name",
|
||||||
"To return to your account in future you need to set a password": "To return to your account in future you need to set a password",
|
"To return to your account in future you need to set a password": "To return to your account in future you need to set a password",
|
||||||
|
@ -1426,7 +1430,6 @@
|
||||||
"A phone number is required to register on this homeserver.": "A phone number is required to register on this homeserver.",
|
"A phone number is required to register on this homeserver.": "A phone number is required to register on this homeserver.",
|
||||||
"You need to enter a username.": "You need to enter a username.",
|
"You need to enter a username.": "You need to enter a username.",
|
||||||
"An unknown error occurred.": "An unknown error occurred.",
|
"An unknown error occurred.": "An unknown error occurred.",
|
||||||
"Create your account": "Create your account",
|
|
||||||
"Commands": "Commands",
|
"Commands": "Commands",
|
||||||
"Results from DuckDuckGo": "Results from DuckDuckGo",
|
"Results from DuckDuckGo": "Results from DuckDuckGo",
|
||||||
"Emoji": "Emoji",
|
"Emoji": "Emoji",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue