Merge branch 'develop' into jryans/4s-new-key-backup
This commit is contained in:
commit
72f0401589
142 changed files with 2121 additions and 807 deletions
|
@ -112,7 +112,7 @@ module.exports = createReactClass({
|
|||
}
|
||||
});
|
||||
|
||||
httpPromise.done(function() {
|
||||
httpPromise.then(function() {
|
||||
self.setState({
|
||||
phase: self.Phases.Display,
|
||||
avatarUrl: MatrixClientPeg.get().mxcUrlToHttp(newUrl),
|
||||
|
|
|
@ -25,7 +25,6 @@ const Modal = require("../../../Modal");
|
|||
const sdk = require("../../../index");
|
||||
|
||||
import dis from "../../../dispatcher";
|
||||
import Promise from 'bluebird';
|
||||
import AccessibleButton from '../elements/AccessibleButton';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
|
@ -174,7 +173,7 @@ module.exports = createReactClass({
|
|||
newPassword: "",
|
||||
newPasswordConfirm: "",
|
||||
});
|
||||
}).done();
|
||||
});
|
||||
},
|
||||
|
||||
_optionallySetEmail: function() {
|
||||
|
|
|
@ -52,7 +52,7 @@ export default class DevicesPanel extends React.Component {
|
|||
}
|
||||
|
||||
_loadDevices() {
|
||||
MatrixClientPeg.get().getDevices().done(
|
||||
MatrixClientPeg.get().getDevices().then(
|
||||
(resp) => {
|
||||
if (this._unmounted) { return; }
|
||||
this.setState({devices: resp.devices || []});
|
||||
|
|
|
@ -21,12 +21,9 @@ import sdk from '../../../index';
|
|||
import { _t } from '../../../languageHandler';
|
||||
import dis from '../../../dispatcher';
|
||||
|
||||
export default class IntegrationsManager extends React.Component {
|
||||
export default class IntegrationManager extends React.Component {
|
||||
static propTypes = {
|
||||
// false to display an error saying that there is no integrations manager configured
|
||||
configured: PropTypes.bool.isRequired,
|
||||
|
||||
// false to display an error saying that we couldn't connect to the integrations manager
|
||||
// false to display an error saying that we couldn't connect to the integration manager
|
||||
connected: PropTypes.bool.isRequired,
|
||||
|
||||
// true to display a loading spinner
|
||||
|
@ -40,7 +37,6 @@ export default class IntegrationsManager extends React.Component {
|
|||
};
|
||||
|
||||
static defaultProps = {
|
||||
configured: true,
|
||||
connected: true,
|
||||
loading: false,
|
||||
};
|
||||
|
@ -70,20 +66,11 @@ export default class IntegrationsManager extends React.Component {
|
|||
};
|
||||
|
||||
render() {
|
||||
if (!this.props.configured) {
|
||||
return (
|
||||
<div className='mx_IntegrationsManager_error'>
|
||||
<h3>{_t("No integrations server configured")}</h3>
|
||||
<p>{_t("This Riot instance does not have an integrations server configured.")}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.props.loading) {
|
||||
const Spinner = sdk.getComponent("elements.Spinner");
|
||||
return (
|
||||
<div className='mx_IntegrationsManager_loading'>
|
||||
<h3>{_t("Connecting to integrations server...")}</h3>
|
||||
<div className='mx_IntegrationManager_loading'>
|
||||
<h3>{_t("Connecting to integration manager...")}</h3>
|
||||
<Spinner />
|
||||
</div>
|
||||
);
|
||||
|
@ -91,9 +78,9 @@ export default class IntegrationsManager extends React.Component {
|
|||
|
||||
if (!this.props.connected) {
|
||||
return (
|
||||
<div className='mx_IntegrationsManager_error'>
|
||||
<h3>{_t("Cannot connect to integrations server")}</h3>
|
||||
<p>{_t("The integrations server is offline or it cannot reach your homeserver.")}</p>
|
||||
<div className='mx_IntegrationManager_error'>
|
||||
<h3>{_t("Cannot connect to integration manager")}</h3>
|
||||
<p>{_t("The integration manager is offline or it cannot reach your homeserver.")}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -16,7 +16,6 @@ limitations under the License.
|
|||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import Promise from 'bluebird';
|
||||
import sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
|
@ -97,7 +96,7 @@ module.exports = createReactClass({
|
|||
phase: this.phases.LOADING,
|
||||
});
|
||||
|
||||
MatrixClientPeg.get().setPushRuleEnabled('global', self.state.masterPushRule.kind, self.state.masterPushRule.rule_id, !checked).done(function() {
|
||||
MatrixClientPeg.get().setPushRuleEnabled('global', self.state.masterPushRule.kind, self.state.masterPushRule.rule_id, !checked).then(function() {
|
||||
self._refreshFromServer();
|
||||
});
|
||||
},
|
||||
|
@ -170,7 +169,7 @@ module.exports = createReactClass({
|
|||
emailPusher.kind = null;
|
||||
emailPusherPromise = MatrixClientPeg.get().setPusher(emailPusher);
|
||||
}
|
||||
emailPusherPromise.done(() => {
|
||||
emailPusherPromise.then(() => {
|
||||
this._refreshFromServer();
|
||||
}, (error) => {
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
|
@ -274,7 +273,7 @@ module.exports = createReactClass({
|
|||
}
|
||||
}
|
||||
|
||||
Promise.all(deferreds).done(function() {
|
||||
Promise.all(deferreds).then(function() {
|
||||
self._refreshFromServer();
|
||||
}, function(error) {
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
|
@ -343,7 +342,7 @@ module.exports = createReactClass({
|
|||
}
|
||||
}
|
||||
|
||||
Promise.all(deferreds).done(function(resps) {
|
||||
Promise.all(deferreds).then(function(resps) {
|
||||
self._refreshFromServer();
|
||||
}, function(error) {
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
|
@ -398,7 +397,7 @@ module.exports = createReactClass({
|
|||
};
|
||||
|
||||
// Then, add the new ones
|
||||
Promise.all(removeDeferreds).done(function(resps) {
|
||||
Promise.all(removeDeferreds).then(function(resps) {
|
||||
const deferreds = [];
|
||||
|
||||
let pushRuleVectorStateKind = self.state.vectorContentRules.vectorState;
|
||||
|
@ -434,7 +433,7 @@ module.exports = createReactClass({
|
|||
}
|
||||
}
|
||||
|
||||
Promise.all(deferreds).done(function(resps) {
|
||||
Promise.all(deferreds).then(function(resps) {
|
||||
self._refreshFromServer();
|
||||
}, onError);
|
||||
}, onError);
|
||||
|
@ -650,7 +649,7 @@ module.exports = createReactClass({
|
|||
externalContentRules: self.state.externalContentRules,
|
||||
externalPushRules: self.state.externalPushRules,
|
||||
});
|
||||
}).done();
|
||||
});
|
||||
|
||||
MatrixClientPeg.get().getThreePids().then((r) => this.setState({threepids: r.threepids}));
|
||||
},
|
||||
|
|
|
@ -16,13 +16,9 @@ limitations under the License.
|
|||
|
||||
import React from 'react';
|
||||
import {_t} from "../../../languageHandler";
|
||||
import sdk from '../../../index';
|
||||
import Field from "../elements/Field";
|
||||
import {IntegrationManagers} from "../../../integrations/IntegrationManagers";
|
||||
import MatrixClientPeg from "../../../MatrixClientPeg";
|
||||
import {SERVICE_TYPES} from "matrix-js-sdk";
|
||||
import {IntegrationManagerInstance} from "../../../integrations/IntegrationManagerInstance";
|
||||
import Modal from "../../../Modal";
|
||||
import sdk from '../../../index';
|
||||
import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
|
||||
|
||||
export default class SetIntegrationManager extends React.Component {
|
||||
constructor() {
|
||||
|
@ -32,135 +28,23 @@ export default class SetIntegrationManager extends React.Component {
|
|||
|
||||
this.state = {
|
||||
currentManager,
|
||||
url: "", // user-entered text
|
||||
error: null,
|
||||
busy: false,
|
||||
checking: false,
|
||||
provisioningEnabled: SettingsStore.getValue("integrationProvisioning"),
|
||||
};
|
||||
}
|
||||
|
||||
_onUrlChanged = (ev) => {
|
||||
const u = ev.target.value;
|
||||
this.setState({url: u});
|
||||
};
|
||||
onProvisioningToggled = () => {
|
||||
const current = this.state.provisioningEnabled;
|
||||
SettingsStore.setValue("integrationProvisioning", null, SettingLevel.ACCOUNT, !current).catch(err => {
|
||||
console.error("Error changing integration manager provisioning");
|
||||
console.error(err);
|
||||
|
||||
_getTooltip = () => {
|
||||
if (this.state.checking) {
|
||||
const InlineSpinner = sdk.getComponent('views.elements.InlineSpinner');
|
||||
return <div>
|
||||
<InlineSpinner />
|
||||
{ _t("Checking server") }
|
||||
</div>;
|
||||
} else if (this.state.error) {
|
||||
return <span className="warning">{this.state.error}</span>;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
_canChange = () => {
|
||||
return !!this.state.url && !this.state.busy;
|
||||
};
|
||||
|
||||
_continueTerms = async (manager) => {
|
||||
try {
|
||||
await IntegrationManagers.sharedInstance().overwriteManagerOnAccount(manager);
|
||||
this.setState({
|
||||
busy: false,
|
||||
error: null,
|
||||
currentManager: IntegrationManagers.sharedInstance().getPrimaryManager(),
|
||||
url: "", // clear input
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
this.setState({
|
||||
busy: false,
|
||||
error: _t("Failed to update integration manager"),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
_setManager = async (ev) => {
|
||||
// Don't reload the page when the user hits enter in the form.
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
this.setState({busy: true, checking: true, error: null});
|
||||
|
||||
let offline = false;
|
||||
let manager: IntegrationManagerInstance;
|
||||
try {
|
||||
manager = await IntegrationManagers.sharedInstance().tryDiscoverManager(this.state.url);
|
||||
offline = !manager; // no manager implies offline
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
offline = true; // probably a connection error
|
||||
}
|
||||
if (offline) {
|
||||
this.setState({
|
||||
busy: false,
|
||||
checking: false,
|
||||
error: _t("Integration manager offline or not accessible."),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Test the manager (causes terms of service prompt if agreement is needed)
|
||||
// We also cancel the tooltip at this point so it doesn't collide with the dialog.
|
||||
this.setState({checking: false});
|
||||
try {
|
||||
const client = manager.getScalarClient();
|
||||
await client.connect();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
this.setState({
|
||||
busy: false,
|
||||
error: _t("Terms of service not accepted or the integration manager is invalid."),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Specifically request the terms of service to see if there are any.
|
||||
// The above won't trigger a terms of service check if there are no terms to
|
||||
// sign, so when there's no terms at all we need to ensure we tell the user.
|
||||
let hasTerms = true;
|
||||
try {
|
||||
const terms = await MatrixClientPeg.get().getTerms(SERVICE_TYPES.IM, manager.trimmedApiUrl);
|
||||
hasTerms = terms && terms['policies'] && Object.keys(terms['policies']).length > 0;
|
||||
} catch (e) {
|
||||
// Assume errors mean there are no terms. This could be a 404, 500, etc
|
||||
console.error(e);
|
||||
hasTerms = false;
|
||||
}
|
||||
if (!hasTerms) {
|
||||
this.setState({busy: false});
|
||||
const QuestionDialog = sdk.getComponent("views.dialogs.QuestionDialog");
|
||||
Modal.createTrackedDialog('No Terms Warning', '', QuestionDialog, {
|
||||
title: _t("Integration manager has no terms of service"),
|
||||
description: (
|
||||
<div>
|
||||
<span className="warning">
|
||||
{_t("The integration manager you have chosen does not have any terms of service.")}
|
||||
</span>
|
||||
<span>
|
||||
{_t("Only continue if you trust the owner of the server.")}
|
||||
</span>
|
||||
</div>
|
||||
),
|
||||
button: _t("Continue"),
|
||||
onFinished: async (confirmed) => {
|
||||
if (!confirmed) return;
|
||||
this._continueTerms(manager);
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this._continueTerms(manager);
|
||||
this.setState({provisioningEnabled: current});
|
||||
});
|
||||
this.setState({provisioningEnabled: !current});
|
||||
};
|
||||
|
||||
render() {
|
||||
const AccessibleButton = sdk.getComponent('views.elements.AccessibleButton');
|
||||
const ToggleSwitch = sdk.getComponent("views.elements.ToggleSwitch");
|
||||
|
||||
const currentManager = this.state.currentManager;
|
||||
let managerName;
|
||||
|
@ -168,45 +52,32 @@ export default class SetIntegrationManager extends React.Component {
|
|||
if (currentManager) {
|
||||
managerName = `(${currentManager.name})`;
|
||||
bodyText = _t(
|
||||
"You are currently using <b>%(serverName)s</b> to manage your bots, widgets, " +
|
||||
"Use an Integration Manager <b>(%(serverName)s)</b> to manage bots, widgets, " +
|
||||
"and sticker packs.",
|
||||
{serverName: currentManager.name},
|
||||
{ b: sub => <b>{sub}</b> },
|
||||
);
|
||||
} else {
|
||||
bodyText = _t(
|
||||
"Add which integration manager you want to manage your bots, widgets, " +
|
||||
"and sticker packs.",
|
||||
);
|
||||
bodyText = _t("Use an Integration Manager to manage bots, widgets, and sticker packs.");
|
||||
}
|
||||
|
||||
return (
|
||||
<form className="mx_SettingsTab_section mx_SetIntegrationManager" onSubmit={this._setManager}>
|
||||
<div className='mx_SetIntegrationManager'>
|
||||
<div className="mx_SettingsTab_heading">
|
||||
<span>{_t("Integration Manager")}</span>
|
||||
<span>{_t("Manage integrations")}</span>
|
||||
<span className="mx_SettingsTab_subheading">{managerName}</span>
|
||||
<ToggleSwitch checked={this.state.provisioningEnabled} onChange={this.onProvisioningToggled} />
|
||||
</div>
|
||||
<span className="mx_SettingsTab_subsectionText">
|
||||
{bodyText}
|
||||
<br />
|
||||
<br />
|
||||
{_t(
|
||||
"Integration Managers receive configuration data, and can modify widgets, " +
|
||||
"send room invites, and set power levels on your behalf.",
|
||||
)}
|
||||
</span>
|
||||
<Field
|
||||
label={_t("Enter a new integration manager")}
|
||||
id="mx_SetIntegrationManager_newUrl"
|
||||
type="text" value={this.state.url}
|
||||
autoComplete="off"
|
||||
onChange={this._onUrlChanged}
|
||||
tooltipContent={this._getTooltip()}
|
||||
tooltipClassName="mx_SetIntegrationManager_tooltip"
|
||||
disabled={this.state.busy}
|
||||
flagInvalid={!!this.state.error}
|
||||
/>
|
||||
<AccessibleButton
|
||||
kind="primary_sm"
|
||||
type="submit"
|
||||
disabled={!this._canChange()}
|
||||
onClick={this._setManager}
|
||||
>{_t("Change")}</AccessibleButton>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ import LanguageDropdown from "../../../elements/LanguageDropdown";
|
|||
import AccessibleButton from "../../../elements/AccessibleButton";
|
||||
import DeactivateAccountDialog from "../../../dialogs/DeactivateAccountDialog";
|
||||
import PropTypes from "prop-types";
|
||||
import {enumerateThemes} from "../../../../../theme";
|
||||
import {enumerateThemes, ThemeWatcher} from "../../../../../theme";
|
||||
import PlatformPeg from "../../../../../PlatformPeg";
|
||||
import MatrixClientPeg from "../../../../../MatrixClientPeg";
|
||||
import sdk from "../../../../..";
|
||||
|
@ -50,6 +50,7 @@ export default class GeneralUserSettingsTab extends React.Component {
|
|||
this.state = {
|
||||
language: languageHandler.getCurrentLanguage(),
|
||||
theme: SettingsStore.getValueAt(SettingLevel.ACCOUNT, "theme"),
|
||||
useSystemTheme: SettingsStore.getValueAt(SettingLevel.DEVICE, "use_system_theme"),
|
||||
haveIdServer: Boolean(MatrixClientPeg.get().getIdentityServerUrl()),
|
||||
serverSupportsSeparateAddAndBind: null,
|
||||
idServerHasUnsignedTerms: false,
|
||||
|
@ -173,11 +174,29 @@ export default class GeneralUserSettingsTab extends React.Component {
|
|||
const newTheme = e.target.value;
|
||||
if (this.state.theme === newTheme) return;
|
||||
|
||||
SettingsStore.setValue("theme", null, SettingLevel.ACCOUNT, newTheme);
|
||||
// doing getValue in the .catch will still return the value we failed to set,
|
||||
// so remember what the value was before we tried to set it so we can revert
|
||||
const oldTheme = SettingsStore.getValue('theme');
|
||||
SettingsStore.setValue("theme", null, SettingLevel.ACCOUNT, newTheme).catch(() => {
|
||||
dis.dispatch({action: 'recheck_theme'});
|
||||
this.setState({theme: oldTheme});
|
||||
});
|
||||
this.setState({theme: newTheme});
|
||||
dis.dispatch({action: 'set_theme', value: newTheme});
|
||||
// The settings watcher doesn't fire until the echo comes back from the
|
||||
// server, so to make the theme change immediately we need to manually
|
||||
// do the dispatch now
|
||||
// XXX: The local echoed value appears to be unreliable, in particular
|
||||
// when settings custom themes(!) so adding forceTheme to override
|
||||
// the value from settings.
|
||||
dis.dispatch({action: 'recheck_theme', forceTheme: newTheme});
|
||||
};
|
||||
|
||||
_onUseSystemThemeChanged = (checked) => {
|
||||
this.setState({useSystemTheme: checked});
|
||||
dis.dispatch({action: 'recheck_theme'});
|
||||
}
|
||||
|
||||
|
||||
_onPasswordChangeError = (err) => {
|
||||
// TODO: Figure out a design that doesn't involve replacing the current dialog
|
||||
let errMsg = err.error || "";
|
||||
|
@ -288,11 +307,24 @@ export default class GeneralUserSettingsTab extends React.Component {
|
|||
|
||||
_renderThemeSection() {
|
||||
const SettingsFlag = sdk.getComponent("views.elements.SettingsFlag");
|
||||
|
||||
const themeWatcher = new ThemeWatcher();
|
||||
let systemThemeSection;
|
||||
if (themeWatcher.isSystemThemeSupported()) {
|
||||
systemThemeSection = <div>
|
||||
<SettingsFlag name="use_system_theme" level={SettingLevel.DEVICE}
|
||||
onChange={this._onUseSystemThemeChanged}
|
||||
/>
|
||||
</div>;
|
||||
}
|
||||
return (
|
||||
<div className="mx_SettingsTab_section mx_GeneralUserSettingsTab_themeSection">
|
||||
<span className="mx_SettingsTab_subheading">{_t("Theme")}</span>
|
||||
{systemThemeSection}
|
||||
<Field id="theme" label={_t("Theme")} element="select"
|
||||
value={this.state.theme} onChange={this._onThemeChange}>
|
||||
value={this.state.theme} onChange={this._onThemeChange}
|
||||
disabled={this.state.useSystemTheme}
|
||||
>
|
||||
{Object.entries(enumerateThemes()).map(([theme, text]) => {
|
||||
return <option key={theme} value={theme}>{text}</option>;
|
||||
})}
|
||||
|
|
|
@ -75,7 +75,7 @@ export default class HelpUserSettingsTab extends React.Component {
|
|||
// stopping in the middle of the logs.
|
||||
console.log("Clear cache & reload clicked");
|
||||
MatrixClientPeg.get().stopClient();
|
||||
MatrixClientPeg.get().store.deleteAllData().done(() => {
|
||||
MatrixClientPeg.get().store.deleteAllData().then(() => {
|
||||
PlatformPeg.get().reload();
|
||||
});
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue