Merge branch 'travis/integs/base' into travis/integs/widgets
						commit
						4c82d16e34
					
				|  | @ -63,7 +63,7 @@ import SdkConfig from './SdkConfig'; | |||
| import { showUnknownDeviceDialogForCalls } from './cryptodevices'; | ||||
| import WidgetUtils from './utils/WidgetUtils'; | ||||
| import WidgetEchoStore from './stores/WidgetEchoStore'; | ||||
| import ScalarAuthClient from './ScalarAuthClient'; | ||||
| import {IntegrationManagers} from "./integrations/IntegrationManagers"; | ||||
| 
 | ||||
| global.mxCalls = { | ||||
|     //room_id: MatrixCall
 | ||||
|  | @ -348,14 +348,20 @@ async function _startCallApp(roomId, type) { | |||
|     // the state event in anyway, but the resulting widget would then not
 | ||||
|     // work for us. Better that the user knows before everyone else in the
 | ||||
|     // room sees it.
 | ||||
|     const scalarClient = new ScalarAuthClient(); | ||||
|     let haveScalar = false; | ||||
|     try { | ||||
|         await scalarClient.connect(); | ||||
|         haveScalar = scalarClient.hasCredentials(); | ||||
|     } catch (e) { | ||||
|         // fall through
 | ||||
|     const managers = IntegrationManagers.sharedInstance(); | ||||
|     let haveScalar = true; | ||||
|     if (managers.hasManager()) { | ||||
|         try { | ||||
|             const scalarClient = managers.getPrimaryManager().getScalarClient(); | ||||
|             await scalarClient.connect(); | ||||
|             haveScalar = scalarClient.hasCredentials(); | ||||
|         } catch (e) { | ||||
|             // ignore
 | ||||
|         } | ||||
|     } else { | ||||
|         haveScalar = false; | ||||
|     } | ||||
| 
 | ||||
|     if (!haveScalar) { | ||||
|         const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); | ||||
| 
 | ||||
|  | @ -421,7 +427,8 @@ async function _startCallApp(roomId, type) { | |||
|         // URL, but this will at least allow the integration manager to not be hardcoded.
 | ||||
|         widgetUrl = SdkConfig.get().integrations_jitsi_widget_url + '?' + queryString; | ||||
|     } else { | ||||
|         widgetUrl = SdkConfig.get().integrations_rest_url + '/widgets/jitsi.html?' + queryString; | ||||
|         const apiUrl = IntegrationManagers.sharedInstance().getPrimaryManager().apiUrl; | ||||
|         widgetUrl = apiUrl + '/widgets/jitsi.html?' + queryString; | ||||
|     } | ||||
| 
 | ||||
|     const widgetData = { widgetSessionId }; | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ import WidgetMessagingEndpoint from './WidgetMessagingEndpoint'; | |||
| import ActiveWidgetStore from './stores/ActiveWidgetStore'; | ||||
| import MatrixClientPeg from "./MatrixClientPeg"; | ||||
| import RoomViewStore from "./stores/RoomViewStore"; | ||||
| import { showIntegrationsManager } from './integrations/integrations'; | ||||
| import {IntegrationManagers} from "./integrations/IntegrationManagers"; | ||||
| 
 | ||||
| const WIDGET_API_VERSION = '0.0.2'; // Current API version
 | ||||
| const SUPPORTED_WIDGET_API_VERSIONS = [ | ||||
|  | @ -193,11 +193,12 @@ export default class FromWidgetPostMessageApi { | |||
|             const integType = (data && data.integType) ? data.integType : null; | ||||
|             const integId = (data && data.integId) ? data.integId : null; | ||||
| 
 | ||||
|             showIntegrationsManager({ | ||||
|                 room: MatrixClientPeg.get().getRoom(RoomViewStore.getRoomId()), | ||||
|                 screen: 'type_' + integType, | ||||
|                 integrationId: integId, | ||||
|             }); | ||||
|             // TODO: Open the right integration manager for the widget
 | ||||
|             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; | ||||
|  |  | |||
|  | @ -29,20 +29,43 @@ import * as Matrix from 'matrix-js-sdk'; | |||
| // The version of the integration manager API we're intending to work with
 | ||||
| const imApiVersion = "1.1"; | ||||
| 
 | ||||
| class ScalarAuthClient { | ||||
|     constructor() { | ||||
| export default class ScalarAuthClient { | ||||
|     constructor(apiUrl, uiUrl) { | ||||
|         this.apiUrl = apiUrl; | ||||
|         this.uiUrl = uiUrl; | ||||
|         this.scalarToken = null; | ||||
|         // `undefined` to allow `startTermsFlow` to fallback to a default
 | ||||
|         // callback if this is unset.
 | ||||
|         this.termsInteractionCallback = undefined; | ||||
| 
 | ||||
|         // We try and store the token on a per-manager basis, but need a fallback
 | ||||
|         // for the default manager.
 | ||||
|         const configApiUrl = SdkConfig.get()['integrations_rest_url']; | ||||
|         const configUiUrl = SdkConfig.get()['integrations_ui_url']; | ||||
|         this.isDefaultManager = apiUrl === configApiUrl && configUiUrl === uiUrl; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Determines if setting up a ScalarAuthClient is even possible | ||||
|      * @returns {boolean} true if possible, false otherwise. | ||||
|      */ | ||||
|     static isPossible() { | ||||
|         return SdkConfig.get()['integrations_rest_url'] && SdkConfig.get()['integrations_ui_url']; | ||||
|     _writeTokenToStore() { | ||||
|         window.localStorage.setItem("mx_scalar_token_at_" + this.apiUrl, this.scalarToken); | ||||
|         if (this.isDefaultManager) { | ||||
|             // We remove the old token from storage to migrate upwards. This is safe
 | ||||
|             // to do because even if the user switches to /app when this is on /develop
 | ||||
|             // they'll at worst register for a new token.
 | ||||
|             window.localStorage.removeItem("mx_scalar_token"); // no-op when not present
 | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     _readTokenFromStore() { | ||||
|         let token = window.localStorage.getItem("mx_scalar_token_at_" + this.apiUrl); | ||||
|         if (!token && this.isDefaultManager) { | ||||
|             token = window.localStorage.getItem("mx_scalar_token"); | ||||
|         } | ||||
|         return token; | ||||
|     } | ||||
| 
 | ||||
|     _readToken() { | ||||
|         if (this.scalarToken) return this.scalarToken; | ||||
|         return this._readTokenFromStore(); | ||||
|     } | ||||
| 
 | ||||
|     setTermsInteractionCallback(callback) { | ||||
|  | @ -61,8 +84,7 @@ class ScalarAuthClient { | |||
| 
 | ||||
|     // Returns a promise that resolves to a scalar_token string
 | ||||
|     getScalarToken() { | ||||
|         let token = this.scalarToken; | ||||
|         if (!token) token = window.localStorage.getItem("mx_scalar_token"); | ||||
|         const token = this._readToken(); | ||||
| 
 | ||||
|         if (!token) { | ||||
|             return this.registerForToken(); | ||||
|  | @ -78,7 +100,7 @@ class ScalarAuthClient { | |||
|     } | ||||
| 
 | ||||
|     _getAccountName(token) { | ||||
|         const url = SdkConfig.get().integrations_rest_url + "/account"; | ||||
|         const url = this.apiUrl + "/account"; | ||||
| 
 | ||||
|         return new Promise(function(resolve, reject) { | ||||
|             request({ | ||||
|  | @ -111,7 +133,7 @@ class ScalarAuthClient { | |||
|             return token; | ||||
|         }).catch((e) => { | ||||
|             if (e instanceof TermsNotSignedError) { | ||||
|                 console.log("Integrations manager requires new terms to be agreed to"); | ||||
|                 console.log("Integration manager requires new terms to be agreed to"); | ||||
|                 // The terms endpoints are new and so live on standard _matrix prefixes,
 | ||||
|                 // but IM rest urls are currently configured with paths, so remove the
 | ||||
|                 // path from the base URL before passing it to the js-sdk
 | ||||
|  | @ -126,7 +148,7 @@ class ScalarAuthClient { | |||
|                 // Once we've fully transitioned to _matrix URLs, we can give people
 | ||||
|                 // a grace period to update their configs, then use the rest url as
 | ||||
|                 // a regular base url.
 | ||||
|                 const parsedImRestUrl = url.parse(SdkConfig.get().integrations_rest_url); | ||||
|                 const parsedImRestUrl = url.parse(this.apiUrl); | ||||
|                 parsedImRestUrl.path = ''; | ||||
|                 parsedImRestUrl.pathname = ''; | ||||
|                 return startTermsFlow([new Service( | ||||
|  | @ -147,17 +169,18 @@ class ScalarAuthClient { | |||
|         return MatrixClientPeg.get().getOpenIdToken().then((tokenObject) => { | ||||
|             // Now we can send that to scalar and exchange it for a scalar token
 | ||||
|             return this.exchangeForScalarToken(tokenObject); | ||||
|         }).then((tokenObject) => { | ||||
|         }).then((token) => { | ||||
|             // Validate it (this mostly checks to see if the IM needs us to agree to some terms)
 | ||||
|             return this._checkToken(tokenObject); | ||||
|         }).then((tokenObject) => { | ||||
|             window.localStorage.setItem("mx_scalar_token", tokenObject); | ||||
|             return tokenObject; | ||||
|             return this._checkToken(token); | ||||
|         }).then((token) => { | ||||
|             this.scalarToken = token; | ||||
|             this._writeTokenToStore(); | ||||
|             return token; | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     exchangeForScalarToken(openidTokenObject) { | ||||
|         const scalarRestUrl = SdkConfig.get().integrations_rest_url; | ||||
|         const scalarRestUrl = this.apiUrl; | ||||
| 
 | ||||
|         return new Promise(function(resolve, reject) { | ||||
|             request({ | ||||
|  | @ -181,7 +204,7 @@ class ScalarAuthClient { | |||
|     } | ||||
| 
 | ||||
|     getScalarPageTitle(url) { | ||||
|         let scalarPageLookupUrl = SdkConfig.get().integrations_rest_url + '/widgets/title_lookup'; | ||||
|         let scalarPageLookupUrl = this.apiUrl + '/widgets/title_lookup'; | ||||
|         scalarPageLookupUrl = this.getStarterLink(scalarPageLookupUrl); | ||||
|         scalarPageLookupUrl += '&curl=' + encodeURIComponent(url); | ||||
| 
 | ||||
|  | @ -217,7 +240,7 @@ class ScalarAuthClient { | |||
|      * @return {Promise}           Resolves on completion | ||||
|      */ | ||||
|     disableWidgetAssets(widgetType, widgetId) { | ||||
|         let url = SdkConfig.get().integrations_rest_url + '/widgets/set_assets_state'; | ||||
|         let url = this.apiUrl + '/widgets/set_assets_state'; | ||||
|         url = this.getStarterLink(url); | ||||
|         return new Promise((resolve, reject) => { | ||||
|             request({ | ||||
|  | @ -246,7 +269,7 @@ class ScalarAuthClient { | |||
|     getScalarInterfaceUrlForRoom(room, screen, id) { | ||||
|         const roomId = room.roomId; | ||||
|         const roomName = room.name; | ||||
|         let url = SdkConfig.get().integrations_ui_url; | ||||
|         let url = this.uiUrl; | ||||
|         url += "?scalar_token=" + encodeURIComponent(this.scalarToken); | ||||
|         url += "&room_id=" + encodeURIComponent(roomId); | ||||
|         url += "&room_name=" + encodeURIComponent(roomName); | ||||
|  | @ -264,5 +287,3 @@ class ScalarAuthClient { | |||
|         return starterLinkUrl + "?scalar_token=" + encodeURIComponent(this.scalarToken); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| module.exports = ScalarAuthClient; | ||||
|  |  | |||
|  | @ -232,13 +232,13 @@ Example: | |||
| } | ||||
| */ | ||||
| 
 | ||||
| import SdkConfig from './SdkConfig'; | ||||
| import MatrixClientPeg from './MatrixClientPeg'; | ||||
| import { MatrixEvent } from 'matrix-js-sdk'; | ||||
| import dis from './dispatcher'; | ||||
| import WidgetUtils from './utils/WidgetUtils'; | ||||
| import RoomViewStore from './stores/RoomViewStore'; | ||||
| import { _t } from './languageHandler'; | ||||
| import {IntegrationManagers} from "./integrations/IntegrationManagers"; | ||||
| 
 | ||||
| function sendResponse(event, res) { | ||||
|     const data = JSON.parse(JSON.stringify(event.data)); | ||||
|  | @ -548,7 +548,8 @@ const onMessage = function(event) { | |||
|     // (See https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage)
 | ||||
|     let configUrl; | ||||
|     try { | ||||
|         configUrl = new URL(SdkConfig.get().integrations_ui_url); | ||||
|         // TODO: Support multiple integration managers
 | ||||
|         configUrl = new URL(IntegrationManagers.sharedInstance().getPrimaryManager().uiUrl); | ||||
|     } catch (e) { | ||||
|         // No integrations UI URL, ignore silently.
 | ||||
|         return; | ||||
|  |  | |||
|  | @ -22,7 +22,6 @@ import qs from 'querystring'; | |||
| import React from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import MatrixClientPeg from '../../../MatrixClientPeg'; | ||||
| import ScalarAuthClient from '../../../ScalarAuthClient'; | ||||
| import WidgetMessaging from '../../../WidgetMessaging'; | ||||
| import AccessibleButton from './AccessibleButton'; | ||||
| import Modal from '../../../Modal'; | ||||
|  | @ -35,7 +34,7 @@ import WidgetUtils from '../../../utils/WidgetUtils'; | |||
| import dis from '../../../dispatcher'; | ||||
| import ActiveWidgetStore from '../../../stores/ActiveWidgetStore'; | ||||
| import classNames from 'classnames'; | ||||
| import { showIntegrationsManager } from '../../../integrations/integrations'; | ||||
| import {IntegrationManagers} from "../../../integrations/IntegrationManagers"; | ||||
| 
 | ||||
| const ALLOWED_APP_URL_SCHEMES = ['https:', 'http:']; | ||||
| const ENABLE_REACT_PERF = false; | ||||
|  | @ -178,9 +177,22 @@ export default class AppTile extends React.Component { | |||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         const managers = IntegrationManagers.sharedInstance(); | ||||
|         if (!managers.hasManager()) { | ||||
|             console.warn("No integration manager - not setting scalar token", url); | ||||
|             this.setState({ | ||||
|                 error: null, | ||||
|                 widgetUrl: this._addWurlParams(this.props.url), | ||||
|                 initialising: false, | ||||
|             }); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         // TODO: Pick the right manager for the widget
 | ||||
| 
 | ||||
|         // Fetch the token before loading the iframe as we need it to mangle the URL
 | ||||
|         if (!this._scalarClient) { | ||||
|             this._scalarClient = new ScalarAuthClient(); | ||||
|             this._scalarClient = managers.getPrimaryManager().getScalarClient(); | ||||
|         } | ||||
|         this._scalarClient.getScalarToken().done((token) => { | ||||
|             // Append scalar_token as a query param if not already present
 | ||||
|  | @ -189,7 +201,7 @@ export default class AppTile extends React.Component { | |||
|             const params = qs.parse(u.query); | ||||
|             if (!params.scalar_token) { | ||||
|                 params.scalar_token = encodeURIComponent(token); | ||||
|                 // u.search must be set to undefined, so that u.format() uses query paramerters - https://nodejs.org/docs/latest/api/url.html#url_url_format_url_options
 | ||||
|                 // u.search must be set to undefined, so that u.format() uses query parameters - https://nodejs.org/docs/latest/api/url.html#url_url_format_url_options
 | ||||
|                 u.search = undefined; | ||||
|                 u.query = params; | ||||
|             } | ||||
|  | @ -251,11 +263,12 @@ export default class AppTile extends React.Component { | |||
|         if (this.props.onEditClick) { | ||||
|             this.props.onEditClick(); | ||||
|         } else { | ||||
|             showIntegrationsManager({ | ||||
|                 room: this.props.room, | ||||
|                 screen: 'type_' + this.props.type, | ||||
|                 integrationId: this.props.id, | ||||
|             }); | ||||
|             // TODO: Open the right manager for the widget
 | ||||
|             IntegrationManagers.sharedInstance().getPrimaryManager().open( | ||||
|                 this.props.room, | ||||
|                 this.props.type, | ||||
|                 this.props.id, | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -18,9 +18,8 @@ limitations under the License. | |||
| import React from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import sdk from '../../../index'; | ||||
| import ScalarAuthClient from '../../../ScalarAuthClient'; | ||||
| import { _t } from '../../../languageHandler'; | ||||
| import { showIntegrationsManager } from '../../../integrations/integrations'; | ||||
| import {IntegrationManagers} from "../../../integrations/IntegrationManagers"; | ||||
| 
 | ||||
| export default class ManageIntegsButton extends React.Component { | ||||
|     constructor(props) { | ||||
|  | @ -30,12 +29,17 @@ export default class ManageIntegsButton extends React.Component { | |||
|     onManageIntegrations = (ev) => { | ||||
|         ev.preventDefault(); | ||||
| 
 | ||||
|         showIntegrationsManager({ room: this.props.room }); | ||||
|         const managers = IntegrationManagers.sharedInstance(); | ||||
|         if (!managers.hasManager()) { | ||||
|             managers.openNoManagerDialog(); | ||||
|         } else { | ||||
|             managers.getPrimaryManager().open(this.props.room); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     render() { | ||||
|         let integrationsButton = <div />; | ||||
|         if (ScalarAuthClient.isPossible()) { | ||||
|         if (IntegrationManagers.sharedInstance().hasManager()) { | ||||
|             const AccessibleButton = sdk.getComponent("elements.AccessibleButton"); | ||||
|             integrationsButton = ( | ||||
|                 <AccessibleButton | ||||
|  |  | |||
|  | @ -25,7 +25,6 @@ import highlight from 'highlight.js'; | |||
| import * as HtmlUtils from '../../../HtmlUtils'; | ||||
| import {formatDate} from '../../../DateUtils'; | ||||
| import sdk from '../../../index'; | ||||
| import ScalarAuthClient from '../../../ScalarAuthClient'; | ||||
| import Modal from '../../../Modal'; | ||||
| import SdkConfig from '../../../SdkConfig'; | ||||
| import dis from '../../../dispatcher'; | ||||
|  | @ -35,6 +34,7 @@ import SettingsStore from "../../../settings/SettingsStore"; | |||
| import ReplyThread from "../elements/ReplyThread"; | ||||
| import {host as matrixtoHost} from '../../../matrix-to'; | ||||
| import {pillifyLinks} from '../../../utils/pillify'; | ||||
| import {IntegrationManagers} from "../../../integrations/IntegrationManagers"; | ||||
| 
 | ||||
| module.exports = React.createClass({ | ||||
|     displayName: 'TextualBody', | ||||
|  | @ -318,12 +318,19 @@ module.exports = React.createClass({ | |||
|         // which requires the user to click through and THEN we can open the link in a new tab because
 | ||||
|         // the window.open command occurs in the same stack frame as the onClick callback.
 | ||||
| 
 | ||||
|         const managers = IntegrationManagers.sharedInstance(); | ||||
|         if (!managers.hasManager()) { | ||||
|             managers.openNoManagerDialog(); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         // Go fetch a scalar token
 | ||||
|         const scalarClient = new ScalarAuthClient(); | ||||
|         const integrationManager = managers.getPrimaryManager(); | ||||
|         const scalarClient = integrationManager.getScalarClient(); | ||||
|         scalarClient.connect().then(() => { | ||||
|             const completeUrl = scalarClient.getStarterLink(starterLink); | ||||
|             const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); | ||||
|             const integrationsUrl = SdkConfig.get().integrations_ui_url; | ||||
|             const integrationsUrl = integrationManager.uiUrl; | ||||
|             Modal.createTrackedDialog('Add an integration', '', QuestionDialog, { | ||||
|                 title: _t("Add an Integration"), | ||||
|                 description: | ||||
|  |  | |||
|  | @ -29,7 +29,7 @@ import { _t } from '../../../languageHandler'; | |||
| import WidgetUtils from '../../../utils/WidgetUtils'; | ||||
| import WidgetEchoStore from "../../../stores/WidgetEchoStore"; | ||||
| import AccessibleButton from '../elements/AccessibleButton'; | ||||
| import { showIntegrationsManager } from '../../../integrations/integrations'; | ||||
| import {IntegrationManagers} from "../../../integrations/IntegrationManagers"; | ||||
| 
 | ||||
| // The maximum number of widgets that can be added in a room
 | ||||
| const MAX_WIDGETS = 2; | ||||
|  | @ -128,10 +128,7 @@ module.exports = React.createClass({ | |||
|     }, | ||||
| 
 | ||||
|     _launchManageIntegrations: function() { | ||||
|         showIntegrationsManager({ | ||||
|             room: this.props.room, | ||||
|             screen: 'add_integ', | ||||
|         }); | ||||
|         IntegrationManagers.sharedInstance().getPrimaryManager().open(this.props.room, 'add_integ'); | ||||
|     }, | ||||
| 
 | ||||
|     onClickAddWidget: function(e) { | ||||
|  |  | |||
|  | @ -18,13 +18,12 @@ import {_t, _td} from '../../../languageHandler'; | |||
| import AppTile from '../elements/AppTile'; | ||||
| import MatrixClientPeg from '../../../MatrixClientPeg'; | ||||
| import sdk from '../../../index'; | ||||
| import ScalarAuthClient from '../../../ScalarAuthClient'; | ||||
| import dis from '../../../dispatcher'; | ||||
| import AccessibleButton from '../elements/AccessibleButton'; | ||||
| import WidgetUtils from '../../../utils/WidgetUtils'; | ||||
| import ActiveWidgetStore from '../../../stores/ActiveWidgetStore'; | ||||
| import PersistedElement from "../elements/PersistedElement"; | ||||
| import { showIntegrationsManager } from '../../../integrations/integrations'; | ||||
| import {IntegrationManagers} from "../../../integrations/IntegrationManagers"; | ||||
| 
 | ||||
| const widgetType = 'm.stickerpicker'; | ||||
| 
 | ||||
|  | @ -67,8 +66,9 @@ export default class Stickerpicker extends React.Component { | |||
| 
 | ||||
|     _acquireScalarClient() { | ||||
|         if (this.scalarClient) return Promise.resolve(this.scalarClient); | ||||
|         if (ScalarAuthClient.isPossible()) { | ||||
|             this.scalarClient = new ScalarAuthClient(); | ||||
|         // TODO: Pick the right manager for the widget
 | ||||
|         if (IntegrationManagers.sharedInstance().hasManager()) { | ||||
|             this.scalarClient = IntegrationManagers.sharedInstance().getPrimaryManager().getScalarClient(); | ||||
|             return this.scalarClient.connect().then(() => { | ||||
|                 this.forceUpdate(); | ||||
|                 return this.scalarClient; | ||||
|  | @ -348,11 +348,12 @@ export default class Stickerpicker extends React.Component { | |||
|      * Launch the integrations manager on the stickers integration page | ||||
|      */ | ||||
|     _launchManageIntegrations() { | ||||
|         showIntegrationsManager({ | ||||
|             room: this.props.room, | ||||
|             screen: `type_${widgetType}`, | ||||
|             integrationId: this.state.widgetId, | ||||
|         }); | ||||
|         // TODO: Open the right integration manager for the widget
 | ||||
|         IntegrationManagers.sharedInstance().getPrimaryManager().open( | ||||
|             this.props.room, | ||||
|             `type_${widgetType}`, | ||||
|             this.state.widgetId, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     render() { | ||||
|  |  | |||
|  | @ -0,0 +1,81 @@ | |||
| /* | ||||
| 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 ScalarAuthClient from "../ScalarAuthClient"; | ||||
| import sdk from "../index"; | ||||
| import {dialogTermsInteractionCallback, TermsNotSignedError} from "../Terms"; | ||||
| import type {Room} from "matrix-js-sdk"; | ||||
| import Modal from '../Modal'; | ||||
| 
 | ||||
| export class IntegrationManagerInstance { | ||||
|     apiUrl: string; | ||||
|     uiUrl: string; | ||||
| 
 | ||||
|     constructor(apiUrl: string, uiUrl: string) { | ||||
|         this.apiUrl = apiUrl; | ||||
|         this.uiUrl = uiUrl; | ||||
| 
 | ||||
|         // Per the spec: UI URL is optional.
 | ||||
|         if (!this.uiUrl) this.uiUrl = this.apiUrl; | ||||
|     } | ||||
| 
 | ||||
|     getScalarClient(): ScalarAuthClient { | ||||
|         return new ScalarAuthClient(this.apiUrl, this.uiUrl); | ||||
|     } | ||||
| 
 | ||||
|     async open(room: Room = null, screen: string = null, integrationId: string = null): void { | ||||
|         const IntegrationsManager = sdk.getComponent("views.settings.IntegrationsManager"); | ||||
|         const dialog = Modal.createTrackedDialog( | ||||
|             'Integration Manager', '', IntegrationsManager, | ||||
|             {loading: true}, 'mx_IntegrationsManager', | ||||
|         ); | ||||
| 
 | ||||
|         const client = this.getScalarClient(); | ||||
|         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', | ||||
|             ); | ||||
|         }); | ||||
| 
 | ||||
|         const newProps = {}; | ||||
|         try { | ||||
|             await client.connect(); | ||||
|             if (!client.hasCredentials()) { | ||||
|                 newProps["connected"] = false; | ||||
|             } else { | ||||
|                 newProps["url"] = client.getScalarInterfaceUrlForRoom(room, screen, integrationId); | ||||
|             } | ||||
|         } catch (e) { | ||||
|             if (e instanceof TermsNotSignedError) { | ||||
|                 dialog.close(); | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             console.error(e); | ||||
|             newProps["connected"] = false; | ||||
|         } | ||||
| 
 | ||||
|         // Close the old dialog and open a new one
 | ||||
|         dialog.close(); | ||||
|         Modal.createTrackedDialog( | ||||
|             'Integration Manager', '', IntegrationsManager, | ||||
|             newProps, 'mx_IntegrationsManager', | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,68 @@ | |||
| /* | ||||
| 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 SdkConfig from '../SdkConfig'; | ||||
| import sdk from "../index"; | ||||
| import Modal from '../Modal'; | ||||
| import {IntegrationManagerInstance} from "./IntegrationManagerInstance"; | ||||
| 
 | ||||
| export class IntegrationManagers { | ||||
|     static _instance; | ||||
| 
 | ||||
|     static sharedInstance(): IntegrationManagers { | ||||
|         if (!IntegrationManagers._instance) { | ||||
|             IntegrationManagers._instance = new IntegrationManagers(); | ||||
|         } | ||||
|         return IntegrationManagers._instance; | ||||
|     } | ||||
| 
 | ||||
|     _managers: IntegrationManagerInstance[] = []; | ||||
| 
 | ||||
|     constructor() { | ||||
|         this._setupConfiguredManager(); | ||||
|     } | ||||
| 
 | ||||
|     _setupConfiguredManager() { | ||||
|         const apiUrl = SdkConfig.get()['integrations_rest_url']; | ||||
|         const uiUrl = SdkConfig.get()['integrations_ui_url']; | ||||
| 
 | ||||
|         if (apiUrl && uiUrl) { | ||||
|             this._managers.push(new IntegrationManagerInstance(apiUrl, uiUrl)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     hasManager(): boolean { | ||||
|         return this._managers.length > 0; | ||||
|     } | ||||
| 
 | ||||
|     getPrimaryManager(): IntegrationManagerInstance { | ||||
|         if (this.hasManager()) { | ||||
|             // TODO: TravisR - Handle custom integration managers (widgets)
 | ||||
|             return this._managers[0]; | ||||
|         } else { | ||||
|             return null; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     openNoManagerDialog(): void { | ||||
|         // TODO: Is it Integrations (plural) or Integration (singular). Singular is easier spoken.
 | ||||
|         const IntegrationsManager = sdk.getComponent("views.settings.IntegrationsManager"); | ||||
|         Modal.createTrackedDialog( | ||||
|             "Integration Manager", "None", IntegrationsManager, | ||||
|             {configured: false}, 'mx_IntegrationsManager', | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|  | @ -1,79 +0,0 @@ | |||
| /* | ||||
| 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 sdk from "../index"; | ||||
| import ScalarAuthClient from '../ScalarAuthClient'; | ||||
| import Modal from '../Modal'; | ||||
| import { TermsNotSignedError, dialogTermsInteractionCallback } from '../Terms'; | ||||
| 
 | ||||
| export async function showIntegrationsManager(opts) { | ||||
|     const IntegrationsManager = sdk.getComponent("views.settings.IntegrationsManager"); | ||||
| 
 | ||||
|     let props = {}; | ||||
|     if (ScalarAuthClient.isPossible()) { | ||||
|         props.loading = true; | ||||
|     } else { | ||||
|         props.configured = false; | ||||
|     } | ||||
| 
 | ||||
|     const close = Modal.createTrackedDialog( | ||||
|         'Integrations Manager', '', IntegrationsManager, props, "mx_IntegrationsManager", | ||||
|     ).close; | ||||
| 
 | ||||
|     if (!ScalarAuthClient.isPossible()) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     const scalarClient = new ScalarAuthClient(); | ||||
|     scalarClient.setTermsInteractionCallback(integrationsTermsInteractionCallback); | ||||
|     try { | ||||
|         await scalarClient.connect(); | ||||
|         if (!scalarClient.hasCredentials()) { | ||||
|             props = { connected: false }; | ||||
|         } else { | ||||
|             props = { | ||||
|                 url: scalarClient.getScalarInterfaceUrlForRoom( | ||||
|                     opts.room, | ||||
|                     opts.screen, | ||||
|                     opts.integrationId, | ||||
|                 ), | ||||
|             }; | ||||
|         } | ||||
|     } catch (err) { | ||||
|         if (err instanceof TermsNotSignedError) { | ||||
|             // user canceled terms dialog, so just cancel the action
 | ||||
|             close(); | ||||
|             return; | ||||
|         } | ||||
|         console.error(err); | ||||
|         props = { connected: false }; | ||||
|     } | ||||
|     close(); | ||||
|     Modal.createTrackedDialog('Integrations Manager', '', IntegrationsManager, props, "mx_IntegrationsManager"); | ||||
| } | ||||
| 
 | ||||
| /* | ||||
|  * 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. | ||||
|  */ | ||||
| function integrationsTermsInteractionCallback(policiesAndServicePairs, agreedUrls) { | ||||
|     return dialogTermsInteractionCallback( | ||||
|         policiesAndServicePairs, | ||||
|         agreedUrls, | ||||
|         "mx_TermsDialog_forIntegrationsManager", | ||||
|     ); | ||||
| } | ||||
|  | @ -27,6 +27,7 @@ import WidgetEchoStore from '../stores/WidgetEchoStore'; | |||
| const WIDGET_WAIT_TIME = 20000; | ||||
| import SettingsStore from "../settings/SettingsStore"; | ||||
| import ActiveWidgetStore from "../stores/ActiveWidgetStore"; | ||||
| import {IntegrationManagers} from "../integrations/IntegrationManagers"; | ||||
| 
 | ||||
| /** | ||||
|  * Encodes a URI according to a set of template variables. Variables will be | ||||
|  | @ -102,7 +103,8 @@ export default class WidgetUtils { | |||
| 
 | ||||
|         let scalarUrls = SdkConfig.get().integrations_widgets_urls; | ||||
|         if (!scalarUrls || scalarUrls.length === 0) { | ||||
|             scalarUrls = [SdkConfig.get().integrations_rest_url]; | ||||
|             const defaultManager = IntegrationManagers.sharedInstance().getPrimaryManager(); | ||||
|             if (defaultManager) scalarUrls = [defaultManager.apiUrl]; | ||||
|         } | ||||
| 
 | ||||
|         for (let i = 0; i < scalarUrls.length; i++) { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Travis Ralston
						Travis Ralston