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

Support multiple integration managers behind a labs flag #3341

Merged
merged 5 commits into from
Aug 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
62 changes: 62 additions & 0 deletions res/css/views/dialogs/_TabbedIntegrationManagerDialog.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
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;
padding: 10px 8px;
margin-right: 5px;
}

.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