You've already forked matrix-react-sdk
mirror of
https://github.com/matrix-org/matrix-react-sdk.git
synced 2025-07-31 13:44:28 +03:00
Revert "Support registration & login with phone number (#742)"
This reverts commit 0269562383
.
This breaks against the current synapse release. We need to think more
carefully about backwards compatibility.
This commit is contained in:
@ -58,22 +58,6 @@ export function unicodeToImage(str) {
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Given one or more unicode characters (represented by unicode
|
|
||||||
* character number), return an image node with the corresponding
|
|
||||||
* emoji.
|
|
||||||
*
|
|
||||||
* @param alt {string} String to use for the image alt text
|
|
||||||
* @param unicode {integer} One or more integers representing unicode characters
|
|
||||||
* @returns A img node with the corresponding emoji
|
|
||||||
*/
|
|
||||||
export function charactersToImageNode(alt, ...unicode) {
|
|
||||||
const fileName = unicode.map((u) => {
|
|
||||||
return u.toString(16);
|
|
||||||
}).join('-');
|
|
||||||
return <img alt={alt} src={`${emojione.imagePathSVG}${fileName}.svg${emojione.cacheBustParam}`}/>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function stripParagraphs(html: string): string {
|
export function stripParagraphs(html: string): string {
|
||||||
const contentDiv = document.createElement('div');
|
const contentDiv = document.createElement('div');
|
||||||
contentDiv.innerHTML = html;
|
contentDiv.innerHTML = html;
|
||||||
|
39
src/Login.js
39
src/Login.js
@ -105,38 +105,21 @@ export default class Login {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
loginViaPassword(username, phoneCountry, phoneNumber, pass) {
|
loginViaPassword(username, pass) {
|
||||||
const self = this;
|
var self = this;
|
||||||
|
var isEmail = username.indexOf("@") > 0;
|
||||||
const isEmail = username.indexOf("@") > 0;
|
var loginParams = {
|
||||||
|
|
||||||
let identifier;
|
|
||||||
if (phoneCountry && phoneNumber) {
|
|
||||||
identifier = {
|
|
||||||
type: 'm.id.phone',
|
|
||||||
country: phoneCountry,
|
|
||||||
number: phoneNumber,
|
|
||||||
};
|
|
||||||
} else if (isEmail) {
|
|
||||||
identifier = {
|
|
||||||
type: 'm.id.thirdparty',
|
|
||||||
medium: 'email',
|
|
||||||
address: username,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
identifier = {
|
|
||||||
type: 'm.id.user',
|
|
||||||
user: username,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const loginParams = {
|
|
||||||
password: pass,
|
password: pass,
|
||||||
identifier: identifier,
|
|
||||||
initial_device_display_name: this._defaultDeviceDisplayName,
|
initial_device_display_name: this._defaultDeviceDisplayName,
|
||||||
};
|
};
|
||||||
|
if (isEmail) {
|
||||||
|
loginParams.medium = 'email';
|
||||||
|
loginParams.address = username;
|
||||||
|
} else {
|
||||||
|
loginParams.user = username;
|
||||||
|
}
|
||||||
|
|
||||||
const client = this._createTemporaryClient();
|
var client = this._createTemporaryClient();
|
||||||
return client.login('m.login.password', loginParams).then(function(data) {
|
return client.login('m.login.password', loginParams).then(function(data) {
|
||||||
return q({
|
return q({
|
||||||
homeserverUrl: self._hsUrl,
|
homeserverUrl: self._hsUrl,
|
||||||
|
@ -109,8 +109,6 @@ import views$elements$DeviceVerifyButtons from './components/views/elements/Devi
|
|||||||
views$elements$DeviceVerifyButtons && (module.exports.components['views.elements.DeviceVerifyButtons'] = views$elements$DeviceVerifyButtons);
|
views$elements$DeviceVerifyButtons && (module.exports.components['views.elements.DeviceVerifyButtons'] = views$elements$DeviceVerifyButtons);
|
||||||
import views$elements$DirectorySearchBox from './components/views/elements/DirectorySearchBox';
|
import views$elements$DirectorySearchBox from './components/views/elements/DirectorySearchBox';
|
||||||
views$elements$DirectorySearchBox && (module.exports.components['views.elements.DirectorySearchBox'] = views$elements$DirectorySearchBox);
|
views$elements$DirectorySearchBox && (module.exports.components['views.elements.DirectorySearchBox'] = views$elements$DirectorySearchBox);
|
||||||
import views$elements$Dropdown from './components/views/elements/Dropdown';
|
|
||||||
views$elements$Dropdown && (module.exports.components['views.elements.Dropdown'] = views$elements$Dropdown);
|
|
||||||
import views$elements$EditableText from './components/views/elements/EditableText';
|
import views$elements$EditableText from './components/views/elements/EditableText';
|
||||||
views$elements$EditableText && (module.exports.components['views.elements.EditableText'] = views$elements$EditableText);
|
views$elements$EditableText && (module.exports.components['views.elements.EditableText'] = views$elements$EditableText);
|
||||||
import views$elements$EditableTextContainer from './components/views/elements/EditableTextContainer';
|
import views$elements$EditableTextContainer from './components/views/elements/EditableTextContainer';
|
||||||
@ -133,8 +131,6 @@ import views$login$CaptchaForm from './components/views/login/CaptchaForm';
|
|||||||
views$login$CaptchaForm && (module.exports.components['views.login.CaptchaForm'] = views$login$CaptchaForm);
|
views$login$CaptchaForm && (module.exports.components['views.login.CaptchaForm'] = views$login$CaptchaForm);
|
||||||
import views$login$CasLogin from './components/views/login/CasLogin';
|
import views$login$CasLogin from './components/views/login/CasLogin';
|
||||||
views$login$CasLogin && (module.exports.components['views.login.CasLogin'] = views$login$CasLogin);
|
views$login$CasLogin && (module.exports.components['views.login.CasLogin'] = views$login$CasLogin);
|
||||||
import views$login$CountryDropdown from './components/views/login/CountryDropdown';
|
|
||||||
views$login$CountryDropdown && (module.exports.components['views.login.CountryDropdown'] = views$login$CountryDropdown);
|
|
||||||
import views$login$CustomServerDialog from './components/views/login/CustomServerDialog';
|
import views$login$CustomServerDialog from './components/views/login/CustomServerDialog';
|
||||||
views$login$CustomServerDialog && (module.exports.components['views.login.CustomServerDialog'] = views$login$CustomServerDialog);
|
views$login$CustomServerDialog && (module.exports.components['views.login.CustomServerDialog'] = views$login$CustomServerDialog);
|
||||||
import views$login$InteractiveAuthEntryComponents from './components/views/login/InteractiveAuthEntryComponents';
|
import views$login$InteractiveAuthEntryComponents from './components/views/login/InteractiveAuthEntryComponents';
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
Copyright 2017 Vector Creations Ltd
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -65,10 +64,8 @@ module.exports = React.createClass({
|
|||||||
enteredHomeserverUrl: this.props.customHsUrl || this.props.defaultHsUrl,
|
enteredHomeserverUrl: this.props.customHsUrl || this.props.defaultHsUrl,
|
||||||
enteredIdentityServerUrl: this.props.customIsUrl || this.props.defaultIsUrl,
|
enteredIdentityServerUrl: this.props.customIsUrl || this.props.defaultIsUrl,
|
||||||
|
|
||||||
// used for preserving form values when changing homeserver
|
// used for preserving username when changing homeserver
|
||||||
username: "",
|
username: "",
|
||||||
phoneCountry: null,
|
|
||||||
phoneNumber: "",
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -76,21 +73,20 @@ module.exports = React.createClass({
|
|||||||
this._initLoginLogic();
|
this._initLoginLogic();
|
||||||
},
|
},
|
||||||
|
|
||||||
onPasswordLogin: function(username, phoneCountry, phoneNumber, password) {
|
onPasswordLogin: function(username, password) {
|
||||||
this.setState({
|
var self = this;
|
||||||
|
self.setState({
|
||||||
busy: true,
|
busy: true,
|
||||||
errorText: null,
|
errorText: null,
|
||||||
loginIncorrect: false,
|
loginIncorrect: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
this._loginLogic.loginViaPassword(
|
this._loginLogic.loginViaPassword(username, password).then(function(data) {
|
||||||
username, phoneCountry, phoneNumber, password,
|
self.props.onLoggedIn(data);
|
||||||
).then((data) => {
|
}, function(error) {
|
||||||
this.props.onLoggedIn(data);
|
self._setStateFromError(error, true);
|
||||||
}, (error) => {
|
}).finally(function() {
|
||||||
this._setStateFromError(error, true);
|
self.setState({
|
||||||
}).finally(() => {
|
|
||||||
this.setState({
|
|
||||||
busy: false
|
busy: false
|
||||||
});
|
});
|
||||||
}).done();
|
}).done();
|
||||||
@ -123,14 +119,6 @@ module.exports = React.createClass({
|
|||||||
this.setState({ username: username });
|
this.setState({ username: username });
|
||||||
},
|
},
|
||||||
|
|
||||||
onPhoneCountryChanged: function(phoneCountry) {
|
|
||||||
this.setState({ phoneCountry: phoneCountry });
|
|
||||||
},
|
|
||||||
|
|
||||||
onPhoneNumberChanged: function(phoneNumber) {
|
|
||||||
this.setState({ phoneNumber: phoneNumber });
|
|
||||||
},
|
|
||||||
|
|
||||||
onHsUrlChanged: function(newHsUrl) {
|
onHsUrlChanged: function(newHsUrl) {
|
||||||
var self = this;
|
var self = this;
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -237,11 +225,7 @@ module.exports = React.createClass({
|
|||||||
<PasswordLogin
|
<PasswordLogin
|
||||||
onSubmit={this.onPasswordLogin}
|
onSubmit={this.onPasswordLogin}
|
||||||
initialUsername={this.state.username}
|
initialUsername={this.state.username}
|
||||||
initialPhoneCountry={this.state.phoneCountry}
|
|
||||||
initialPhoneNumber={this.state.phoneNumber}
|
|
||||||
onUsernameChanged={this.onUsernameChanged}
|
onUsernameChanged={this.onUsernameChanged}
|
||||||
onPhoneCountryChanged={this.onPhoneCountryChanged}
|
|
||||||
onPhoneNumberChanged={this.onPhoneNumberChanged}
|
|
||||||
onForgotPasswordClick={this.props.onForgotPasswordClick}
|
onForgotPasswordClick={this.props.onForgotPasswordClick}
|
||||||
loginIncorrect={this.state.loginIncorrect}
|
loginIncorrect={this.state.loginIncorrect}
|
||||||
/>
|
/>
|
||||||
|
@ -262,9 +262,6 @@ module.exports = React.createClass({
|
|||||||
case "RegistrationForm.ERR_EMAIL_INVALID":
|
case "RegistrationForm.ERR_EMAIL_INVALID":
|
||||||
errMsg = "This doesn't look like a valid email address";
|
errMsg = "This doesn't look like a valid email address";
|
||||||
break;
|
break;
|
||||||
case "RegistrationForm.ERR_PHONE_NUMBER_INVALID":
|
|
||||||
errMsg = "This doesn't look like a valid phone number";
|
|
||||||
break;
|
|
||||||
case "RegistrationForm.ERR_USERNAME_INVALID":
|
case "RegistrationForm.ERR_USERNAME_INVALID":
|
||||||
errMsg = "User names may only contain letters, numbers, dots, hyphens and underscores.";
|
errMsg = "User names may only contain letters, numbers, dots, hyphens and underscores.";
|
||||||
break;
|
break;
|
||||||
@ -299,20 +296,15 @@ module.exports = React.createClass({
|
|||||||
guestAccessToken = null;
|
guestAccessToken = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
|
||||||
// session).
|
|
||||||
const bindThreepids = this.state.formVals.password ? {
|
|
||||||
email: true,
|
|
||||||
msisdn: true,
|
|
||||||
} : {};
|
|
||||||
|
|
||||||
return this._matrixClient.register(
|
return this._matrixClient.register(
|
||||||
this.state.formVals.username,
|
this.state.formVals.username,
|
||||||
this.state.formVals.password,
|
this.state.formVals.password,
|
||||||
undefined, // session id: included in the auth dict already
|
undefined, // session id: included in the auth dict already
|
||||||
auth,
|
auth,
|
||||||
bindThreepids,
|
// Only send the bind_email param if we're sending username / pw params
|
||||||
|
// (Since we need to send no params at all to use the ones saved in the
|
||||||
|
// session).
|
||||||
|
Boolean(this.state.formVals.username) || undefined,
|
||||||
guestAccessToken,
|
guestAccessToken,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -363,8 +355,6 @@ module.exports = React.createClass({
|
|||||||
<RegistrationForm
|
<RegistrationForm
|
||||||
defaultUsername={this.state.formVals.username}
|
defaultUsername={this.state.formVals.username}
|
||||||
defaultEmail={this.state.formVals.email}
|
defaultEmail={this.state.formVals.email}
|
||||||
defaultPhoneCountry={this.state.formVals.phoneCountry}
|
|
||||||
defaultPhoneNumber={this.state.formVals.phoneNumber}
|
|
||||||
defaultPassword={this.state.formVals.password}
|
defaultPassword={this.state.formVals.password}
|
||||||
teamsConfig={this.state.teamsConfig}
|
teamsConfig={this.state.teamsConfig}
|
||||||
guestUsername={guestUsername}
|
guestUsername={guestUsername}
|
||||||
|
@ -27,8 +27,8 @@ import React from 'react';
|
|||||||
export default function AccessibleButton(props) {
|
export default function AccessibleButton(props) {
|
||||||
const {element, onClick, children, ...restProps} = props;
|
const {element, onClick, children, ...restProps} = props;
|
||||||
restProps.onClick = onClick;
|
restProps.onClick = onClick;
|
||||||
restProps.onKeyUp = function(e) {
|
restProps.onKeyDown = function(e) {
|
||||||
if (e.keyCode == 13 || e.keyCode == 32) return onClick(e);
|
if (e.keyCode == 13 || e.keyCode == 32) return onClick();
|
||||||
};
|
};
|
||||||
restProps.tabIndex = restProps.tabIndex || "0";
|
restProps.tabIndex = restProps.tabIndex || "0";
|
||||||
restProps.role = "button";
|
restProps.role = "button";
|
||||||
|
@ -1,324 +0,0 @@
|
|||||||
/*
|
|
||||||
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 classnames from 'classnames';
|
|
||||||
import AccessibleButton from './AccessibleButton';
|
|
||||||
|
|
||||||
class MenuOption extends React.Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this._onMouseEnter = this._onMouseEnter.bind(this);
|
|
||||||
this._onClick = this._onClick.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onMouseEnter() {
|
|
||||||
this.props.onMouseEnter(this.props.dropdownKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onClick(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
this.props.onClick(this.props.dropdownKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const optClasses = classnames({
|
|
||||||
mx_Dropdown_option: true,
|
|
||||||
mx_Dropdown_option_highlight: this.props.highlighted,
|
|
||||||
});
|
|
||||||
|
|
||||||
return <div className={optClasses}
|
|
||||||
onClick={this._onClick} onKeyPress={this._onKeyPress}
|
|
||||||
onMouseEnter={this._onMouseEnter}
|
|
||||||
>
|
|
||||||
{this.props.children}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
MenuOption.propTypes = {
|
|
||||||
children: React.PropTypes.oneOfType([
|
|
||||||
React.PropTypes.arrayOf(React.PropTypes.node),
|
|
||||||
React.PropTypes.node
|
|
||||||
]),
|
|
||||||
highlighted: React.PropTypes.bool,
|
|
||||||
dropdownKey: React.PropTypes.string,
|
|
||||||
onClick: React.PropTypes.func.isRequired,
|
|
||||||
onMouseEnter: React.PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Reusable dropdown select control, akin to react-select,
|
|
||||||
* but somewhat simpler as react-select is 79KB of minified
|
|
||||||
* javascript.
|
|
||||||
*
|
|
||||||
* TODO: Port NetworkDropdown to use this.
|
|
||||||
*/
|
|
||||||
export default class Dropdown extends React.Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.dropdownRootElement = null;
|
|
||||||
this.ignoreEvent = null;
|
|
||||||
|
|
||||||
this._onInputClick = this._onInputClick.bind(this);
|
|
||||||
this._onRootClick = this._onRootClick.bind(this);
|
|
||||||
this._onDocumentClick = this._onDocumentClick.bind(this);
|
|
||||||
this._onMenuOptionClick = this._onMenuOptionClick.bind(this);
|
|
||||||
this._onInputKeyPress = this._onInputKeyPress.bind(this);
|
|
||||||
this._onInputKeyUp = this._onInputKeyUp.bind(this);
|
|
||||||
this._onInputChange = this._onInputChange.bind(this);
|
|
||||||
this._collectRoot = this._collectRoot.bind(this);
|
|
||||||
this._collectInputTextBox = this._collectInputTextBox.bind(this);
|
|
||||||
this._setHighlightedOption = this._setHighlightedOption.bind(this);
|
|
||||||
|
|
||||||
this.inputTextBox = null;
|
|
||||||
|
|
||||||
this._reindexChildren(this.props.children);
|
|
||||||
|
|
||||||
const firstChild = React.Children.toArray(props.children)[0];
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
// True if the menu is dropped-down
|
|
||||||
expanded: false,
|
|
||||||
// The key of the highlighted option
|
|
||||||
// (the option that would become selected if you pressed enter)
|
|
||||||
highlightedOption: firstChild ? firstChild.key : null,
|
|
||||||
// the current search query
|
|
||||||
searchQuery: '',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillMount() {
|
|
||||||
// Listen for all clicks on the document so we can close the
|
|
||||||
// menu when the user clicks somewhere else
|
|
||||||
document.addEventListener('click', this._onDocumentClick, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
document.removeEventListener('click', this._onDocumentClick, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
|
||||||
this._reindexChildren(nextProps.children);
|
|
||||||
const firstChild = React.Children.toArray(nextProps.children)[0];
|
|
||||||
this.setState({
|
|
||||||
highlightedOption: firstChild ? firstChild.key : null,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_reindexChildren(children) {
|
|
||||||
this.childrenByKey = {};
|
|
||||||
React.Children.forEach(children, (child) => {
|
|
||||||
this.childrenByKey[child.key] = child;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_onDocumentClick(ev) {
|
|
||||||
// Close the dropdown if the user clicks anywhere that isn't
|
|
||||||
// within our root element
|
|
||||||
if (ev !== this.ignoreEvent) {
|
|
||||||
this.setState({
|
|
||||||
expanded: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_onRootClick(ev) {
|
|
||||||
// This captures any clicks that happen within our elements,
|
|
||||||
// such that we can then ignore them when they're seen by the
|
|
||||||
// click listener on the document handler, ie. not close the
|
|
||||||
// dropdown immediately after opening it.
|
|
||||||
// NB. We can't just stopPropagation() because then the event
|
|
||||||
// doesn't reach the React onClick().
|
|
||||||
this.ignoreEvent = ev;
|
|
||||||
}
|
|
||||||
|
|
||||||
_onInputClick(ev) {
|
|
||||||
this.setState({
|
|
||||||
expanded: !this.state.expanded,
|
|
||||||
});
|
|
||||||
ev.preventDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
_onMenuOptionClick(dropdownKey) {
|
|
||||||
this.setState({
|
|
||||||
expanded: false,
|
|
||||||
});
|
|
||||||
this.props.onOptionChange(dropdownKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onInputKeyPress(e) {
|
|
||||||
// This needs to be on the keypress event because otherwise
|
|
||||||
// it can't cancel the form submission
|
|
||||||
if (e.key == 'Enter') {
|
|
||||||
this.setState({
|
|
||||||
expanded: false,
|
|
||||||
});
|
|
||||||
this.props.onOptionChange(this.state.highlightedOption);
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_onInputKeyUp(e) {
|
|
||||||
// These keys don't generate keypress events and so needs to
|
|
||||||
// be on keyup
|
|
||||||
if (e.key == 'Escape') {
|
|
||||||
this.setState({
|
|
||||||
expanded: false,
|
|
||||||
});
|
|
||||||
} else if (e.key == 'ArrowDown') {
|
|
||||||
this.setState({
|
|
||||||
highlightedOption: this._nextOption(this.state.highlightedOption),
|
|
||||||
});
|
|
||||||
} else if (e.key == 'ArrowUp') {
|
|
||||||
this.setState({
|
|
||||||
highlightedOption: this._prevOption(this.state.highlightedOption),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_onInputChange(e) {
|
|
||||||
this.setState({
|
|
||||||
searchQuery: e.target.value,
|
|
||||||
});
|
|
||||||
if (this.props.onSearchChange) {
|
|
||||||
this.props.onSearchChange(e.target.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_collectRoot(e) {
|
|
||||||
if (this.dropdownRootElement) {
|
|
||||||
this.dropdownRootElement.removeEventListener(
|
|
||||||
'click', this._onRootClick, false,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (e) {
|
|
||||||
e.addEventListener('click', this._onRootClick, false);
|
|
||||||
}
|
|
||||||
this.dropdownRootElement = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
_collectInputTextBox(e) {
|
|
||||||
this.inputTextBox = e;
|
|
||||||
if (e) e.focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
_setHighlightedOption(optionKey) {
|
|
||||||
this.setState({
|
|
||||||
highlightedOption: optionKey,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_nextOption(optionKey) {
|
|
||||||
const keys = Object.keys(this.childrenByKey);
|
|
||||||
const index = keys.indexOf(optionKey);
|
|
||||||
return keys[(index + 1) % keys.length];
|
|
||||||
}
|
|
||||||
|
|
||||||
_prevOption(optionKey) {
|
|
||||||
const keys = Object.keys(this.childrenByKey);
|
|
||||||
const index = keys.indexOf(optionKey);
|
|
||||||
return keys[(index - 1) % keys.length];
|
|
||||||
}
|
|
||||||
|
|
||||||
_getMenuOptions() {
|
|
||||||
const options = React.Children.map(this.props.children, (child) => {
|
|
||||||
return (
|
|
||||||
<MenuOption key={child.key} dropdownKey={child.key}
|
|
||||||
highlighted={this.state.highlightedOption == child.key}
|
|
||||||
onMouseEnter={this._setHighlightedOption}
|
|
||||||
onClick={this._onMenuOptionClick}
|
|
||||||
>
|
|
||||||
{child}
|
|
||||||
</MenuOption>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!this.state.searchQuery) {
|
|
||||||
options.push(
|
|
||||||
<div key="_searchprompt" className="mx_Dropdown_searchPrompt">
|
|
||||||
Type to search...
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
let currentValue;
|
|
||||||
|
|
||||||
const menuStyle = {};
|
|
||||||
if (this.props.menuWidth) menuStyle.width = this.props.menuWidth;
|
|
||||||
|
|
||||||
let menu;
|
|
||||||
if (this.state.expanded) {
|
|
||||||
currentValue = <input type="text" className="mx_Dropdown_option"
|
|
||||||
ref={this._collectInputTextBox} onKeyPress={this._onInputKeyPress}
|
|
||||||
onKeyUp={this._onInputKeyUp}
|
|
||||||
onChange={this._onInputChange}
|
|
||||||
value={this.state.searchQuery}
|
|
||||||
/>;
|
|
||||||
menu = <div className="mx_Dropdown_menu" style={menuStyle}>
|
|
||||||
{this._getMenuOptions()}
|
|
||||||
</div>;
|
|
||||||
} else {
|
|
||||||
const selectedChild = this.props.getShortOption ?
|
|
||||||
this.props.getShortOption(this.props.value) :
|
|
||||||
this.childrenByKey[this.props.value];
|
|
||||||
currentValue = <div className="mx_Dropdown_option">
|
|
||||||
{selectedChild}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
const dropdownClasses = {
|
|
||||||
mx_Dropdown: true,
|
|
||||||
};
|
|
||||||
if (this.props.className) {
|
|
||||||
dropdownClasses[this.props.className] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note the menu sits inside the AccessibleButton div so it's anchored
|
|
||||||
// to the input, but overflows below it. The root contains both.
|
|
||||||
return <div className={classnames(dropdownClasses)} ref={this._collectRoot}>
|
|
||||||
<AccessibleButton className="mx_Dropdown_input" onClick={this._onInputClick}>
|
|
||||||
{currentValue}
|
|
||||||
<span className="mx_Dropdown_arrow"></span>
|
|
||||||
{menu}
|
|
||||||
</AccessibleButton>
|
|
||||||
</div>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Dropdown.propTypes = {
|
|
||||||
// The width that the dropdown should be. If specified,
|
|
||||||
// the dropped-down part of the menu will be set to this
|
|
||||||
// width.
|
|
||||||
menuWidth: React.PropTypes.number,
|
|
||||||
// Called when the selected option changes
|
|
||||||
onOptionChange: React.PropTypes.func.isRequired,
|
|
||||||
// Called when the value of the search field changes
|
|
||||||
onSearchChange: React.PropTypes.func,
|
|
||||||
// Function that, given the key of an option, returns
|
|
||||||
// a node representing that option to be displayed in the
|
|
||||||
// box itself as the currently-selected option (ie. as
|
|
||||||
// opposed to in the actual dropped-down part). If
|
|
||||||
// unspecified, the appropriate child element is used as
|
|
||||||
// in the dropped-down menu.
|
|
||||||
getShortOption: React.PropTypes.func,
|
|
||||||
value: React.PropTypes.string,
|
|
||||||
}
|
|
@ -1,123 +0,0 @@
|
|||||||
/*
|
|
||||||
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 sdk from '../../../index';
|
|
||||||
|
|
||||||
import { COUNTRIES } from '../../../phonenumber';
|
|
||||||
import { charactersToImageNode } from '../../../HtmlUtils';
|
|
||||||
|
|
||||||
const COUNTRIES_BY_ISO2 = new Object(null);
|
|
||||||
for (const c of COUNTRIES) {
|
|
||||||
COUNTRIES_BY_ISO2[c.iso2] = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
function countryMatchesSearchQuery(query, country) {
|
|
||||||
if (country.name.toUpperCase().indexOf(query.toUpperCase()) == 0) return true;
|
|
||||||
if (country.iso2 == query.toUpperCase()) return true;
|
|
||||||
if (country.prefix == query) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const MAX_DISPLAYED_ROWS = 2;
|
|
||||||
|
|
||||||
export default class CountryDropdown extends React.Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this._onSearchChange = this._onSearchChange.bind(this);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
searchQuery: '',
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!props.value) {
|
|
||||||
// If no value is given, we start with the first
|
|
||||||
// country selected, but our parent component
|
|
||||||
// doesn't know this, therefore we do this.
|
|
||||||
this.props.onOptionChange(COUNTRIES[0].iso2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_onSearchChange(search) {
|
|
||||||
this.setState({
|
|
||||||
searchQuery: search,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_flagImgForIso2(iso2) {
|
|
||||||
// Unicode Regional Indicator Symbol letter 'A'
|
|
||||||
const RIS_A = 0x1F1E6;
|
|
||||||
const ASCII_A = 65;
|
|
||||||
return charactersToImageNode(iso2,
|
|
||||||
RIS_A + (iso2.charCodeAt(0) - ASCII_A),
|
|
||||||
RIS_A + (iso2.charCodeAt(1) - ASCII_A),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const Dropdown = sdk.getComponent('elements.Dropdown');
|
|
||||||
|
|
||||||
let displayedCountries;
|
|
||||||
if (this.state.searchQuery) {
|
|
||||||
displayedCountries = COUNTRIES.filter(
|
|
||||||
countryMatchesSearchQuery.bind(this, this.state.searchQuery),
|
|
||||||
);
|
|
||||||
if (
|
|
||||||
this.state.searchQuery.length == 2 &&
|
|
||||||
COUNTRIES_BY_ISO2[this.state.searchQuery.toUpperCase()]
|
|
||||||
) {
|
|
||||||
// exact ISO2 country name match: make the first result the matches ISO2
|
|
||||||
const matched = COUNTRIES_BY_ISO2[this.state.searchQuery.toUpperCase()];
|
|
||||||
displayedCountries = displayedCountries.filter((c) => {
|
|
||||||
return c.iso2 != matched.iso2;
|
|
||||||
});
|
|
||||||
displayedCountries.unshift(matched);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
displayedCountries = COUNTRIES;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (displayedCountries.length > MAX_DISPLAYED_ROWS) {
|
|
||||||
displayedCountries = displayedCountries.slice(0, MAX_DISPLAYED_ROWS);
|
|
||||||
}
|
|
||||||
|
|
||||||
const options = displayedCountries.map((country) => {
|
|
||||||
return <div key={country.iso2}>
|
|
||||||
{this._flagImgForIso2(country.iso2)}
|
|
||||||
{country.name}
|
|
||||||
</div>;
|
|
||||||
});
|
|
||||||
|
|
||||||
// default value here too, otherwise we need to handle null / undefined
|
|
||||||
// values between mounting and the initial value propgating
|
|
||||||
const value = this.props.value || COUNTRIES[0].iso2;
|
|
||||||
|
|
||||||
return <Dropdown className={this.props.className}
|
|
||||||
onOptionChange={this.props.onOptionChange} onSearchChange={this._onSearchChange}
|
|
||||||
menuWidth={298} getShortOption={this._flagImgForIso2}
|
|
||||||
value={value}
|
|
||||||
>
|
|
||||||
{options}
|
|
||||||
</Dropdown>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CountryDropdown.propTypes = {
|
|
||||||
className: React.PropTypes.string,
|
|
||||||
onOptionChange: React.PropTypes.func.isRequired,
|
|
||||||
value: React.PropTypes.string,
|
|
||||||
};
|
|
@ -16,8 +16,6 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import url from 'url';
|
|
||||||
import classnames from 'classnames';
|
|
||||||
|
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
|
|
||||||
@ -257,137 +255,6 @@ export const EmailIdentityAuthEntry = React.createClass({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const MsisdnAuthEntry = React.createClass({
|
|
||||||
displayName: 'MsisdnAuthEntry',
|
|
||||||
|
|
||||||
statics: {
|
|
||||||
LOGIN_TYPE: "m.login.msisdn",
|
|
||||||
},
|
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
inputs: React.PropTypes.shape({
|
|
||||||
phoneCountry: React.PropTypes.string,
|
|
||||||
phoneNumber: React.PropTypes.string,
|
|
||||||
}),
|
|
||||||
fail: React.PropTypes.func,
|
|
||||||
clientSecret: React.PropTypes.func,
|
|
||||||
submitAuthDict: React.PropTypes.func.isRequired,
|
|
||||||
matrixClient: React.PropTypes.object,
|
|
||||||
submitAuthDict: React.PropTypes.func,
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState: function() {
|
|
||||||
return {
|
|
||||||
token: '',
|
|
||||||
requestingToken: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentWillMount: function() {
|
|
||||||
this._sid = null;
|
|
||||||
this._msisdn = null;
|
|
||||||
this._tokenBox = null;
|
|
||||||
|
|
||||||
this.setState({requestingToken: true});
|
|
||||||
this._requestMsisdnToken().catch((e) => {
|
|
||||||
this.props.fail(e);
|
|
||||||
}).finally(() => {
|
|
||||||
this.setState({requestingToken: false});
|
|
||||||
}).done();
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Requests a verification token by SMS.
|
|
||||||
*/
|
|
||||||
_requestMsisdnToken: function() {
|
|
||||||
return this.props.matrixClient.requestRegisterMsisdnToken(
|
|
||||||
this.props.inputs.phoneCountry,
|
|
||||||
this.props.inputs.phoneNumber,
|
|
||||||
this.props.clientSecret,
|
|
||||||
1, // TODO: Multiple send attempts?
|
|
||||||
).then((result) => {
|
|
||||||
this._sid = result.sid;
|
|
||||||
this._msisdn = result.msisdn;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_onTokenChange: function(e) {
|
|
||||||
this.setState({
|
|
||||||
token: e.target.value,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_onFormSubmit: function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
if (this.state.token == '') return;
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
errorText: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.props.matrixClient.submitMsisdnToken(
|
|
||||||
this._sid, this.props.clientSecret, this.state.token
|
|
||||||
).then((result) => {
|
|
||||||
if (result.success) {
|
|
||||||
const idServerParsedUrl = url.parse(
|
|
||||||
this.props.matrixClient.getIdentityServerUrl(),
|
|
||||||
)
|
|
||||||
this.props.submitAuthDict({
|
|
||||||
type: MsisdnAuthEntry.LOGIN_TYPE,
|
|
||||||
threepid_creds: {
|
|
||||||
sid: this._sid,
|
|
||||||
client_secret: this.props.clientSecret,
|
|
||||||
id_server: idServerParsedUrl.host,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.setState({
|
|
||||||
errorText: "Token incorrect",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}).catch((e) => {
|
|
||||||
this.props.fail(e);
|
|
||||||
console.log("Failed to submit msisdn token");
|
|
||||||
}).done();
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
if (this.state.requestingToken) {
|
|
||||||
const Loader = sdk.getComponent("elements.Spinner");
|
|
||||||
return <Loader />;
|
|
||||||
} else {
|
|
||||||
const enableSubmit = Boolean(this.state.token);
|
|
||||||
const submitClasses = classnames({
|
|
||||||
mx_InteractiveAuthEntryComponents_msisdnSubmit: true,
|
|
||||||
mx_UserSettings_button: true, // XXX button classes
|
|
||||||
});
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<p>A text message has been sent to +<i>{this._msisdn}</i></p>
|
|
||||||
<p>Please enter the code it contains:</p>
|
|
||||||
<div className="mx_InteractiveAuthEntryComponents_msisdnWrapper">
|
|
||||||
<form onSubmit={this._onFormSubmit}>
|
|
||||||
<input type="text"
|
|
||||||
className="mx_InteractiveAuthEntryComponents_msisdnEntry"
|
|
||||||
value={this.state.token}
|
|
||||||
onChange={this._onTokenChange}
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<input type="submit" value="Submit"
|
|
||||||
className={submitClasses}
|
|
||||||
disabled={!enableSubmit}
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
<div className="error">
|
|
||||||
{this.state.errorText}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const FallbackAuthEntry = React.createClass({
|
export const FallbackAuthEntry = React.createClass({
|
||||||
displayName: 'FallbackAuthEntry',
|
displayName: 'FallbackAuthEntry',
|
||||||
|
|
||||||
@ -446,7 +313,6 @@ const AuthEntryComponents = [
|
|||||||
PasswordAuthEntry,
|
PasswordAuthEntry,
|
||||||
RecaptchaAuthEntry,
|
RecaptchaAuthEntry,
|
||||||
EmailIdentityAuthEntry,
|
EmailIdentityAuthEntry,
|
||||||
MsisdnAuthEntry,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export function getEntryComponentForLoginType(loginType) {
|
export function getEntryComponentForLoginType(loginType) {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
Copyright 2017 Vector Creations Ltd
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -18,7 +17,6 @@ limitations under the License.
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import sdk from '../../../index';
|
|
||||||
import {field_input_incorrect} from '../../../UiEffects';
|
import {field_input_incorrect} from '../../../UiEffects';
|
||||||
|
|
||||||
|
|
||||||
@ -30,12 +28,8 @@ module.exports = React.createClass({displayName: 'PasswordLogin',
|
|||||||
onSubmit: React.PropTypes.func.isRequired, // fn(username, password)
|
onSubmit: React.PropTypes.func.isRequired, // fn(username, password)
|
||||||
onForgotPasswordClick: React.PropTypes.func, // fn()
|
onForgotPasswordClick: React.PropTypes.func, // fn()
|
||||||
initialUsername: React.PropTypes.string,
|
initialUsername: React.PropTypes.string,
|
||||||
initialPhoneCountry: React.PropTypes.string,
|
|
||||||
initialPhoneNumber: React.PropTypes.string,
|
|
||||||
initialPassword: React.PropTypes.string,
|
initialPassword: React.PropTypes.string,
|
||||||
onUsernameChanged: React.PropTypes.func,
|
onUsernameChanged: React.PropTypes.func,
|
||||||
onPhoneCountryChanged: React.PropTypes.func,
|
|
||||||
onPhoneNumberChanged: React.PropTypes.func,
|
|
||||||
onPasswordChanged: React.PropTypes.func,
|
onPasswordChanged: React.PropTypes.func,
|
||||||
loginIncorrect: React.PropTypes.bool,
|
loginIncorrect: React.PropTypes.bool,
|
||||||
},
|
},
|
||||||
@ -44,11 +38,7 @@ module.exports = React.createClass({displayName: 'PasswordLogin',
|
|||||||
return {
|
return {
|
||||||
onUsernameChanged: function() {},
|
onUsernameChanged: function() {},
|
||||||
onPasswordChanged: function() {},
|
onPasswordChanged: function() {},
|
||||||
onPhoneCountryChanged: function() {},
|
|
||||||
onPhoneNumberChanged: function() {},
|
|
||||||
initialUsername: "",
|
initialUsername: "",
|
||||||
initialPhoneCountry: "",
|
|
||||||
initialPhoneNumber: "",
|
|
||||||
initialPassword: "",
|
initialPassword: "",
|
||||||
loginIncorrect: false,
|
loginIncorrect: false,
|
||||||
};
|
};
|
||||||
@ -58,8 +48,6 @@ module.exports = React.createClass({displayName: 'PasswordLogin',
|
|||||||
return {
|
return {
|
||||||
username: this.props.initialUsername,
|
username: this.props.initialUsername,
|
||||||
password: this.props.initialPassword,
|
password: this.props.initialPassword,
|
||||||
phoneCountry: this.props.initialPhoneCountry,
|
|
||||||
phoneNumber: this.props.initialPhoneNumber,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -75,12 +63,7 @@ module.exports = React.createClass({displayName: 'PasswordLogin',
|
|||||||
|
|
||||||
onSubmitForm: function(ev) {
|
onSubmitForm: function(ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
this.props.onSubmit(
|
this.props.onSubmit(this.state.username, this.state.password);
|
||||||
this.state.username,
|
|
||||||
this.state.phoneCountry,
|
|
||||||
this.state.phoneNumber,
|
|
||||||
this.state.password,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onUsernameChanged: function(ev) {
|
onUsernameChanged: function(ev) {
|
||||||
@ -88,16 +71,6 @@ module.exports = React.createClass({displayName: 'PasswordLogin',
|
|||||||
this.props.onUsernameChanged(ev.target.value);
|
this.props.onUsernameChanged(ev.target.value);
|
||||||
},
|
},
|
||||||
|
|
||||||
onPhoneCountryChanged: function(country) {
|
|
||||||
this.setState({phoneCountry: country});
|
|
||||||
this.props.onPhoneCountryChanged(country);
|
|
||||||
},
|
|
||||||
|
|
||||||
onPhoneNumberChanged: function(ev) {
|
|
||||||
this.setState({phoneNumber: ev.target.value});
|
|
||||||
this.props.onPhoneNumberChanged(ev.target.value);
|
|
||||||
},
|
|
||||||
|
|
||||||
onPasswordChanged: function(ev) {
|
onPasswordChanged: function(ev) {
|
||||||
this.setState({password: ev.target.value});
|
this.setState({password: ev.target.value});
|
||||||
this.props.onPasswordChanged(ev.target.value);
|
this.props.onPasswordChanged(ev.target.value);
|
||||||
@ -119,28 +92,13 @@ module.exports = React.createClass({displayName: 'PasswordLogin',
|
|||||||
error: this.props.loginIncorrect,
|
error: this.props.loginIncorrect,
|
||||||
});
|
});
|
||||||
|
|
||||||
const CountryDropdown = sdk.getComponent('views.login.CountryDropdown');
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<form onSubmit={this.onSubmitForm}>
|
<form onSubmit={this.onSubmitForm}>
|
||||||
<input className="mx_Login_field mx_Login_username" type="text"
|
<input className="mx_Login_field" type="text"
|
||||||
name="username" // make it a little easier for browser's remember-password
|
name="username" // make it a little easier for browser's remember-password
|
||||||
value={this.state.username} onChange={this.onUsernameChanged}
|
value={this.state.username} onChange={this.onUsernameChanged}
|
||||||
placeholder="Email or user name" autoFocus />
|
placeholder="Email or user name" autoFocus />
|
||||||
or
|
|
||||||
<div className="mx_Login_phoneSection">
|
|
||||||
<CountryDropdown ref="phone_country" onOptionChange={this.onPhoneCountryChanged}
|
|
||||||
className="mx_Login_phoneCountry"
|
|
||||||
value={this.state.phoneCountry}
|
|
||||||
/>
|
|
||||||
<input type="text" ref="phoneNumber"
|
|
||||||
onChange={this.onPhoneNumberChanged}
|
|
||||||
placeholder="Mobile phone number"
|
|
||||||
className="mx_Login_phoneNumberField mx_Login_field"
|
|
||||||
value={this.state.phoneNumber}
|
|
||||||
name="phoneNumber"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<br />
|
<br />
|
||||||
<input className={pwFieldClass} ref={(e) => {this._passwordField = e;}} type="password"
|
<input className={pwFieldClass} ref={(e) => {this._passwordField = e;}} type="password"
|
||||||
name="password"
|
name="password"
|
||||||
|
@ -19,12 +19,9 @@ import React from 'react';
|
|||||||
import { field_input_incorrect } from '../../../UiEffects';
|
import { field_input_incorrect } from '../../../UiEffects';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import Email from '../../../email';
|
import Email from '../../../email';
|
||||||
import { looksValid as phoneNumberLooksValid } from '../../../phonenumber';
|
|
||||||
import Modal from '../../../Modal';
|
import Modal from '../../../Modal';
|
||||||
|
|
||||||
const FIELD_EMAIL = 'field_email';
|
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_USERNAME = 'field_username';
|
||||||
const FIELD_PASSWORD = 'field_password';
|
const FIELD_PASSWORD = 'field_password';
|
||||||
const FIELD_PASSWORD_CONFIRM = 'field_password_confirm';
|
const FIELD_PASSWORD_CONFIRM = 'field_password_confirm';
|
||||||
@ -38,8 +35,6 @@ module.exports = React.createClass({
|
|||||||
propTypes: {
|
propTypes: {
|
||||||
// Values pre-filled in the input boxes when the component loads
|
// Values pre-filled in the input boxes when the component loads
|
||||||
defaultEmail: React.PropTypes.string,
|
defaultEmail: React.PropTypes.string,
|
||||||
defaultPhoneCountry: React.PropTypes.string,
|
|
||||||
defaultPhoneNumber: React.PropTypes.string,
|
|
||||||
defaultUsername: React.PropTypes.string,
|
defaultUsername: React.PropTypes.string,
|
||||||
defaultPassword: React.PropTypes.string,
|
defaultPassword: React.PropTypes.string,
|
||||||
teamsConfig: React.PropTypes.shape({
|
teamsConfig: React.PropTypes.shape({
|
||||||
@ -76,8 +71,6 @@ module.exports = React.createClass({
|
|||||||
return {
|
return {
|
||||||
fieldValid: {},
|
fieldValid: {},
|
||||||
selectedTeam: null,
|
selectedTeam: null,
|
||||||
// The ISO2 country code selected in the phone number entry
|
|
||||||
phoneCountry: this.props.defaultPhoneCountry,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -92,7 +85,6 @@ module.exports = React.createClass({
|
|||||||
this.validateField(FIELD_PASSWORD_CONFIRM);
|
this.validateField(FIELD_PASSWORD_CONFIRM);
|
||||||
this.validateField(FIELD_PASSWORD);
|
this.validateField(FIELD_PASSWORD);
|
||||||
this.validateField(FIELD_USERNAME);
|
this.validateField(FIELD_USERNAME);
|
||||||
this.validateField(FIELD_PHONE_NUMBER);
|
|
||||||
this.validateField(FIELD_EMAIL);
|
this.validateField(FIELD_EMAIL);
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
@ -126,8 +118,6 @@ module.exports = React.createClass({
|
|||||||
username: this.refs.username.value.trim() || this.props.guestUsername,
|
username: this.refs.username.value.trim() || this.props.guestUsername,
|
||||||
password: this.refs.password.value.trim(),
|
password: this.refs.password.value.trim(),
|
||||||
email: email,
|
email: email,
|
||||||
phoneCountry: this.state.phoneCountry,
|
|
||||||
phoneNumber: this.refs.phoneNumber.value.trim(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (promise) {
|
if (promise) {
|
||||||
@ -184,11 +174,6 @@ module.exports = React.createClass({
|
|||||||
const emailValid = email === '' || Email.looksValid(email);
|
const emailValid = email === '' || Email.looksValid(email);
|
||||||
this.markFieldValid(field_id, emailValid, "RegistrationForm.ERR_EMAIL_INVALID");
|
this.markFieldValid(field_id, emailValid, "RegistrationForm.ERR_EMAIL_INVALID");
|
||||||
break;
|
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:
|
case FIELD_USERNAME:
|
||||||
// XXX: SPEC-1
|
// XXX: SPEC-1
|
||||||
var username = this.refs.username.value.trim() || this.props.guestUsername;
|
var username = this.refs.username.value.trim() || this.props.guestUsername;
|
||||||
@ -248,8 +233,6 @@ module.exports = React.createClass({
|
|||||||
switch (field_id) {
|
switch (field_id) {
|
||||||
case FIELD_EMAIL:
|
case FIELD_EMAIL:
|
||||||
return this.refs.email;
|
return this.refs.email;
|
||||||
case FIELD_PHONE_NUMBER:
|
|
||||||
return this.refs.phoneNumber;
|
|
||||||
case FIELD_USERNAME:
|
case FIELD_USERNAME:
|
||||||
return this.refs.username;
|
return this.refs.username;
|
||||||
case FIELD_PASSWORD:
|
case FIELD_PASSWORD:
|
||||||
@ -268,12 +251,6 @@ module.exports = React.createClass({
|
|||||||
return cls;
|
return cls;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onPhoneCountryChange(newVal) {
|
|
||||||
this.setState({
|
|
||||||
phoneCountry: newVal,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
@ -309,25 +286,6 @@ module.exports = React.createClass({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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"
|
|
||||||
value={this.state.phoneCountry}
|
|
||||||
/>
|
|
||||||
<input type="text" ref="phoneNumber"
|
|
||||||
placeholder="Mobile phone number (optional)"
|
|
||||||
defaultValue={this.props.defaultPhoneNumber}
|
|
||||||
className={this._classForField(
|
|
||||||
FIELD_PHONE_NUMBER, 'mx_Login_phoneNumberField', 'mx_Login_field'
|
|
||||||
)}
|
|
||||||
onBlur={function() {self.validateField(FIELD_PHONE_NUMBER);}}
|
|
||||||
value={self.state.phoneNumber}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const registerButton = (
|
const registerButton = (
|
||||||
<input className="mx_Login_submit" type="submit" value="Register" />
|
<input className="mx_Login_submit" type="submit" value="Register" />
|
||||||
);
|
);
|
||||||
@ -342,7 +300,6 @@ module.exports = React.createClass({
|
|||||||
<form onSubmit={this.onSubmit}>
|
<form onSubmit={this.onSubmit}>
|
||||||
{emailSection}
|
{emailSection}
|
||||||
{belowEmailSection}
|
{belowEmailSection}
|
||||||
{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')}
|
||||||
|
1273
src/phonenumber.js
1273
src/phonenumber.js
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user