Merge remote-tracking branch 'origin/dbkr/change_is' into travis/integs/account_set
This commit is contained in:
commit
e21c12c2c9
7 changed files with 241 additions and 13 deletions
|
@ -168,6 +168,7 @@
|
||||||
@import "./views/settings/_Notifications.scss";
|
@import "./views/settings/_Notifications.scss";
|
||||||
@import "./views/settings/_PhoneNumbers.scss";
|
@import "./views/settings/_PhoneNumbers.scss";
|
||||||
@import "./views/settings/_ProfileSettings.scss";
|
@import "./views/settings/_ProfileSettings.scss";
|
||||||
|
@import "./views/settings/_SetIdServer.scss";
|
||||||
@import "./views/settings/tabs/_SettingsTab.scss";
|
@import "./views/settings/tabs/_SettingsTab.scss";
|
||||||
@import "./views/settings/tabs/room/_GeneralRoomSettingsTab.scss";
|
@import "./views/settings/tabs/room/_GeneralRoomSettingsTab.scss";
|
||||||
@import "./views/settings/tabs/room/_RolesRoomSettingsTab.scss";
|
@import "./views/settings/tabs/room/_RolesRoomSettingsTab.scss";
|
||||||
|
|
|
@ -55,7 +55,7 @@ limitations under the License.
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
box-shadow: 4px 4px 12px 0 $menu-box-shadow-color;
|
box-shadow: 4px 4px 12px 0 $menu-box-shadow-color;
|
||||||
background-color: $menu-bg-color;
|
background-color: $menu-bg-color;
|
||||||
z-index: 2000;
|
z-index: 4000; // Higher than dialogs so tooltips can be used in dialogs
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
line-height: 14px;
|
line-height: 14px;
|
||||||
|
|
19
res/css/views/settings/_SetIdServer.scss
Normal file
19
res/css/views/settings/_SetIdServer.scss
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
Copyright 2019 New Vector Ltd
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.mx_SetIdServer .mx_Field_input {
|
||||||
|
width: 300px;
|
||||||
|
}
|
|
@ -46,6 +46,9 @@ export default class Field extends React.PureComponent {
|
||||||
// and a `feedback` react component field to provide feedback
|
// and a `feedback` react component field to provide feedback
|
||||||
// to the user.
|
// to the user.
|
||||||
onValidate: PropTypes.func,
|
onValidate: PropTypes.func,
|
||||||
|
// If specified, contents will appear as a tooltip on the element and
|
||||||
|
// validation feedback tooltips will be suppressed.
|
||||||
|
tooltip: PropTypes.node,
|
||||||
// All other props pass through to the <input>.
|
// All other props pass through to the <input>.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -135,6 +138,7 @@ export default class Field extends React.PureComponent {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { element, prefix, onValidate, children, ...inputProps } = this.props;
|
const { element, prefix, onValidate, children, ...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";
|
||||||
|
|
||||||
|
@ -165,12 +169,12 @@ 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 tooltip;
|
let fieldTooltip;
|
||||||
if (this.state.feedback) {
|
if (this.props.tooltip || this.state.feedback) {
|
||||||
tooltip = <Tooltip
|
fieldTooltip = <Tooltip
|
||||||
tooltipClassName="mx_Field_tooltip"
|
tooltipClassName="mx_Field_tooltip"
|
||||||
visible={this.state.feedbackVisible}
|
visible={this.state.feedbackVisible}
|
||||||
label={this.state.feedback}
|
label={this.props.tooltip || this.state.feedback}
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,7 +182,7 @@ export default class Field extends React.PureComponent {
|
||||||
{prefixContainer}
|
{prefixContainer}
|
||||||
{fieldInput}
|
{fieldInput}
|
||||||
<label htmlFor={this.props.id}>{this.props.label}</label>
|
<label htmlFor={this.props.id}>{this.props.label}</label>
|
||||||
{tooltip}
|
{fieldTooltip}
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
194
src/components/views/settings/SetIdServer.js
Normal file
194
src/components/views/settings/SetIdServer.js
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
/*
|
||||||
|
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 request from 'browser-request';
|
||||||
|
import url from 'url';
|
||||||
|
import React from 'react';
|
||||||
|
import {_t} from "../../../languageHandler";
|
||||||
|
import sdk from '../../../index';
|
||||||
|
import MatrixClientPeg from "../../../MatrixClientPeg";
|
||||||
|
import SdkConfig from "../../../SdkConfig";
|
||||||
|
import Field from "../elements/Field";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If a url has no path component, etc. abbreviate it to just the hostname
|
||||||
|
*
|
||||||
|
* @param {string} u The url to be abbreviated
|
||||||
|
* @returns {string} The abbreviated url
|
||||||
|
*/
|
||||||
|
function abbreviateUrl(u) {
|
||||||
|
if (!u) return '';
|
||||||
|
|
||||||
|
const parsedUrl = url.parse(u);
|
||||||
|
// if it's something we can't parse as a url then just return it
|
||||||
|
if (!parsedUrl) return u;
|
||||||
|
|
||||||
|
if (parsedUrl.path == '/') {
|
||||||
|
// we ignore query / hash parts: these aren't relevant for IS server URLs
|
||||||
|
return parsedUrl.host;
|
||||||
|
}
|
||||||
|
|
||||||
|
return u;
|
||||||
|
}
|
||||||
|
|
||||||
|
function unabbreviateUrl(u) {
|
||||||
|
if (!u) return '';
|
||||||
|
|
||||||
|
let longUrl = u;
|
||||||
|
if (!u.startsWith('https://')) longUrl = 'https://' + u;
|
||||||
|
const parsed = url.parse(longUrl);
|
||||||
|
if (parsed.hostname === null) return u;
|
||||||
|
|
||||||
|
return longUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check an IS URL is valid, including liveness check
|
||||||
|
*
|
||||||
|
* @param {string} isUrl The url to check
|
||||||
|
* @returns {string} null if url passes all checks, otherwise i18ned error string
|
||||||
|
*/
|
||||||
|
async function checkIsUrl(isUrl) {
|
||||||
|
const parsedUrl = url.parse(isUrl);
|
||||||
|
|
||||||
|
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
|
||||||
|
// js-sdk so probably as easy to duplicate it than to separate it out so we can reuse it
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
request(
|
||||||
|
// also XXX: we don't really know whether to hit /v1 or /v2 for this: we
|
||||||
|
// probably want a /versions endpoint like the C/S API.
|
||||||
|
{ 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) {
|
||||||
|
resolve(_t("Not a valid ID Server (status code %(code)s)", {code: response.status}));
|
||||||
|
} else {
|
||||||
|
resolve(null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class SetIdServer extends React.Component {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
let defaultIdServer = abbreviateUrl(MatrixClientPeg.get().getIdentityServerUrl());
|
||||||
|
if (!defaultIdServer) {
|
||||||
|
defaultIdServer = abbreviateUrl(SdkConfig.get()['validated_server_config']['idServer']) || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
currentClientIdServer: MatrixClientPeg.get().getIdentityServerUrl(),
|
||||||
|
idServer: defaultIdServer,
|
||||||
|
error: null,
|
||||||
|
busy: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_onIdentityServerChanged = (ev) => {
|
||||||
|
const u = ev.target.value;
|
||||||
|
|
||||||
|
this.setState({idServer: u});
|
||||||
|
};
|
||||||
|
|
||||||
|
_getTooltip = () => {
|
||||||
|
if (this.state.busy) {
|
||||||
|
const InlineSpinner = sdk.getComponent('views.elements.InlineSpinner');
|
||||||
|
return <div>
|
||||||
|
<InlineSpinner />
|
||||||
|
{ _t("Checking Server") }
|
||||||
|
</div>;
|
||||||
|
} else if (this.state.error) {
|
||||||
|
return this.state.error;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_idServerChangeEnabled = () => {
|
||||||
|
return !!this.state.idServer && !this.state.busy;
|
||||||
|
};
|
||||||
|
|
||||||
|
_saveIdServer = async () => {
|
||||||
|
this.setState({busy: true});
|
||||||
|
|
||||||
|
const fullUrl = unabbreviateUrl(this.state.idServer);
|
||||||
|
|
||||||
|
const errStr = await checkIsUrl(fullUrl);
|
||||||
|
|
||||||
|
let newFormValue = this.state.idServer;
|
||||||
|
if (!errStr) {
|
||||||
|
MatrixClientPeg.get().setIdentityServerUrl(fullUrl);
|
||||||
|
localStorage.removeItem("mx_is_access_token");
|
||||||
|
localStorage.setItem("mx_is_url", fullUrl);
|
||||||
|
newFormValue = '';
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
busy: false,
|
||||||
|
error: errStr,
|
||||||
|
currentClientIdServer: MatrixClientPeg.get().getIdentityServerUrl(),
|
||||||
|
idServer: newFormValue,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const idServerUrl = this.state.currentClientIdServer;
|
||||||
|
let sectionTitle;
|
||||||
|
let bodyText;
|
||||||
|
if (idServerUrl) {
|
||||||
|
sectionTitle = _t("Identity Server (%(server)s)", { server: abbreviateUrl(idServerUrl) });
|
||||||
|
bodyText = _t(
|
||||||
|
"You are currently using <server></server> to discover and be discoverable by " +
|
||||||
|
"existing contacts you know. You can change your identity server below.",
|
||||||
|
{},
|
||||||
|
{ server: sub => <b>{abbreviateUrl(idServerUrl)}</b> },
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
sectionTitle = _t("Identity Server");
|
||||||
|
bodyText = _t(
|
||||||
|
"You are not currently using an Identity Server. " +
|
||||||
|
"To discover and be discoverable by existing contacts you know, " +
|
||||||
|
"add one below",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form className="mx_SettingsTab_section mx_SetIdServer" onSubmit={this._saveIdServer}>
|
||||||
|
<span className="mx_SettingsTab_subheading">
|
||||||
|
{sectionTitle}
|
||||||
|
</span>
|
||||||
|
<span className="mx_SettingsTab_subsectionText">
|
||||||
|
{bodyText}
|
||||||
|
</span>
|
||||||
|
<Field label={_t("Identity Server")}
|
||||||
|
id="mx_SetIdServer_idServer"
|
||||||
|
type="text" value={this.state.idServer} autoComplete="off"
|
||||||
|
onChange={this._onIdentityServerChanged}
|
||||||
|
tooltip={this._getTooltip()}
|
||||||
|
/>
|
||||||
|
<input className="mx_Dialog_primary"
|
||||||
|
type="submit" value={_t("Change")}
|
||||||
|
disabled={!this._idServerChangeEnabled()}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,11 +28,11 @@ import AccessibleButton from "../../../elements/AccessibleButton";
|
||||||
import DeactivateAccountDialog from "../../../dialogs/DeactivateAccountDialog";
|
import DeactivateAccountDialog from "../../../dialogs/DeactivateAccountDialog";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import {THEMES} from "../../../../../themes";
|
import {THEMES} from "../../../../../themes";
|
||||||
const PlatformPeg = require("../../../../../PlatformPeg");
|
import PlatformPeg from "../../../../../PlatformPeg";
|
||||||
const MatrixClientPeg = require("../../../../../MatrixClientPeg");
|
import MatrixClientPeg from "../../../../../MatrixClientPeg";
|
||||||
const sdk = require('../../../../..');
|
import sdk from "../../../../..";
|
||||||
const Modal = require("../../../../../Modal");
|
import Modal from "../../../../../Modal";
|
||||||
const dis = require("../../../../../dispatcher");
|
import dis from "../../../../../dispatcher";
|
||||||
|
|
||||||
export default class GeneralUserSettingsTab extends React.Component {
|
export default class GeneralUserSettingsTab extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -174,6 +174,7 @@ export default class GeneralUserSettingsTab extends React.Component {
|
||||||
_renderDiscoverySection() {
|
_renderDiscoverySection() {
|
||||||
const EmailAddresses = sdk.getComponent("views.settings.discovery.EmailAddresses");
|
const EmailAddresses = sdk.getComponent("views.settings.discovery.EmailAddresses");
|
||||||
const PhoneNumbers = sdk.getComponent("views.settings.discovery.PhoneNumbers");
|
const PhoneNumbers = sdk.getComponent("views.settings.discovery.PhoneNumbers");
|
||||||
|
const SetIdServer = sdk.getComponent("views.settings.SetIdServer");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_SettingsTab_section">
|
<div className="mx_SettingsTab_section">
|
||||||
|
@ -182,6 +183,8 @@ export default class GeneralUserSettingsTab extends React.Component {
|
||||||
|
|
||||||
<span className="mx_SettingsTab_subheading">{_t("Phone numbers")}</span>
|
<span className="mx_SettingsTab_subheading">{_t("Phone numbers")}</span>
|
||||||
<PhoneNumbers />
|
<PhoneNumbers />
|
||||||
|
{ /* has its own heading as it includes the current ID server */ }
|
||||||
|
<SetIdServer />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -539,6 +539,15 @@
|
||||||
"<a>Upgrade</a> to your own domain": "<a>Upgrade</a> to your own domain",
|
"<a>Upgrade</a> to your own domain": "<a>Upgrade</a> to your own domain",
|
||||||
"Display Name": "Display Name",
|
"Display Name": "Display Name",
|
||||||
"Save": "Save",
|
"Save": "Save",
|
||||||
|
"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 ID Server (status code %(code)s)": "Not a valid ID Server (status code %(code)s)",
|
||||||
|
"Checking Server": "Checking Server",
|
||||||
|
"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.",
|
||||||
|
"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",
|
||||||
|
"Change": "Change",
|
||||||
"Flair": "Flair",
|
"Flair": "Flair",
|
||||||
"Failed to change password. Is your password correct?": "Failed to change password. Is your password correct?",
|
"Failed to change password. Is your password correct?": "Failed to change password. Is your password correct?",
|
||||||
"Success": "Success",
|
"Success": "Success",
|
||||||
|
@ -1276,7 +1285,6 @@
|
||||||
"Missing session data": "Missing session data",
|
"Missing session data": "Missing session data",
|
||||||
"Some session data, including encrypted message keys, is missing. Sign out and sign in to fix this, restoring keys from backup.": "Some session data, including encrypted message keys, is missing. Sign out and sign in to fix this, restoring keys from backup.",
|
"Some session data, including encrypted message keys, is missing. Sign out and sign in to fix this, restoring keys from backup.": "Some session data, including encrypted message keys, is missing. Sign out and sign in to fix this, restoring keys from backup.",
|
||||||
"Your browser likely removed this data when running low on disk space.": "Your browser likely removed this data when running low on disk space.",
|
"Your browser likely removed this data when running low on disk space.": "Your browser likely removed this data when running low on disk space.",
|
||||||
"Identity Server": "Identity Server",
|
|
||||||
"Integrations Manager": "Integrations Manager",
|
"Integrations Manager": "Integrations Manager",
|
||||||
"Find others by phone or email": "Find others by phone or email",
|
"Find others by phone or email": "Find others by phone or email",
|
||||||
"Be found by phone or email": "Be found by phone or email",
|
"Be found by phone or email": "Be found by phone or email",
|
||||||
|
@ -1396,7 +1404,6 @@
|
||||||
"Not sure of your password? <a>Set a new one</a>": "Not sure of your password? <a>Set a new one</a>",
|
"Not sure of your password? <a>Set a new one</a>": "Not sure of your password? <a>Set a new one</a>",
|
||||||
"Sign in to your Matrix account on %(serverName)s": "Sign in to your Matrix account on %(serverName)s",
|
"Sign in to your Matrix account on %(serverName)s": "Sign in to your Matrix account on %(serverName)s",
|
||||||
"Sign in to your Matrix account on <underlinedServerName />": "Sign in to your Matrix account on <underlinedServerName />",
|
"Sign in to your Matrix account on <underlinedServerName />": "Sign in to your Matrix account on <underlinedServerName />",
|
||||||
"Change": "Change",
|
|
||||||
"Sign in with": "Sign in with",
|
"Sign in with": "Sign in with",
|
||||||
"If you don't specify an email address, you won't be able to reset your password. Are you sure?": "If you don't specify an email address, you won't be able to reset your password. Are you sure?",
|
"If you don't specify an email address, you won't be able to reset your password. Are you sure?": "If you don't specify an email address, you won't be able to reset your password. Are you sure?",
|
||||||
"No Identity Server is configured so you cannot add add an email address in order to reset your password in the future.": "No Identity Server is configured so you cannot add add an email address in order to reset your password in the future.",
|
"No Identity Server is configured so you cannot add add an email address in order to reset your password in the future.": "No Identity Server is configured so you cannot add add an email address in order to reset your password in the future.",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue