@@ -313,7 +313,7 @@ module.exports = React.createClass({
{ emailSection }
{ phoneSection }
diff --git a/src/components/views/auth/ServerConfig.js b/src/components/views/auth/ServerConfig.js
index f20695e9e2..fb35104e49 100644
--- a/src/components/views/auth/ServerConfig.js
+++ b/src/components/views/auth/ServerConfig.js
@@ -1,5 +1,6 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
+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.
@@ -14,21 +15,18 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-'use strict';
-
-const React = require('react');
+import React from 'react';
import PropTypes from 'prop-types';
-const Modal = require('../../../Modal');
-const sdk = require('../../../index');
+import Modal from '../../../Modal';
+import sdk from '../../../index';
import { _t } from '../../../languageHandler';
-/**
+/*
* A pure UI component which displays the HS and IS to use.
*/
-module.exports = React.createClass({
- displayName: 'ServerConfig',
- propTypes: {
+export default class ServerConfig extends React.PureComponent {
+ static propTypes = {
onServerConfigChange: PropTypes.func,
// default URLs are defined in config.json (or the hardcoded defaults)
@@ -45,155 +43,103 @@ module.exports = React.createClass({
customHsUrl: PropTypes.string,
customIsUrl: PropTypes.string,
- withToggleButton: PropTypes.bool,
delayTimeMs: PropTypes.number, // time to wait before invoking onChanged
- },
+ }
- getDefaultProps: function() {
- return {
- onServerConfigChange: function() {},
- customHsUrl: "",
- customIsUrl: "",
- withToggleButton: false,
- delayTimeMs: 0,
+ static defaultProps = {
+ onServerConfigChange: function() {},
+ customHsUrl: "",
+ customIsUrl: "",
+ delayTimeMs: 0,
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ hsUrl: props.customHsUrl,
+ isUrl: props.customIsUrl,
};
- },
+ }
- getInitialState: function() {
- return {
- hs_url: this.props.customHsUrl,
- is_url: this.props.customIsUrl,
- // if withToggleButton is false, then show the config all the time given we have no way otherwise of making it visible
- configVisible: !this.props.withToggleButton ||
- (this.props.customHsUrl !== this.props.defaultHsUrl) ||
- (this.props.customIsUrl !== this.props.defaultIsUrl),
- };
- },
-
- componentWillReceiveProps: function(newProps) {
- if (newProps.customHsUrl === this.state.hs_url &&
- newProps.customIsUrl === this.state.is_url) return;
+ componentWillReceiveProps(newProps) {
+ if (newProps.customHsUrl === this.state.hsUrl &&
+ newProps.customIsUrl === this.state.isUrl) return;
this.setState({
- hs_url: newProps.customHsUrl,
- is_url: newProps.customIsUrl,
- configVisible: !newProps.withToggleButton ||
- (newProps.customHsUrl !== newProps.defaultHsUrl) ||
- (newProps.customIsUrl !== newProps.defaultIsUrl),
+ hsUrl: newProps.customHsUrl,
+ isUrl: newProps.customIsUrl,
});
this.props.onServerConfigChange({
hsUrl: newProps.customHsUrl,
isUrl: newProps.customIsUrl,
});
- },
+ }
- onHomeserverChanged: function(ev) {
- this.setState({hs_url: ev.target.value}, () => {
+ onHomeserverChanged = (ev) => {
+ this.setState({hsUrl: ev.target.value}, () => {
this._hsTimeoutId = this._waitThenInvoke(this._hsTimeoutId, () => {
- let hsUrl = this.state.hs_url.trim().replace(/\/$/, "");
+ let hsUrl = this.state.hsUrl.trim().replace(/\/$/, "");
if (hsUrl === "") hsUrl = this.props.defaultHsUrl;
this.props.onServerConfigChange({
- hsUrl: this.state.hs_url,
- isUrl: this.state.is_url,
+ hsUrl: this.state.hsUrl,
+ isUrl: this.state.isUrl,
});
});
});
- },
+ }
- onIdentityServerChanged: function(ev) {
- this.setState({is_url: ev.target.value}, () => {
+ onIdentityServerChanged = (ev) => {
+ this.setState({isUrl: ev.target.value}, () => {
this._isTimeoutId = this._waitThenInvoke(this._isTimeoutId, () => {
- let isUrl = this.state.is_url.trim().replace(/\/$/, "");
+ let isUrl = this.state.isUrl.trim().replace(/\/$/, "");
if (isUrl === "") isUrl = this.props.defaultIsUrl;
this.props.onServerConfigChange({
- hsUrl: this.state.hs_url,
- isUrl: this.state.is_url,
+ hsUrl: this.state.hsUrl,
+ isUrl: this.state.isUrl,
});
});
});
- },
+ }
- _waitThenInvoke: function(existingTimeoutId, fn) {
+ _waitThenInvoke(existingTimeoutId, fn) {
if (existingTimeoutId) {
clearTimeout(existingTimeoutId);
}
return setTimeout(fn.bind(this), this.props.delayTimeMs);
- },
+ }
- onServerConfigVisibleChange: function(visible, ev) {
- this.setState({
- configVisible: visible,
- });
- if (!visible) {
- this.props.onServerConfigChange({
- hsUrl: this.props.defaultHsUrl,
- isUrl: this.props.defaultIsUrl,
- });
- } else {
- this.props.onServerConfigChange({
- hsUrl: this.state.hs_url,
- isUrl: this.state.is_url,
- });
- }
- },
-
- showHelpPopup: function() {
+ showHelpPopup = () => {
const CustomServerDialog = sdk.getComponent('auth.CustomServerDialog');
Modal.createTrackedDialog('Custom Server Dialog', '', CustomServerDialog);
- },
+ }
- render: function() {
- const serverConfigStyle = {};
- serverConfigStyle.display = this.state.configVisible ? 'block' : 'none';
-
- let toggleButton;
- if (this.props.withToggleButton) {
- toggleButton = (
-
- );
- }
+ render() {
+ const Field = sdk.getComponent('elements.Field');
return (
-
- { toggleButton }
-
);
- },
-});
+ }
+}
diff --git a/src/components/views/auth/ServerTypeSelector.js b/src/components/views/auth/ServerTypeSelector.js
new file mode 100644
index 0000000000..de76e6acf9
--- /dev/null
+++ b/src/components/views/auth/ServerTypeSelector.js
@@ -0,0 +1,148 @@
+/*
+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.
+*/
+
+import React from 'react';
+import PropTypes from 'prop-types';
+import { _t } from '../../../languageHandler';
+import sdk from '../../../index';
+import classnames from 'classnames';
+
+const MODULAR_URL = 'https://modular.im/?utm_source=riot-web&utm_medium=web&utm_campaign=riot-web-authentication';
+
+export const FREE = 'Free';
+export const PREMIUM = 'Premium';
+export const ADVANCED = 'Advanced';
+
+export const TYPES = {
+ FREE: {
+ id: FREE,
+ label: () => _t('Free'),
+ logo: () =>
,
+ description: () => _t('Join millions for free on the largest public server'),
+ hsUrl: 'https://matrix.org',
+ isUrl: 'https://vector.im',
+ },
+ PREMIUM: {
+ id: PREMIUM,
+ label: () => _t('Premium'),
+ logo: () =>
,
+ description: () => _t('Premium hosting for organisations
Learn more', {}, {
+ a: sub =>
+ {sub}
+ ,
+ }),
+ },
+ ADVANCED: {
+ id: ADVANCED,
+ label: () => _t('Advanced'),
+ logo: () =>
+
+ {_t('Other')}
+
,
+ description: () => _t('Find other public servers or use a custom server'),
+ },
+};
+
+function getDefaultType(defaultHsUrl) {
+ if (!defaultHsUrl) {
+ return null;
+ } else if (defaultHsUrl === TYPES.FREE.hsUrl) {
+ return FREE;
+ } else if (new URL(defaultHsUrl).hostname.endsWith('.modular.im')) {
+ // TODO: Use a Riot config parameter to detect Modular-ness.
+ // https://github.com/vector-im/riot-web/issues/8253
+ return PREMIUM;
+ } else {
+ return ADVANCED;
+ }
+}
+
+export default class ServerTypeSelector extends React.PureComponent {
+ static propTypes = {
+ // The default HS URL as another way to set the initially selected type.
+ defaultHsUrl: PropTypes.string,
+ // Handler called when the selected type changes.
+ onChange: PropTypes.func.isRequired,
+ }
+
+ constructor(props) {
+ super(props);
+
+ const {
+ defaultHsUrl,
+ onChange,
+ } = props;
+ const type = getDefaultType(defaultHsUrl);
+ this.state = {
+ selected: type,
+ };
+ if (onChange) {
+ onChange(type);
+ }
+ }
+
+ updateSelectedType(type) {
+ if (this.state.selected === type) {
+ return;
+ }
+ this.setState({
+ selected: type,
+ });
+ if (this.props.onChange) {
+ this.props.onChange(type);
+ }
+ }
+
+ onClick = (e) => {
+ e.stopPropagation();
+ const type = e.currentTarget.dataset.id;
+ this.updateSelectedType(type);
+ }
+
+ render() {
+ const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
+
+ const serverTypes = [];
+ for (const type of Object.values(TYPES)) {
+ const { id, label, logo, description } = type;
+ const classes = classnames(
+ "mx_ServerTypeSelector_type",
+ `mx_ServerTypeSelector_type_${id}`,
+ {
+ "mx_ServerTypeSelector_type_selected": id === this.state.selected,
+ },
+ );
+
+ serverTypes.push(
+
+ {label()}
+
+
+
+ {logo()}
+
+
+ {description()}
+
+
+
);
+ }
+
+ return
+ {serverTypes}
+
;
+ }
+}
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index f04c62cbbb..4d67c33dad 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -4,7 +4,7 @@
"Failed to verify email address: make sure you clicked the link in the email": "Failed to verify email address: make sure you clicked the link in the email",
"The platform you're on": "The platform you're on",
"The version of Riot.im": "The version of Riot.im",
- "Whether or not you're logged in (we don't record your user name)": "Whether or not you're logged in (we don't record your user name)",
+ "Whether or not you're logged in (we don't record your username)": "Whether or not you're logged in (we don't record your username)",
"Your language of choice": "Your language of choice",
"Which officially provided instance you are using, if any": "Which officially provided instance you are using, if any",
"Whether or not you're using the Richtext mode of the Rich Text Editor": "Whether or not you're using the Richtext mode of the Rich Text Editor",
@@ -305,7 +305,7 @@
"Uploading report": "Uploading report",
"Waiting for response from server": "Waiting for response from server",
"Messages containing my display name": "Messages containing my display name",
- "Messages containing my user name": "Messages containing my user name",
+ "Messages containing my username": "Messages containing my username",
"Messages containing @room": "Messages containing @room",
"Messages in one-to-one chats": "Messages in one-to-one chats",
"Encrypted messages in one-to-one chats": "Encrypted messages in one-to-one chats",
@@ -1165,9 +1165,8 @@
"Robot check is currently unavailable on desktop - please use a
web browser": "Robot check is currently unavailable on desktop - please use a
web browser",
"This Home Server would like to make sure you are not a robot": "This Home Server would like to make sure you are not a robot",
"Custom Server Options": "Custom Server Options",
- "You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.": "You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.",
- "This allows you to use this app with an existing Matrix account on a different home server.": "This allows you to use this app with an existing Matrix account on a different home server.",
- "You can also set a custom identity server but this will typically prevent interaction with users based on email address.": "You can also set a custom identity server but this will typically prevent interaction with users based on email address.",
+ "You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use this app with an existing Matrix account on a different homeserver.": "You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use this app with an existing Matrix account on a different homeserver.",
+ "You can also set a custom identity server, but you won't be able to invite users by email address, or be invited by email address yourself.": "You can also set a custom identity server, but you won't be able to invite users by email address, or be invited by email address yourself.",
"To continue, please enter your password.": "To continue, please enter your password.",
"Password:": "Password:",
"Please review and accept all of the homeserver's policies": "Please review and accept all of the homeserver's policies",
@@ -1179,25 +1178,34 @@
"Please enter the code it contains:": "Please enter the code it contains:",
"Code": "Code",
"Start authentication": "Start authentication",
+ "Your Modular server": "Your Modular server",
+ "Enter the location of your Modular homeserver. It may use your own domain name or be a subdomain of
modular.im.": "Enter the location of your Modular homeserver. It may use your own domain name or be a subdomain of
modular.im.",
+ "Server Name": "Server Name",
"The email field must not be blank.": "The email field must not be blank.",
- "The user name field must not be blank.": "The user name field must not be blank.",
+ "The username field must not be blank.": "The username field must not be blank.",
"The phone number field must not be blank.": "The phone number field must not be blank.",
"The password field must not be blank.": "The password field must not be blank.",
"Username on %(hs)s": "Username on %(hs)s",
- "User name": "User name",
+ "Username": "Username",
"Mobile phone number": "Mobile phone number",
"Not sure of your password?
Set a new one": "Not sure of your password?
Set a new one",
- "%(serverName)s Matrix ID": "%(serverName)s Matrix ID",
+ "Your account": "Your account",
+ "Your %(serverName)s account": "Your %(serverName)s account",
"Sign in with": "Sign in with",
"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?",
"Email address (optional)": "Email address (optional)",
"Mobile phone number (optional)": "Mobile phone number (optional)",
- "Default server": "Default server",
- "Custom server": "Custom server",
- "Home server URL": "Home server URL",
- "Identity server URL": "Identity server URL",
- "What does this mean?": "What does this mean?",
+ "Other servers": "Other servers",
+ "Enter custom server URLs
What does this mean?": "Enter custom server URLs
What does this mean?",
+ "Homeserver URL": "Homeserver URL",
+ "Identity Server URL": "Identity Server URL",
+ "Free": "Free",
+ "Join millions for free on the largest public server": "Join millions for free on the largest public server",
+ "Premium": "Premium",
+ "Premium hosting for organisations
Learn more": "Premium hosting for organisations
Learn more",
+ "Other": "Other",
+ "Find other public servers or use a custom server": "Find other public servers or use a custom server",
"Sorry, your browser is
not able to run Riot.": "Sorry, your browser is
not able to run Riot.",
"Riot uses many advanced browser features, some of which are not available or experimental in your current browser.": "Riot uses many advanced browser features, some of which are not available or experimental in your current browser.",
"Please install
Chrome or
Firefox for the best experience.": "Please install
Chrome or
Firefox for the best experience.",
@@ -1400,7 +1408,7 @@
"This doesn't look like a valid phone number.": "This doesn't look like a valid phone number.",
"An email address is required to register on this homeserver.": "An email address 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 user name.": "You need to enter a user name.",
+ "You need to enter a username.": "You need to enter a username.",
"An unknown error occurred.": "An unknown error occurred.",
"Create your account": "Create your account",
"Commands": "Commands",
diff --git a/src/notifications/VectorPushRulesDefinitions.js b/src/notifications/VectorPushRulesDefinitions.js
index 3df2e70774..402a69e7a6 100644
--- a/src/notifications/VectorPushRulesDefinitions.js
+++ b/src/notifications/VectorPushRulesDefinitions.js
@@ -83,7 +83,7 @@ module.exports = {
// Messages containing user's username (localpart/MXID)
".m.rule.contains_user_name": new VectorPushRuleDefinition({
kind: "override",
- description: _td("Messages containing my user name"), // passed through _t() translation in src/components/views/settings/Notifications.js
+ description: _td("Messages containing my username"), // passed through _t() translation in src/components/views/settings/Notifications.js
vectorStateToActions: { // The actions for each vector state, or null to disable the rule.
on: StandardActions.ACTION_NOTIFY,
loud: StandardActions.ACTION_HIGHLIGHT_DEFAULT_SOUND,