Merge pull request #2327 from matrix-org/travis/well-known-improvements

Introduce a default_server_name for aesthetics and rework .well-known
This commit is contained in:
Travis Ralston 2018-12-13 14:55:04 -07:00 committed by GitHub
commit 366f343432
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 190 additions and 122 deletions

View file

@ -48,6 +48,8 @@ import SettingsStore, {SettingLevel} from "../../settings/SettingsStore";
import { startAnyRegistrationFlow } from "../../Registration.js"; import { startAnyRegistrationFlow } from "../../Registration.js";
import { messageForSyncError } from '../../utils/ErrorUtils'; import { messageForSyncError } from '../../utils/ErrorUtils';
const AutoDiscovery = Matrix.AutoDiscovery;
// Disable warnings for now: we use deprecated bluebird functions // Disable warnings for now: we use deprecated bluebird functions
// and need to migrate, but they spam the console with warnings. // and need to migrate, but they spam the console with warnings.
Promise.config({warnings: false}); Promise.config({warnings: false});
@ -181,6 +183,12 @@ export default React.createClass({
register_is_url: null, register_is_url: null,
register_id_sid: null, register_id_sid: null,
// Parameters used for setting up the login/registration views
defaultServerName: this.props.config.default_server_name,
defaultHsUrl: this.props.config.default_hs_url,
defaultIsUrl: this.props.config.default_is_url,
defaultServerDiscoveryError: null,
// When showing Modal dialogs we need to set aria-hidden on the root app element // When showing Modal dialogs we need to set aria-hidden on the root app element
// and disable it when there are no dialogs // and disable it when there are no dialogs
hideToSRUsers: false, hideToSRUsers: false,
@ -199,6 +207,10 @@ export default React.createClass({
}; };
}, },
getDefaultServerName: function() {
return this.state.defaultServerName;
},
getCurrentHsUrl: function() { getCurrentHsUrl: function() {
if (this.state.register_hs_url) { if (this.state.register_hs_url) {
return this.state.register_hs_url; return this.state.register_hs_url;
@ -209,8 +221,10 @@ export default React.createClass({
} }
}, },
getDefaultHsUrl() { getDefaultHsUrl(defaultToMatrixDotOrg) {
return this.props.config.default_hs_url || "https://matrix.org"; defaultToMatrixDotOrg = typeof(defaultToMatrixDotOrg) !== 'boolean' ? true : defaultToMatrixDotOrg;
if (!this.state.defaultHsUrl && defaultToMatrixDotOrg) return "https://matrix.org";
return this.state.defaultHsUrl;
}, },
getFallbackHsUrl: function() { getFallbackHsUrl: function() {
@ -228,7 +242,7 @@ export default React.createClass({
}, },
getDefaultIsUrl() { getDefaultIsUrl() {
return this.props.config.default_is_url || "https://vector.im"; return this.state.defaultIsUrl || "https://vector.im";
}, },
componentWillMount: function() { componentWillMount: function() {
@ -278,6 +292,20 @@ export default React.createClass({
console.info(`Team token set to ${this._teamToken}`); console.info(`Team token set to ${this._teamToken}`);
} }
// Set up the default URLs (async)
if (this.getDefaultServerName() && !this.getDefaultHsUrl(false)) {
this.setState({loadingDefaultHomeserver: true});
this._tryDiscoverDefaultHomeserver(this.getDefaultServerName());
} else if (this.getDefaultServerName() && this.getDefaultHsUrl(false)) {
// Ideally we would somehow only communicate this to the server admins, but
// given this is at login time we can't really do much besides hope that people
// will check their settings.
this.setState({
defaultServerName: null, // To un-hide any secrets people might be keeping
defaultServerDiscoveryError: _t("Invalid configuration: Cannot supply a default homeserver URL and a default server name"),
});
}
// Set a default HS with query param `hs_url` // Set a default HS with query param `hs_url`
const paramHs = this.props.startingFragmentQueryParams.hs_url; const paramHs = this.props.startingFragmentQueryParams.hs_url;
if (paramHs) { if (paramHs) {
@ -1728,6 +1756,36 @@ export default React.createClass({
this.setState(newState); this.setState(newState);
}, },
_tryDiscoverDefaultHomeserver: async function(serverName) {
try {
const discovery = await AutoDiscovery.findClientConfig(serverName);
const state = discovery["m.homeserver"].state;
if (state !== AutoDiscovery.SUCCESS) {
console.error("Failed to discover homeserver on startup:", discovery);
this.setState({
defaultServerDiscoveryError: discovery["m.homeserver"].error,
loadingDefaultHomeserver: false,
});
} else {
const hsUrl = discovery["m.homeserver"].base_url;
const isUrl = discovery["m.identity_server"].state === AutoDiscovery.SUCCESS
? discovery["m.identity_server"].base_url
: "https://vector.im";
this.setState({
defaultHsUrl: hsUrl,
defaultIsUrl: isUrl,
loadingDefaultHomeserver: false,
});
}
} catch (e) {
console.error(e);
this.setState({
defaultServerDiscoveryError: _t("Unknown error discovering homeserver"),
loadingDefaultHomeserver: false,
});
}
},
_makeRegistrationUrl: function(params) { _makeRegistrationUrl: function(params) {
if (this.props.startingFragmentQueryParams.referrer) { if (this.props.startingFragmentQueryParams.referrer) {
params.referrer = this.props.startingFragmentQueryParams.referrer; params.referrer = this.props.startingFragmentQueryParams.referrer;
@ -1742,7 +1800,7 @@ export default React.createClass({
render: function() { render: function() {
// console.log(`Rendering MatrixChat with view ${this.state.view}`); // console.log(`Rendering MatrixChat with view ${this.state.view}`);
if (this.state.view === VIEWS.LOADING || this.state.view === VIEWS.LOGGING_IN) { if (this.state.view === VIEWS.LOADING || this.state.view === VIEWS.LOGGING_IN || this.state.loadingDefaultHomeserver) {
const Spinner = sdk.getComponent('elements.Spinner'); const Spinner = sdk.getComponent('elements.Spinner');
return ( return (
<div className="mx_MatrixChat_splash"> <div className="mx_MatrixChat_splash">
@ -1816,6 +1874,8 @@ export default React.createClass({
idSid={this.state.register_id_sid} idSid={this.state.register_id_sid}
email={this.props.startingFragmentQueryParams.email} email={this.props.startingFragmentQueryParams.email}
referrer={this.props.startingFragmentQueryParams.referrer} referrer={this.props.startingFragmentQueryParams.referrer}
defaultServerName={this.getDefaultServerName()}
defaultServerDiscoveryError={this.state.defaultServerDiscoveryError}
defaultHsUrl={this.getDefaultHsUrl()} defaultHsUrl={this.getDefaultHsUrl()}
defaultIsUrl={this.getDefaultIsUrl()} defaultIsUrl={this.getDefaultIsUrl()}
brand={this.props.config.brand} brand={this.props.config.brand}
@ -1838,6 +1898,8 @@ export default React.createClass({
const ForgotPassword = sdk.getComponent('structures.login.ForgotPassword'); const ForgotPassword = sdk.getComponent('structures.login.ForgotPassword');
return ( return (
<ForgotPassword <ForgotPassword
defaultServerName={this.getDefaultServerName()}
defaultServerDiscoveryError={this.state.defaultServerDiscoveryError}
defaultHsUrl={this.getDefaultHsUrl()} defaultHsUrl={this.getDefaultHsUrl()}
defaultIsUrl={this.getDefaultIsUrl()} defaultIsUrl={this.getDefaultIsUrl()}
customHsUrl={this.getCurrentHsUrl()} customHsUrl={this.getCurrentHsUrl()}
@ -1854,6 +1916,8 @@ export default React.createClass({
<Login <Login
onLoggedIn={Lifecycle.setLoggedIn} onLoggedIn={Lifecycle.setLoggedIn}
onRegisterClick={this.onRegisterClick} onRegisterClick={this.onRegisterClick}
defaultServerName={this.getDefaultServerName()}
defaultServerDiscoveryError={this.state.defaultServerDiscoveryError}
defaultHsUrl={this.getDefaultHsUrl()} defaultHsUrl={this.getDefaultHsUrl()}
defaultIsUrl={this.getDefaultIsUrl()} defaultIsUrl={this.getDefaultIsUrl()}
customHsUrl={this.getCurrentHsUrl()} customHsUrl={this.getCurrentHsUrl()}

View file

@ -36,6 +36,14 @@ module.exports = React.createClass({
onLoginClick: PropTypes.func, onLoginClick: PropTypes.func,
onRegisterClick: PropTypes.func, onRegisterClick: PropTypes.func,
onComplete: PropTypes.func.isRequired, onComplete: PropTypes.func.isRequired,
// The default server name to use when the user hasn't specified
// one. This is used when displaying the defaultHsUrl in the UI.
defaultServerName: PropTypes.string,
// An error passed along from higher up explaining that something
// went wrong when finding the defaultHsUrl.
defaultServerDiscoveryError: PropTypes.string,
}, },
getInitialState: function() { getInitialState: function() {
@ -45,6 +53,7 @@ module.exports = React.createClass({
progress: null, progress: null,
password: null, password: null,
password2: null, password2: null,
errorText: null,
}; };
}, },
@ -81,6 +90,13 @@ module.exports = React.createClass({
onSubmitForm: function(ev) { onSubmitForm: function(ev) {
ev.preventDefault(); ev.preventDefault();
// Don't allow the user to register if there's a discovery error
// Without this, the user could end up registering on the wrong homeserver.
if (this.props.defaultServerDiscoveryError) {
this.setState({errorText: this.props.defaultServerDiscoveryError});
return;
}
if (!this.state.email) { if (!this.state.email) {
this.showErrorDialog(_t('The email address linked to your account must be entered.')); this.showErrorDialog(_t('The email address linked to your account must be entered.'));
} else if (!this.state.password || !this.state.password2) { } else if (!this.state.password || !this.state.password2) {
@ -200,6 +216,12 @@ module.exports = React.createClass({
); );
} }
let errorText = null;
const err = this.state.errorText || this.props.defaultServerDiscoveryError;
if (err) {
errorText = <div className="mx_Login_error">{ err }</div>;
}
const LanguageSelector = sdk.getComponent('structures.login.LanguageSelector'); const LanguageSelector = sdk.getComponent('structures.login.LanguageSelector');
resetPasswordJsx = ( resetPasswordJsx = (
@ -230,6 +252,7 @@ module.exports = React.createClass({
<input className="mx_Login_submit" type="submit" value={_t('Send Reset Email')} /> <input className="mx_Login_submit" type="submit" value={_t('Send Reset Email')} />
</form> </form>
{ serverConfigSection } { serverConfigSection }
{ errorText }
<a className="mx_Login_create" onClick={this.props.onLoginClick} href="#"> <a className="mx_Login_create" onClick={this.props.onLoginClick} href="#">
{ _t('Return to login screen') } { _t('Return to login screen') }
</a> </a>

View file

@ -26,11 +26,17 @@ import Login from '../../../Login';
import SdkConfig from '../../../SdkConfig'; import SdkConfig from '../../../SdkConfig';
import SettingsStore from "../../../settings/SettingsStore"; import SettingsStore from "../../../settings/SettingsStore";
import { messageForResourceLimitError } from '../../../utils/ErrorUtils'; import { messageForResourceLimitError } from '../../../utils/ErrorUtils';
import request from 'browser-request'; import { AutoDiscovery } from "matrix-js-sdk";
// For validating phone numbers without country codes // For validating phone numbers without country codes
const PHONE_NUMBER_REGEX = /^[0-9()\-\s]*$/; const PHONE_NUMBER_REGEX = /^[0-9()\-\s]*$/;
// These are used in several places, and come from the js-sdk's autodiscovery
// stuff. We define them here so that they'll be picked up by i18n.
_td("Invalid homeserver discovery response");
_td("Invalid identity server discovery response");
_td("General failure");
/** /**
* A wire component which glues together login UI components and Login logic * A wire component which glues together login UI components and Login logic
*/ */
@ -51,6 +57,14 @@ module.exports = React.createClass({
// different home server without confusing users. // different home server without confusing users.
fallbackHsUrl: PropTypes.string, fallbackHsUrl: PropTypes.string,
// The default server name to use when the user hasn't specified
// one. This is used when displaying the defaultHsUrl in the UI.
defaultServerName: PropTypes.string,
// An error passed along from higher up explaining that something
// went wrong when finding the defaultHsUrl.
defaultServerDiscoveryError: PropTypes.string,
defaultDeviceDisplayName: PropTypes.string, defaultDeviceDisplayName: PropTypes.string,
// login shouldn't know or care how registration is done. // login shouldn't know or care how registration is done.
@ -80,6 +94,7 @@ module.exports = React.createClass({
discoveredHsUrl: "", discoveredHsUrl: "",
discoveredIsUrl: "", discoveredIsUrl: "",
discoveryError: "", discoveryError: "",
findingHomeserver: false,
}; };
}, },
@ -113,7 +128,7 @@ module.exports = React.createClass({
onPasswordLogin: function(username, phoneCountry, phoneNumber, password) { onPasswordLogin: function(username, phoneCountry, phoneNumber, password) {
// Prevent people from submitting their password when homeserver // Prevent people from submitting their password when homeserver
// discovery went wrong // discovery went wrong
if (this.state.discoveryError) return; if (this.state.discoveryError || this.props.defaultServerDiscoveryError) return;
this.setState({ this.setState({
busy: true, busy: true,
@ -285,117 +300,54 @@ module.exports = React.createClass({
_tryWellKnownDiscovery: async function(serverName) { _tryWellKnownDiscovery: async function(serverName) {
if (!serverName.trim()) { if (!serverName.trim()) {
// Nothing to discover // Nothing to discover
this.setState({discoveryError: "", discoveredHsUrl: "", discoveredIsUrl: ""}); this.setState({discoveryError: "", discoveredHsUrl: "", discoveredIsUrl: "", findingHomeserver: false});
return; return;
} }
this.setState({findingHomeserver: true});
try { try {
const wellknown = await this._getWellKnownObject(`https://${serverName}/.well-known/matrix/client`); const discovery = await AutoDiscovery.findClientConfig(serverName);
if (!wellknown["m.homeserver"]) { const state = discovery["m.homeserver"].state;
console.error("No m.homeserver key in well-known response"); if (state !== AutoDiscovery.SUCCESS && state !== AutoDiscovery.PROMPT) {
this.setState({discoveryError: _t("Invalid homeserver discovery response")}); this.setState({
return; discoveredHsUrl: "",
} discoveredIsUrl: "",
discoveryError: discovery["m.homeserver"].error,
const hsUrl = this._sanitizeWellKnownUrl(wellknown["m.homeserver"]["base_url"]); findingHomeserver: false,
if (!hsUrl) {
console.error("Invalid base_url for m.homeserver");
this.setState({discoveryError: _t("Invalid homeserver discovery response")});
return;
}
console.log("Verifying homeserver URL: " + hsUrl);
const hsVersions = await this._getWellKnownObject(`${hsUrl}/_matrix/client/versions`);
if (!hsVersions["versions"]) {
console.error("Invalid /versions response");
this.setState({discoveryError: _t("Invalid homeserver discovery response")});
return;
}
let isUrl = "";
if (wellknown["m.identity_server"]) {
isUrl = this._sanitizeWellKnownUrl(wellknown["m.identity_server"]["base_url"]);
if (!isUrl) {
console.error("Invalid base_url for m.identity_server");
this.setState({discoveryError: _t("Invalid homeserver discovery response")});
return;
}
console.log("Verifying identity server URL: " + isUrl);
const isResponse = await this._getWellKnownObject(`${isUrl}/_matrix/identity/api/v1`);
if (!isResponse) {
console.error("Invalid /api/v1 response");
this.setState({discoveryError: _t("Invalid homeserver discovery response")});
return;
}
}
this.setState({discoveredHsUrl: hsUrl, discoveredIsUrl: isUrl, discoveryError: ""});
} catch (e) {
console.error(e);
if (e.wkAction) {
if (e.wkAction === "FAIL_ERROR" || e.wkAction === "FAIL_PROMPT") {
// We treat FAIL_ERROR and FAIL_PROMPT the same to avoid having the user
// submit their details to the wrong homeserver. In practice, the custom
// server options will show up to try and guide the user into entering
// the required information.
this.setState({discoveryError: _t("Cannot find homeserver")});
return;
} else if (e.wkAction === "IGNORE") {
// Nothing to discover
this.setState({discoveryError: "", discoveredHsUrl: "", discoveredIsUrl: ""});
return;
}
}
throw e;
}
},
_sanitizeWellKnownUrl: function(url) {
if (!url) return false;
const parser = document.createElement('a');
parser.href = url;
if (parser.protocol !== "http:" && parser.protocol !== "https:") return false;
if (!parser.hostname) return false;
const port = parser.port ? `:${parser.port}` : "";
const path = parser.pathname ? parser.pathname : "";
let saferUrl = `${parser.protocol}//${parser.hostname}${port}${path}`;
if (saferUrl.endsWith("/")) saferUrl = saferUrl.substring(0, saferUrl.length - 1);
return saferUrl;
},
_getWellKnownObject: function(url) {
return new Promise(function(resolve, reject) {
request(
{ method: "GET", url: url },
(err, response, body) => {
if (err || response.status < 200 || response.status >= 300) {
let action = "FAIL_ERROR";
if (response.status === 404) {
// We could just resolve with an empty object, but that
// causes a different series of branches when the m.homeserver
// bit of the JSON is missing.
action = "IGNORE";
}
reject({err: err, response: response, wkAction: action});
return;
}
try {
resolve(JSON.parse(body));
} catch (e) {
console.error(e);
if (e.name === "SyntaxError") {
reject({wkAction: "FAIL_PROMPT", wkError: "Invalid JSON"});
} else throw e;
}
},
);
}); });
} else if (state === AutoDiscovery.PROMPT) {
this.setState({
discoveredHsUrl: "",
discoveredIsUrl: "",
discoveryError: "",
findingHomeserver: false,
});
} else if (state === AutoDiscovery.SUCCESS) {
this.setState({
discoveredHsUrl: discovery["m.homeserver"].base_url,
discoveredIsUrl:
discovery["m.identity_server"].state === AutoDiscovery.SUCCESS
? discovery["m.identity_server"].base_url
: "",
discoveryError: "",
findingHomeserver: false,
});
} else {
console.warn("Unknown state for m.homeserver in discovery response: ", discovery);
this.setState({
discoveredHsUrl: "",
discoveredIsUrl: "",
discoveryError: _t("Unknown failure discovering homeserver"),
findingHomeserver: false,
});
}
} catch (e) {
console.error(e);
this.setState({
findingHomeserver: false,
discoveryError: _t("Unknown error discovering homeserver"),
});
}
}, },
_initLoginLogic: function(hsUrl, isUrl) { _initLoginLogic: function(hsUrl, isUrl) {
@ -541,6 +493,8 @@ module.exports = React.createClass({
onForgotPasswordClick={this.props.onForgotPasswordClick} onForgotPasswordClick={this.props.onForgotPasswordClick}
loginIncorrect={this.state.loginIncorrect} loginIncorrect={this.state.loginIncorrect}
hsUrl={this.state.enteredHomeserverUrl} hsUrl={this.state.enteredHomeserverUrl}
hsName={this.props.defaultServerName}
disableSubmit={this.state.findingHomeserver}
/> />
); );
}, },
@ -559,7 +513,7 @@ module.exports = React.createClass({
const ServerConfig = sdk.getComponent("login.ServerConfig"); const ServerConfig = sdk.getComponent("login.ServerConfig");
const loader = this.state.busy ? <div className="mx_Login_loader"><Loader /></div> : null; const loader = this.state.busy ? <div className="mx_Login_loader"><Loader /></div> : null;
const errorText = this.state.discoveryError || this.state.errorText; const errorText = this.props.defaultServerDiscoveryError || this.state.discoveryError || this.state.errorText;
let loginAsGuestJsx; let loginAsGuestJsx;
if (this.props.enableGuest) { if (this.props.enableGuest) {
@ -576,7 +530,7 @@ module.exports = React.createClass({
serverConfig = <ServerConfig ref="serverConfig" serverConfig = <ServerConfig ref="serverConfig"
withToggleButton={true} withToggleButton={true}
customHsUrl={this.state.discoveredHsUrl || this.props.customHsUrl} customHsUrl={this.state.discoveredHsUrl || this.props.customHsUrl}
customIsUrl={this.state.discoveredIsUrl ||this.props.customIsUrl} customIsUrl={this.state.discoveredIsUrl || this.props.customIsUrl}
defaultHsUrl={this.props.defaultHsUrl} defaultHsUrl={this.props.defaultHsUrl}
defaultIsUrl={this.props.defaultIsUrl} defaultIsUrl={this.props.defaultIsUrl}
onServerConfigChange={this.onServerConfigChange} onServerConfigChange={this.onServerConfigChange}

View file

@ -57,6 +57,14 @@ module.exports = React.createClass({
}), }),
teamSelected: PropTypes.object, teamSelected: PropTypes.object,
// The default server name to use when the user hasn't specified
// one. This is used when displaying the defaultHsUrl in the UI.
defaultServerName: PropTypes.string,
// An error passed along from higher up explaining that something
// went wrong when finding the defaultHsUrl.
defaultServerDiscoveryError: PropTypes.string,
defaultDeviceDisplayName: PropTypes.string, defaultDeviceDisplayName: PropTypes.string,
// registration shouldn't know or care how login is done. // registration shouldn't know or care how login is done.
@ -170,6 +178,12 @@ module.exports = React.createClass({
}, },
onFormSubmit: function(formVals) { onFormSubmit: function(formVals) {
// Don't allow the user to register if there's a discovery error
// Without this, the user could end up registering on the wrong homeserver.
if (this.props.defaultServerDiscoveryError) {
this.setState({errorText: this.props.defaultServerDiscoveryError});
return;
}
this.setState({ this.setState({
errorText: "", errorText: "",
busy: true, busy: true,
@ -441,12 +455,13 @@ module.exports = React.createClass({
let header; let header;
let errorText; let errorText;
// FIXME: remove hardcoded Status team tweaks at some point // FIXME: remove hardcoded Status team tweaks at some point
if (theme === 'status' && this.state.errorText) { const err = this.state.errorText || this.props.defaultServerDiscoveryError;
header = <div className="mx_Login_error">{ this.state.errorText }</div>; if (theme === 'status' && err) {
header = <div className="mx_Login_error">{ err }</div>;
} else { } else {
header = <h2>{ _t('Create an account') }</h2>; header = <h2>{ _t('Create an account') }</h2>;
if (this.state.errorText) { if (err) {
errorText = <div className="mx_Login_error">{ this.state.errorText }</div>; errorText = <div className="mx_Login_error">{ err }</div>;
} }
} }

View file

@ -40,6 +40,8 @@ class PasswordLogin extends React.Component {
initialPassword: "", initialPassword: "",
loginIncorrect: false, loginIncorrect: false,
hsDomain: "", hsDomain: "",
hsName: null,
disableSubmit: false,
} }
constructor(props) { constructor(props) {
@ -250,13 +252,15 @@ class PasswordLogin extends React.Component {
); );
} }
let matrixIdText = ''; let matrixIdText = _t('Matrix ID');
if (this.props.hsUrl) { if (this.props.hsName) {
matrixIdText = _t('%(serverName)s Matrix ID', {serverName: this.props.hsName});
} else {
try { try {
const parsedHsUrl = new URL(this.props.hsUrl); const parsedHsUrl = new URL(this.props.hsUrl);
matrixIdText = _t('%(serverName)s Matrix ID', {serverName: parsedHsUrl.hostname}); matrixIdText = _t('%(serverName)s Matrix ID', {serverName: parsedHsUrl.hostname});
} catch (e) { } catch (e) {
// pass // ignore
} }
} }
@ -288,6 +292,8 @@ class PasswordLogin extends React.Component {
); );
} }
const disableSubmit = this.props.disableSubmit || matrixIdText === '';
return ( return (
<div> <div>
<form onSubmit={this.onSubmitForm}> <form onSubmit={this.onSubmitForm}>
@ -301,7 +307,7 @@ class PasswordLogin extends React.Component {
/> />
<br /> <br />
{ forgotPasswordJsx } { forgotPasswordJsx }
<input className="mx_Login_submit" type="submit" value={_t('Sign in')} disabled={matrixIdText === ''} /> <input className="mx_Login_submit" type="submit" value={_t('Sign in')} disabled={disableSubmit} />
</form> </form>
</div> </div>
); );
@ -325,6 +331,8 @@ PasswordLogin.propTypes = {
onPhoneNumberChanged: PropTypes.func, onPhoneNumberChanged: PropTypes.func,
onPasswordChanged: PropTypes.func, onPasswordChanged: PropTypes.func,
loginIncorrect: PropTypes.bool, loginIncorrect: PropTypes.bool,
hsName: PropTypes.string,
disableSubmit: PropTypes.bool,
}; };
module.exports = PasswordLogin; module.exports = PasswordLogin;

View file

@ -724,6 +724,7 @@
"User name": "User name", "User name": "User name",
"Mobile phone number": "Mobile phone number", "Mobile phone number": "Mobile phone number",
"Forgot your password?": "Forgot your password?", "Forgot your password?": "Forgot your password?",
"Matrix ID": "Matrix ID",
"%(serverName)s Matrix ID": "%(serverName)s Matrix ID", "%(serverName)s Matrix ID": "%(serverName)s Matrix ID",
"Sign in with": "Sign in with", "Sign in with": "Sign in with",
"Email address": "Email address", "Email address": "Email address",
@ -871,7 +872,6 @@
"And %(count)s more...|other": "And %(count)s more...", "And %(count)s more...|other": "And %(count)s more...",
"ex. @bob:example.com": "ex. @bob:example.com", "ex. @bob:example.com": "ex. @bob:example.com",
"Add User": "Add User", "Add User": "Add User",
"Matrix ID": "Matrix ID",
"Matrix Room ID": "Matrix Room ID", "Matrix Room ID": "Matrix Room ID",
"email address": "email address", "email address": "email address",
"That doesn't look like a valid email address": "That doesn't look like a valid email address", "That doesn't look like a valid email address": "That doesn't look like a valid email address",
@ -1117,6 +1117,7 @@
"You are currently using Riot anonymously as a guest.": "You are currently using Riot anonymously as a guest.", "You are currently using Riot anonymously as a guest.": "You are currently using Riot anonymously as a guest.",
"If you would like to create a Matrix account you can <a>register</a> now.": "If you would like to create a Matrix account you can <a>register</a> now.", "If you would like to create a Matrix account you can <a>register</a> now.": "If you would like to create a Matrix account you can <a>register</a> now.",
"Login": "Login", "Login": "Login",
"Invalid configuration: Cannot supply a default homeserver URL and a default server name": "Invalid configuration: Cannot supply a default homeserver URL and a default server name",
"Failed to reject invitation": "Failed to reject invitation", "Failed to reject invitation": "Failed to reject invitation",
"This room is not public. You will not be able to rejoin without an invite.": "This room is not public. You will not be able to rejoin without an invite.", "This room is not public. You will not be able to rejoin without an invite.": "This room is not public. You will not be able to rejoin without an invite.",
"Are you sure you want to leave the room '%(roomName)s'?": "Are you sure you want to leave the room '%(roomName)s'?", "Are you sure you want to leave the room '%(roomName)s'?": "Are you sure you want to leave the room '%(roomName)s'?",
@ -1130,6 +1131,7 @@
"Review terms and conditions": "Review terms and conditions", "Review terms and conditions": "Review terms and conditions",
"Old cryptography data detected": "Old cryptography data detected", "Old cryptography data detected": "Old cryptography data detected",
"Data from an older version of Riot has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.": "Data from an older version of Riot has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.", "Data from an older version of Riot has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.": "Data from an older version of Riot has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.",
"Unknown error discovering homeserver": "Unknown error discovering homeserver",
"Logout": "Logout", "Logout": "Logout",
"Your Communities": "Your Communities", "Your Communities": "Your Communities",
"Did you know: you can use communities to filter your Riot.im experience!": "Did you know: you can use communities to filter your Riot.im experience!", "Did you know: you can use communities to filter your Riot.im experience!": "Did you know: you can use communities to filter your Riot.im experience!",
@ -1290,6 +1292,9 @@
"Confirm your new password": "Confirm your new password", "Confirm your new password": "Confirm your new password",
"Send Reset Email": "Send Reset Email", "Send Reset Email": "Send Reset Email",
"Create an account": "Create an account", "Create an account": "Create an account",
"Invalid homeserver discovery response": "Invalid homeserver discovery response",
"Invalid identity server discovery response": "Invalid identity server discovery response",
"General failure": "General failure",
"This Home Server does not support login using email address.": "This Home Server does not support login using email address.", "This Home Server does not support login using email address.": "This Home Server does not support login using email address.",
"Please <a>contact your service administrator</a> to continue using this service.": "Please <a>contact your service administrator</a> to continue using this service.", "Please <a>contact your service administrator</a> to continue using this service.": "Please <a>contact your service administrator</a> to continue using this service.",
"Incorrect username and/or password.": "Incorrect username and/or password.", "Incorrect username and/or password.": "Incorrect username and/or password.",
@ -1297,8 +1302,7 @@
"Guest access is disabled on this Home Server.": "Guest access is disabled on this Home Server.", "Guest access is disabled on this Home Server.": "Guest access is disabled on this Home Server.",
"Failed to perform homeserver discovery": "Failed to perform homeserver discovery", "Failed to perform homeserver discovery": "Failed to perform homeserver discovery",
"The phone number entered looks invalid": "The phone number entered looks invalid", "The phone number entered looks invalid": "The phone number entered looks invalid",
"Invalid homeserver discovery response": "Invalid homeserver discovery response", "Unknown failure discovering homeserver": "Unknown failure discovering homeserver",
"Cannot find homeserver": "Cannot find homeserver",
"This homeserver doesn't offer any login flows which are supported by this client.": "This homeserver doesn't offer any login flows which are supported by this client.", "This homeserver doesn't offer any login flows which are supported by this client.": "This homeserver doesn't offer any login flows which are supported by this client.",
"Error: Problem communicating with the given homeserver.": "Error: Problem communicating with the given homeserver.", "Error: Problem communicating with the given homeserver.": "Error: Problem communicating with the given homeserver.",
"Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or <a>enable unsafe scripts</a>.": "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or <a>enable unsafe scripts</a>.", "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or <a>enable unsafe scripts</a>.": "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or <a>enable unsafe scripts</a>.",