Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Commit

Permalink
Support multiple integration managers behind a labs flag
Browse files Browse the repository at this point in the history
  • Loading branch information
turt2live committed Aug 23, 2019
1 parent 602c338 commit b3cda4b
Show file tree
Hide file tree
Showing 13 changed files with 318 additions and 21 deletions.
1 change: 1 addition & 0 deletions res/css/_components.scss
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
@import "./views/dialogs/_SettingsDialog.scss";
@import "./views/dialogs/_ShareDialog.scss";
@import "./views/dialogs/_SlashCommandHelpDialog.scss";
@import "./views/dialogs/_TabbedIntegrationManagerDialog.scss";
@import "./views/dialogs/_TermsDialog.scss";
@import "./views/dialogs/_UnknownDeviceDialog.scss";
@import "./views/dialogs/_UploadConfirmDialog.scss";
Expand Down
67 changes: 67 additions & 0 deletions res/css/views/dialogs/_TabbedIntegrationManagerDialog.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
Copyright 2019 The Matrix.org Foundation C.I.C.
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_TabbedIntegrationManagerDialog .mx_Dialog {
width: 60%;
height: 70%;
overflow: hidden;
padding: 0;
max-width: initial;
max-height: initial;
position: relative;
}

.mx_TabbedIntegrationManagerDialog_container {
// Full size of the dialog, whatever it is
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;

.mx_TabbedIntegrationManagerDialog_currentManager {
width: 100%;
height: 100%;
border-top: 1px solid $accent-color;

iframe {
background-color: #fff;
border: 0;
width: 100%;
height: 100%;
}
}
}

.mx_TabbedIntegrationManagerDialog_tab {
display: inline-block;
border: 1px solid $accent-color;
border-bottom: 0;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
//background-color: $accent-color-50pct;
padding: 10px 8px;
margin-right: 5px;
}

.mx_TabbedIntegrationManagerDialog_tab:first-child {
//margin-left: 8px;
}

.mx_TabbedIntegrationManagerDialog_currentTab {
background-color: $accent-color;
color: $accent-fg-color;
}
19 changes: 14 additions & 5 deletions src/FromWidgetPostMessageApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import ActiveWidgetStore from './stores/ActiveWidgetStore';
import MatrixClientPeg from "./MatrixClientPeg";
import RoomViewStore from "./stores/RoomViewStore";
import {IntegrationManagers} from "./integrations/IntegrationManagers";
import SettingsStore from "./settings/SettingsStore";

const WIDGET_API_VERSION = '0.0.2'; // Current API version
const SUPPORTED_WIDGET_API_VERSIONS = [
Expand Down Expand Up @@ -194,11 +195,19 @@ export default class FromWidgetPostMessageApi {
const integId = (data && data.integId) ? data.integId : null;

// TODO: Open the right integration manager for the widget
IntegrationManagers.sharedInstance().getPrimaryManager().open(
MatrixClientPeg.get().getRoom(RoomViewStore.getRoomId()),
`type_${integType}`,
integId,
);
if (SettingsStore.isFeatureEnabled("feature_many_integration_managers")) {
IntegrationManagers.sharedInstance().openAll(
MatrixClientPeg.get().getRoom(RoomViewStore.getRoomId()),
`type_${integType}`,
integId,
);
} else {
IntegrationManagers.sharedInstance().getPrimaryManager().open(
MatrixClientPeg.get().getRoom(RoomViewStore.getRoomId()),
`type_${integType}`,
integId,
);
}
} else if (action === 'set_always_on_screen') {
// This is a new message: there is no reason to support the deprecated widgetData here
const data = event.data.data;
Expand Down
9 changes: 7 additions & 2 deletions src/ScalarMessaging.js
Original file line number Diff line number Diff line change
Expand Up @@ -548,8 +548,8 @@ const onMessage = function(event) {
// (See https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage)
let configUrl;
try {
// TODO: Support multiple integration managers
configUrl = new URL(IntegrationManagers.sharedInstance().getPrimaryManager().uiUrl);
if (!openManagerUrl) openManagerUrl = IntegrationManagers.sharedInstance().getPrimaryManager().uiUrl;
configUrl = new URL(openManagerUrl);
} catch (e) {
// No integrations UI URL, ignore silently.
return;
Expand Down Expand Up @@ -657,6 +657,7 @@ const onMessage = function(event) {
};

let listenerCount = 0;
let openManagerUrl = null;
module.exports = {
startListening: function() {
if (listenerCount === 0) {
Expand All @@ -679,4 +680,8 @@ module.exports = {
console.error(e);
}
},

setOpenManagerUrl: function(url) {
openManagerUrl = url;
}
};
172 changes: 172 additions & 0 deletions src/components/views/dialogs/TabbedIntegrationManagerDialog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/*
Copyright 2019 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import React from 'react';
import PropTypes from 'prop-types';
import {IntegrationManagers} from "../../../integrations/IntegrationManagers";
import {Room} from "matrix-js-sdk";
import sdk from '../../../index';
import {dialogTermsInteractionCallback, TermsNotSignedError} from "../../../Terms";
import classNames from 'classnames';
import ScalarMessaging from "../../../ScalarMessaging";

export default class TabbedIntegrationManagerDialog extends React.Component {
static propTypes = {
/**
* Called with:
* * success {bool} True if the user accepted any douments, false if cancelled
* * agreedUrls {string[]} List of agreed URLs
*/
onFinished: PropTypes.func.isRequired,

/**
* Optional room where the integration manager should be open to
*/
room: PropTypes.instanceOf(Room),

/**
* Optional screen to open on the integration manager
*/
screen: PropTypes.string,

/**
* Optional integration ID to open in the integration manager
*/
integrationId: PropTypes.string,
};

constructor(props) {
super(props);

this.state = {
managers: IntegrationManagers.sharedInstance().getOrderedManagers(),
busy: true,
currentIndex: 0,
currentConnected: false,
currentLoading: true,
currentScalarClient: null,
};
}

componentDidMount(): void {
this.openManager(0, true);
}

openManager = async (i: number, force = false) => {
if (i === this.state.currentIndex && !force) return;

const manager = this.state.managers[i];
const client = manager.getScalarClient();
this.setState({
busy: true,
currentIndex: i,
currentLoading: true,
currentConnected: false,
currentScalarClient: client,
});

ScalarMessaging.setOpenManagerUrl(manager.uiUrl);

client.setTermsInteractionCallback((policyInfo, agreedUrls) => {
// To avoid visual glitching of two modals stacking briefly, we customise the
// terms dialog sizing when it will appear for the integrations manager so that
// it gets the same basic size as the IM's own modal.
return dialogTermsInteractionCallback(
policyInfo, agreedUrls, 'mx_TermsDialog_forIntegrationsManager',
);
});

try {
await client.connect();
if (!client.hasCredentials()) {
this.setState({
busy: false,
currentLoading: false,
currentConnected: false,
});
} else {
this.setState({
busy: false,
currentLoading: false,
currentConnected: true,
});
}
} catch (e) {
if (e instanceof TermsNotSignedError) {
return;
}

console.error(e);
this.setState({
busy: false,
currentLoading: false,
currentConnected: false,
});
}
};

_renderTabs() {
const AccessibleButton = sdk.getComponent("views.elements.AccessibleButton");
return this.state.managers.map((m, i) => {
const classes = classNames({
'mx_TabbedIntegrationManagerDialog_tab': true,
'mx_TabbedIntegrationManagerDialog_currentTab': this.state.currentIndex === i,
});
return (
<AccessibleButton
className={classes}
onClick={() => this.openManager(i)}
key={`tab_${i}`}
disabled={this.state.busy}
>
{m.name}
</AccessibleButton>
);
})
}

_renderTab() {
const IntegrationsManager = sdk.getComponent("views.settings.IntegrationsManager");
let uiUrl = null;
if (this.state.currentScalarClient) {
uiUrl = this.state.currentScalarClient.getScalarInterfaceUrlForRoom(
this.props.room,
this.props.screen,
this.props.integrationId,
);
}
return <IntegrationsManager
configured={true}
loading={this.state.currentLoading}
connected={this.state.currentConnected}
url={uiUrl}
onFinished={() => {/* no-op */}}
/>;
}

render() {
return (
<div className='mx_TabbedIntegrationManagerDialog_container'>
<div className='mx_TabbedIntegrationManagerDialog_tabs'>
{this._renderTabs()}
</div>
<div className='mx_TabbedIntegrationManagerDialog_currentManager'>
{this._renderTab()}
</div>
</div>
)
}
}
19 changes: 14 additions & 5 deletions src/components/views/elements/AppTile.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import dis from '../../../dispatcher';
import ActiveWidgetStore from '../../../stores/ActiveWidgetStore';
import classNames from 'classnames';
import {IntegrationManagers} from "../../../integrations/IntegrationManagers";
import SettingsStore from "../../../settings/SettingsStore";

const ALLOWED_APP_URL_SCHEMES = ['https:', 'http:'];
const ENABLE_REACT_PERF = false;
Expand Down Expand Up @@ -264,11 +265,19 @@ export default class AppTile extends React.Component {
this.props.onEditClick();
} else {
// TODO: Open the right manager for the widget
IntegrationManagers.sharedInstance().getPrimaryManager().open(
this.props.room,
'type_' + this.props.type,
this.props.id,
);
if (SettingsStore.isFeatureEnabled("feature_many_integration_managers")) {
IntegrationManagers.sharedInstance().openAll(
this.props.room,
'type_' + this.props.type,
this.props.id,
);
} else {
IntegrationManagers.sharedInstance().getPrimaryManager().open(
this.props.room,
'type_' + this.props.type,
this.props.id,
);
}
}
}

Expand Down
7 changes: 6 additions & 1 deletion src/components/views/elements/ManageIntegsButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import PropTypes from 'prop-types';
import sdk from '../../../index';
import { _t } from '../../../languageHandler';
import {IntegrationManagers} from "../../../integrations/IntegrationManagers";
import SettingsStore from "../../../settings/SettingsStore";

export default class ManageIntegsButton extends React.Component {
constructor(props) {
Expand All @@ -33,7 +34,11 @@ export default class ManageIntegsButton extends React.Component {
if (!managers.hasManager()) {
managers.openNoManagerDialog();
} else {
managers.getPrimaryManager().open(this.props.room);
if (SettingsStore.isFeatureEnabled("feature_many_integration_managers")) {
managers.openAll(this.props.room);
} else {
managers.getPrimaryManager().open(this.props.room);
}
}
};

Expand Down
7 changes: 6 additions & 1 deletion src/components/views/rooms/AppsDrawer.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import WidgetUtils from '../../../utils/WidgetUtils';
import WidgetEchoStore from "../../../stores/WidgetEchoStore";
import AccessibleButton from '../elements/AccessibleButton';
import {IntegrationManagers} from "../../../integrations/IntegrationManagers";
import SettingsStore from "../../../settings/SettingsStore";

// The maximum number of widgets that can be added in a room
const MAX_WIDGETS = 2;
Expand Down Expand Up @@ -128,7 +129,11 @@ module.exports = React.createClass({
},

_launchManageIntegrations: function() {
IntegrationManagers.sharedInstance().getPrimaryManager().open(this.props.room, 'add_integ');
if (SettingsStore.isFeatureEnabled("feature_many_integration_managers")) {
IntegrationManagers.sharedInstance().openAll();
} else {
IntegrationManagers.sharedInstance().getPrimaryManager().open(this.props.room, 'add_integ');
}
},

onClickAddWidget: function(e) {
Expand Down
Loading

0 comments on commit b3cda4b

Please sign in to comment.