Merge branch 'develop' into travis/integs/account_set
This commit is contained in:
commit
beb6ec4327
13 changed files with 147 additions and 59 deletions
|
@ -50,7 +50,6 @@ src/components/views/settings/Notifications.js
|
||||||
src/GroupAddressPicker.js
|
src/GroupAddressPicker.js
|
||||||
src/HtmlUtils.js
|
src/HtmlUtils.js
|
||||||
src/ImageUtils.js
|
src/ImageUtils.js
|
||||||
src/languageHandler.js
|
|
||||||
src/linkify-matrix.js
|
src/linkify-matrix.js
|
||||||
src/Markdown.js
|
src/Markdown.js
|
||||||
src/MatrixClientPeg.js
|
src/MatrixClientPeg.js
|
||||||
|
|
|
@ -15,6 +15,9 @@ module.exports = {
|
||||||
"number-leading-zero": null,
|
"number-leading-zero": null,
|
||||||
"selector-list-comma-newline-after": null,
|
"selector-list-comma-newline-after": null,
|
||||||
"at-rule-no-unknown": null,
|
"at-rule-no-unknown": null,
|
||||||
"scss/at-rule-no-unknown": true,
|
"scss/at-rule-no-unknown": [true, {
|
||||||
|
// https://github.com/vector-im/riot-web/issues/10544
|
||||||
|
"ignoreAtRules": ["define-mixin"],
|
||||||
|
}],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -559,3 +559,7 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus {
|
||||||
.mx_Username_color8 {
|
.mx_Username_color8 {
|
||||||
color: $username-variant8-color;
|
color: $username-variant8-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@define-mixin mx_Settings_fullWidthField {
|
||||||
|
margin-right: 200px;
|
||||||
|
}
|
||||||
|
|
|
@ -26,6 +26,10 @@ limitations under the License.
|
||||||
height: 4em;
|
height: 4em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_ProfileSettings_controls .mx_Field {
|
||||||
|
margin-right: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_ProfileSettings_controls .mx_Field:first-child {
|
.mx_ProfileSettings_controls .mx_Field:first-child {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2019 New Vector Ltd
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
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.
|
||||||
|
@ -15,5 +15,5 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.mx_SetIdServer .mx_Field_input {
|
.mx_SetIdServer .mx_Field_input {
|
||||||
width: 300px;
|
@mixin mx_Settings_fullWidthField;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_GeneralUserSettingsTab_changePassword .mx_Field,
|
.mx_GeneralUserSettingsTab_changePassword .mx_Field,
|
||||||
.mx_GeneralUserSettingsTab_themeSection .mx_Field {
|
.mx_GeneralUserSettingsTab_themeSection .mx_Field {
|
||||||
margin-right: 100px; // Align with the other fields on the page
|
@mixin mx_Settings_fullWidthField;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_GeneralUserSettingsTab_changePassword .mx_Field:first-child {
|
.mx_GeneralUserSettingsTab_changePassword .mx_Field:first-child {
|
||||||
|
@ -26,5 +26,5 @@ limitations under the License.
|
||||||
.mx_GeneralUserSettingsTab_accountSection .mx_EmailAddresses,
|
.mx_GeneralUserSettingsTab_accountSection .mx_EmailAddresses,
|
||||||
.mx_GeneralUserSettingsTab_accountSection .mx_PhoneNumbers,
|
.mx_GeneralUserSettingsTab_accountSection .mx_PhoneNumbers,
|
||||||
.mx_GeneralUserSettingsTab_languageInput {
|
.mx_GeneralUserSettingsTab_languageInput {
|
||||||
margin-right: 100px; // Align with the other fields on the page
|
@mixin mx_Settings_fullWidthField;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,5 +15,5 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.mx_PreferencesUserSettingsTab .mx_Field {
|
.mx_PreferencesUserSettingsTab .mx_Field {
|
||||||
margin-right: 100px; // Align with the rest of the controls
|
@mixin mx_Settings_fullWidthField;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.mx_VoiceUserSettingsTab .mx_Field {
|
.mx_VoiceUserSettingsTab .mx_Field {
|
||||||
margin-right: 100px; // align with the rest of the fields
|
@mixin mx_Settings_fullWidthField;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_VoiceUserSettingsTab_missingMediaPermissions {
|
.mx_VoiceUserSettingsTab_missingMediaPermissions {
|
||||||
|
|
|
@ -48,7 +48,7 @@ export default class Field extends React.PureComponent {
|
||||||
onValidate: PropTypes.func,
|
onValidate: PropTypes.func,
|
||||||
// If specified, contents will appear as a tooltip on the element and
|
// If specified, contents will appear as a tooltip on the element and
|
||||||
// validation feedback tooltips will be suppressed.
|
// validation feedback tooltips will be suppressed.
|
||||||
tooltip: PropTypes.node,
|
tooltipContent: PropTypes.node,
|
||||||
// All other props pass through to the <input>.
|
// All other props pass through to the <input>.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -137,8 +137,7 @@ export default class Field extends React.PureComponent {
|
||||||
}, VALIDATION_THROTTLE_MS);
|
}, VALIDATION_THROTTLE_MS);
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { element, prefix, onValidate, children, ...inputProps } = this.props;
|
const { element, prefix, onValidate, children, tooltipContent, ...inputProps } = this.props;
|
||||||
delete inputProps.tooltip; // needs to be removed from props but we don't need it here
|
|
||||||
|
|
||||||
const inputElement = element || "input";
|
const inputElement = element || "input";
|
||||||
|
|
||||||
|
@ -170,11 +169,11 @@ export default class Field extends React.PureComponent {
|
||||||
// Handle displaying feedback on validity
|
// Handle displaying feedback on validity
|
||||||
const Tooltip = sdk.getComponent("elements.Tooltip");
|
const Tooltip = sdk.getComponent("elements.Tooltip");
|
||||||
let fieldTooltip;
|
let fieldTooltip;
|
||||||
if (this.props.tooltip || this.state.feedback) {
|
if (tooltipContent || this.state.feedback) {
|
||||||
fieldTooltip = <Tooltip
|
fieldTooltip = <Tooltip
|
||||||
tooltipClassName="mx_Field_tooltip"
|
tooltipClassName="mx_Field_tooltip"
|
||||||
visible={this.state.feedbackVisible}
|
visible={this.state.feedbackVisible}
|
||||||
label={this.props.tooltip || this.state.feedback}
|
label={tooltipContent || this.state.feedback}
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2019 New Vector Ltd
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
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.
|
||||||
|
@ -14,14 +14,14 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import request from 'browser-request';
|
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {_t} from "../../../languageHandler";
|
import {_t} from "../../../languageHandler";
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import MatrixClientPeg from "../../../MatrixClientPeg";
|
import MatrixClientPeg from "../../../MatrixClientPeg";
|
||||||
import SdkConfig from "../../../SdkConfig";
|
import SdkConfig from "../../../SdkConfig";
|
||||||
import Field from "../elements/Field";
|
import Modal from '../../../Modal';
|
||||||
|
import dis from "../../../dispatcher";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If a url has no path component, etc. abbreviate it to just the hostname
|
* If a url has no path component, etc. abbreviate it to just the hostname
|
||||||
|
@ -58,41 +58,39 @@ function unabbreviateUrl(u) {
|
||||||
/**
|
/**
|
||||||
* Check an IS URL is valid, including liveness check
|
* Check an IS URL is valid, including liveness check
|
||||||
*
|
*
|
||||||
* @param {string} isUrl The url to check
|
* @param {string} u The url to check
|
||||||
* @returns {string} null if url passes all checks, otherwise i18ned error string
|
* @returns {string} null if url passes all checks, otherwise i18ned error string
|
||||||
*/
|
*/
|
||||||
async function checkIsUrl(isUrl) {
|
async function checkIdentityServerUrl(u) {
|
||||||
const parsedUrl = url.parse(isUrl);
|
const parsedUrl = url.parse(u);
|
||||||
|
|
||||||
if (parsedUrl.protocol !== 'https:') return _t("Identity Server URL must be HTTPS");
|
if (parsedUrl.protocol !== 'https:') return _t("Identity Server URL must be HTTPS");
|
||||||
|
|
||||||
// XXX: duplicated logic from js-sdk but it's quite tied up in the validation logic in the
|
// XXX: duplicated logic from js-sdk but it's quite tied up in the validation logic in the
|
||||||
// js-sdk so probably as easy to duplicate it than to separate it out so we can reuse it
|
// js-sdk so probably as easy to duplicate it than to separate it out so we can reuse it
|
||||||
return new Promise((resolve) => {
|
try {
|
||||||
request(
|
const response = await fetch(u + '/_matrix/identity/api/v1');
|
||||||
// also XXX: we don't really know whether to hit /v1 or /v2 for this: we
|
if (response.ok) {
|
||||||
// probably want a /versions endpoint like the C/S API.
|
return null;
|
||||||
{ method: "GET", url: isUrl + '/_matrix/identity/api/v1' },
|
|
||||||
(err, response, body) => {
|
|
||||||
if (err) {
|
|
||||||
resolve(_t("Could not connect to ID Server"));
|
|
||||||
} else if (response.status < 200 || response.status >= 300) {
|
} else if (response.status < 200 || response.status >= 300) {
|
||||||
resolve(_t("Not a valid ID Server (status code %(code)s)", {code: response.status}));
|
return _t("Not a valid Identity Server (status code %(code)s)", {code: response.status});
|
||||||
} else {
|
} else {
|
||||||
resolve(null);
|
return _t("Could not connect to Identity Server");
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return _t("Could not connect to Identity Server");
|
||||||
}
|
}
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class SetIdServer extends React.Component {
|
export default class SetIdServer extends React.Component {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
let defaultIdServer = abbreviateUrl(MatrixClientPeg.get().getIdentityServerUrl());
|
let defaultIdServer = '';
|
||||||
if (!defaultIdServer) {
|
if (!MatrixClientPeg.get().getIdentityServerUrl() && SdkConfig.get()['validated_server_config']['isUrl']) {
|
||||||
defaultIdServer = abbreviateUrl(SdkConfig.get()['validated_server_config']['idServer']) || '';
|
// If no ID server is configured but there's one in the config, prepopulate
|
||||||
|
// the field to help the user.
|
||||||
|
defaultIdServer = abbreviateUrl(SdkConfig.get()['validated_server_config']['isUrl']);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
|
@ -114,7 +112,7 @@ export default class SetIdServer extends React.Component {
|
||||||
const InlineSpinner = sdk.getComponent('views.elements.InlineSpinner');
|
const InlineSpinner = sdk.getComponent('views.elements.InlineSpinner');
|
||||||
return <div>
|
return <div>
|
||||||
<InlineSpinner />
|
<InlineSpinner />
|
||||||
{ _t("Checking Server") }
|
{ _t("Checking server") }
|
||||||
</div>;
|
</div>;
|
||||||
} else if (this.state.error) {
|
} else if (this.state.error) {
|
||||||
return this.state.error;
|
return this.state.error;
|
||||||
|
@ -127,18 +125,21 @@ export default class SetIdServer extends React.Component {
|
||||||
return !!this.state.idServer && !this.state.busy;
|
return !!this.state.idServer && !this.state.busy;
|
||||||
};
|
};
|
||||||
|
|
||||||
_saveIdServer = async () => {
|
_saveIdServer = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
this.setState({busy: true});
|
this.setState({busy: true});
|
||||||
|
|
||||||
const fullUrl = unabbreviateUrl(this.state.idServer);
|
const fullUrl = unabbreviateUrl(this.state.idServer);
|
||||||
|
|
||||||
const errStr = await checkIsUrl(fullUrl);
|
const errStr = await checkIdentityServerUrl(fullUrl);
|
||||||
|
|
||||||
let newFormValue = this.state.idServer;
|
let newFormValue = this.state.idServer;
|
||||||
if (!errStr) {
|
if (!errStr) {
|
||||||
MatrixClientPeg.get().setIdentityServerUrl(fullUrl);
|
MatrixClientPeg.get().setIdentityServerUrl(fullUrl);
|
||||||
localStorage.removeItem("mx_is_access_token");
|
localStorage.removeItem("mx_is_access_token");
|
||||||
localStorage.setItem("mx_is_url", fullUrl);
|
localStorage.setItem("mx_is_url", fullUrl);
|
||||||
|
dis.dispatch({action: 'id_server_changed'});
|
||||||
newFormValue = '';
|
newFormValue = '';
|
||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -149,7 +150,49 @@ export default class SetIdServer extends React.Component {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_onDisconnectClicked = () => {
|
||||||
|
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||||
|
Modal.createTrackedDialog('Identity Server Disconnect Warning', '', QuestionDialog, {
|
||||||
|
title: _t("Disconnect Identity Server"),
|
||||||
|
description:
|
||||||
|
<div>
|
||||||
|
{_t(
|
||||||
|
"Disconnect from the identity server <idserver />?", {},
|
||||||
|
{idserver: sub => <b>{abbreviateUrl(this.state.currentClientIdServer)}</b>},
|
||||||
|
)},
|
||||||
|
</div>,
|
||||||
|
button: _t("Disconnect"),
|
||||||
|
onFinished: (confirmed) => {
|
||||||
|
if (confirmed) {
|
||||||
|
this._disconnectIdServer();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
_disconnectIdServer = () => {
|
||||||
|
MatrixClientPeg.get().setIdentityServerUrl(null);
|
||||||
|
localStorage.removeItem("mx_is_access_token");
|
||||||
|
localStorage.removeItem("mx_is_url");
|
||||||
|
|
||||||
|
let newFieldVal = '';
|
||||||
|
if (SdkConfig.get()['validated_server_config']['isUrl']) {
|
||||||
|
// Prepopulate the client's default so the user at least has some idea of
|
||||||
|
// a valid value they might enter
|
||||||
|
newFieldVal = abbreviateUrl(SdkConfig.get()['validated_server_config']['isUrl']);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
busy: false,
|
||||||
|
error: null,
|
||||||
|
currentClientIdServer: MatrixClientPeg.get().getIdentityServerUrl(),
|
||||||
|
idServer: newFieldVal,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const AccessibleButton = sdk.getComponent('views.elements.AccessibleButton');
|
||||||
|
const Field = sdk.getComponent('elements.Field');
|
||||||
const idServerUrl = this.state.currentClientIdServer;
|
const idServerUrl = this.state.currentClientIdServer;
|
||||||
let sectionTitle;
|
let sectionTitle;
|
||||||
let bodyText;
|
let bodyText;
|
||||||
|
@ -164,12 +207,26 @@ export default class SetIdServer extends React.Component {
|
||||||
} else {
|
} else {
|
||||||
sectionTitle = _t("Identity Server");
|
sectionTitle = _t("Identity Server");
|
||||||
bodyText = _t(
|
bodyText = _t(
|
||||||
"You are not currently using an Identity Server. " +
|
"You are not currently using an identity server. " +
|
||||||
"To discover and be discoverable by existing contacts you know, " +
|
"To discover and be discoverable by existing contacts you know, " +
|
||||||
"add one below",
|
"add one below.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let discoSection;
|
||||||
|
if (idServerUrl) {
|
||||||
|
discoSection = <div>
|
||||||
|
<span className="mx_SettingsTab_subsectionText">{_t(
|
||||||
|
"Disconnecting from your identity server will mean you " +
|
||||||
|
"won't be discoverable by other users and you won't be " +
|
||||||
|
"able to invite others by email or phone.",
|
||||||
|
)}</span>
|
||||||
|
<AccessibleButton onClick={this._onDisconnectClicked} kind="danger">
|
||||||
|
{_t("Disconnect")}
|
||||||
|
</AccessibleButton>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form className="mx_SettingsTab_section mx_SetIdServer" onSubmit={this._saveIdServer}>
|
<form className="mx_SettingsTab_section mx_SetIdServer" onSubmit={this._saveIdServer}>
|
||||||
<span className="mx_SettingsTab_subheading">
|
<span className="mx_SettingsTab_subheading">
|
||||||
|
@ -182,12 +239,13 @@ export default class SetIdServer extends React.Component {
|
||||||
id="mx_SetIdServer_idServer"
|
id="mx_SetIdServer_idServer"
|
||||||
type="text" value={this.state.idServer} autoComplete="off"
|
type="text" value={this.state.idServer} autoComplete="off"
|
||||||
onChange={this._onIdentityServerChanged}
|
onChange={this._onIdentityServerChanged}
|
||||||
tooltip={this._getTooltip()}
|
tooltipContent={this._getTooltip()}
|
||||||
/>
|
/>
|
||||||
<input className="mx_Dialog_primary"
|
<AccessibleButton type="submit" kind="primary_sm"
|
||||||
type="submit" value={_t("Change")}
|
onClick={this._saveIdServer}
|
||||||
disabled={!this._idServerChangeEnabled()}
|
disabled={!this._idServerChangeEnabled()}
|
||||||
/>
|
>{_t("Change")}</AccessibleButton>
|
||||||
|
{discoSection}
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,9 +45,22 @@ export default class GeneralUserSettingsTab extends React.Component {
|
||||||
this.state = {
|
this.state = {
|
||||||
language: languageHandler.getCurrentLanguage(),
|
language: languageHandler.getCurrentLanguage(),
|
||||||
theme: SettingsStore.getValueAt(SettingLevel.ACCOUNT, "theme"),
|
theme: SettingsStore.getValueAt(SettingLevel.ACCOUNT, "theme"),
|
||||||
|
haveIdServer: Boolean(MatrixClientPeg.get().getIdentityServerUrl()),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.dispatcherRef = dis.register(this._onAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
dis.unregister(this.dispatcherRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onAction = (payload) => {
|
||||||
|
if (payload.action === 'id_server_changed') {
|
||||||
|
this.setState({haveIdServer: Boolean(MatrixClientPeg.get().getIdentityServerUrl())});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
_onLanguageChange = (newLanguage) => {
|
_onLanguageChange = (newLanguage) => {
|
||||||
if (this.state.language === newLanguage) return;
|
if (this.state.language === newLanguage) return;
|
||||||
|
|
||||||
|
@ -124,7 +137,7 @@ export default class GeneralUserSettingsTab extends React.Component {
|
||||||
onFinished={this._onPasswordChanged} />
|
onFinished={this._onPasswordChanged} />
|
||||||
);
|
);
|
||||||
|
|
||||||
const threepidSection = MatrixClientPeg.get().getIdentityServerUrl() ? <div>
|
const threepidSection = this.state.haveIdServer ? <div>
|
||||||
<span className="mx_SettingsTab_subheading">{_t("Email addresses")}</span>
|
<span className="mx_SettingsTab_subheading">{_t("Email addresses")}</span>
|
||||||
<EmailAddresses />
|
<EmailAddresses />
|
||||||
|
|
||||||
|
|
|
@ -540,13 +540,17 @@
|
||||||
"Display Name": "Display Name",
|
"Display Name": "Display Name",
|
||||||
"Save": "Save",
|
"Save": "Save",
|
||||||
"Identity Server URL must be HTTPS": "Identity Server URL must be HTTPS",
|
"Identity Server URL must be HTTPS": "Identity Server URL must be HTTPS",
|
||||||
"Could not connect to ID Server": "Could not connect to ID Server",
|
"Not a valid Identity Server (status code %(code)s)": "Not a valid Identity Server (status code %(code)s)",
|
||||||
"Not a valid ID Server (status code %(code)s)": "Not a valid ID Server (status code %(code)s)",
|
"Could not connect to Identity Server": "Could not connect to Identity Server",
|
||||||
"Checking Server": "Checking Server",
|
"Checking server": "Checking server",
|
||||||
|
"Disconnect Identity Server": "Disconnect Identity Server",
|
||||||
|
"Disconnect from the identity server <idserver />?": "Disconnect from the identity server <idserver />?",
|
||||||
|
"Disconnect": "Disconnect",
|
||||||
"Identity Server (%(server)s)": "Identity Server (%(server)s)",
|
"Identity Server (%(server)s)": "Identity Server (%(server)s)",
|
||||||
"You are currently using <server></server> to discover and be discoverable by existing contacts you know. You can change your identity server below.": "You are currently using <server></server> to discover and be discoverable by existing contacts you know. You can change your identity server below.",
|
"You are currently using <server></server> to discover and be discoverable by existing contacts you know. You can change your identity server below.": "You are currently using <server></server> to discover and be discoverable by existing contacts you know. You can change your identity server below.",
|
||||||
"Identity Server": "Identity Server",
|
"Identity Server": "Identity Server",
|
||||||
"You are not currently using an Identity Server. To discover and be discoverable by existing contacts you know, add one below": "You are not currently using an Identity Server. To discover and be discoverable by existing contacts you know, add one below",
|
"You are not currently using an identity server. To discover and be discoverable by existing contacts you know, add one below.": "You are not currently using an identity server. To discover and be discoverable by existing contacts you know, add one below.",
|
||||||
|
"Disconnecting from your identity server will mean you won't be discoverable by other users and you won't be able to invite others by email or phone.": "Disconnecting from your identity server will mean you won't be discoverable by other users and you won't be able to invite others by email or phone.",
|
||||||
"Change": "Change",
|
"Change": "Change",
|
||||||
"Checking server": "Checking server",
|
"Checking server": "Checking server",
|
||||||
"Integration manager offline or not accessible.": "Integration manager offline or not accessible.",
|
"Integration manager offline or not accessible.": "Integration manager offline or not accessible.",
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
Copyright 2017 MTRNord and Cooperative EITA
|
Copyright 2017 MTRNord and Cooperative EITA
|
||||||
Copyright 2017 Vector Creations Ltd.
|
Copyright 2017 Vector Creations Ltd.
|
||||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
||||||
|
|
||||||
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.
|
||||||
|
@ -102,7 +103,7 @@ function safeCounterpartTranslate(text, options) {
|
||||||
* @return a React <span> component if any non-strings were used in substitutions, otherwise a string
|
* @return a React <span> component if any non-strings were used in substitutions, otherwise a string
|
||||||
*/
|
*/
|
||||||
export function _t(text, variables, tags) {
|
export function _t(text, variables, tags) {
|
||||||
// Don't do subsitutions in counterpart. We handle it ourselves so we can replace with React components
|
// Don't do substitutions in counterpart. We handle it ourselves so we can replace with React components
|
||||||
// However, still pass the variables to counterpart so that it can choose the correct plural if count is given
|
// However, still pass the variables to counterpart so that it can choose the correct plural if count is given
|
||||||
// It is enough to pass the count variable, but in the future counterpart might make use of other information too
|
// It is enough to pass the count variable, but in the future counterpart might make use of other information too
|
||||||
const args = Object.assign({ interpolate: false }, variables);
|
const args = Object.assign({ interpolate: false }, variables);
|
||||||
|
@ -289,7 +290,7 @@ export function setLanguage(preferredLangs) {
|
||||||
console.log("set language to " + langToUse);
|
console.log("set language to " + langToUse);
|
||||||
|
|
||||||
// Set 'en' as fallback language:
|
// Set 'en' as fallback language:
|
||||||
if (langToUse != "en") {
|
if (langToUse !== "en") {
|
||||||
return getLanguage(i18nFolder + availLangs['en'].fileName);
|
return getLanguage(i18nFolder + availLangs['en'].fileName);
|
||||||
}
|
}
|
||||||
}).then((langData) => {
|
}).then((langData) => {
|
||||||
|
@ -329,13 +330,13 @@ export function getLanguagesFromBrowser() {
|
||||||
*/
|
*/
|
||||||
export function getNormalizedLanguageKeys(language) {
|
export function getNormalizedLanguageKeys(language) {
|
||||||
const languageKeys = [];
|
const languageKeys = [];
|
||||||
const normalizedLanguage = this.normalizeLanguageKey(language);
|
const normalizedLanguage = normalizeLanguageKey(language);
|
||||||
const languageParts = normalizedLanguage.split('-');
|
const languageParts = normalizedLanguage.split('-');
|
||||||
if (languageParts.length == 2 && languageParts[0] == languageParts[1]) {
|
if (languageParts.length === 2 && languageParts[0] === languageParts[1]) {
|
||||||
languageKeys.push(languageParts[0]);
|
languageKeys.push(languageParts[0]);
|
||||||
} else {
|
} else {
|
||||||
languageKeys.push(normalizedLanguage);
|
languageKeys.push(normalizedLanguage);
|
||||||
if (languageParts.length == 2) {
|
if (languageParts.length === 2) {
|
||||||
languageKeys.push(languageParts[0]);
|
languageKeys.push(languageParts[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -345,6 +346,9 @@ export function getNormalizedLanguageKeys(language) {
|
||||||
/**
|
/**
|
||||||
* Returns a language string with underscores replaced with
|
* Returns a language string with underscores replaced with
|
||||||
* hyphens, and lowercased.
|
* hyphens, and lowercased.
|
||||||
|
*
|
||||||
|
* @param {string} language The language string to be normalized
|
||||||
|
* @returns {string} The normalized language string
|
||||||
*/
|
*/
|
||||||
export function normalizeLanguageKey(language) {
|
export function normalizeLanguageKey(language) {
|
||||||
return language.toLowerCase().replace("_", "-");
|
return language.toLowerCase().replace("_", "-");
|
||||||
|
@ -373,7 +377,7 @@ export function pickBestLanguage(langs) {
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// Failing that, a different dialect of the same lnguage
|
// Failing that, a different dialect of the same language
|
||||||
const closeLangIndex = normalisedLangs.find((l) => l.substr(0, 2) === currentLang.substr(0, 2));
|
const closeLangIndex = normalisedLangs.find((l) => l.substr(0, 2) === currentLang.substr(0, 2));
|
||||||
if (closeLangIndex > -1) return langs[closeLangIndex];
|
if (closeLangIndex > -1) return langs[closeLangIndex];
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue