diff --git a/src/Lifecycle.js b/src/Lifecycle.js index 0ddb7e9aae..c03a958840 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -35,6 +35,7 @@ import { sendLoginRequest } from "./Login"; import * as StorageManager from './utils/StorageManager'; import SettingsStore from "./settings/SettingsStore"; import TypingStore from "./stores/TypingStore"; +import {IntegrationManagers} from "./integrations/IntegrationManagers"; /** * Called at startup, to attempt to build a logged-in Matrix session. It tries @@ -580,6 +581,7 @@ async function startMatrixClient(startSyncing=true) { Presence.start(); } DMRoomMap.makeShared().start(); + IntegrationManagers.sharedInstance().startWatching(); ActiveWidgetStore.start(); if (startSyncing) { @@ -638,6 +640,7 @@ export function stopMatrixClient(unsetClient=true) { TypingStore.sharedInstance().reset(); Presence.stop(); ActiveWidgetStore.stop(); + IntegrationManagers.sharedInstance().stopWatching(); if (DMRoomMap.shared()) DMRoomMap.shared().stop(); const cli = MatrixClientPeg.get(); if (cli) { diff --git a/src/integrations/IntegrationManagers.js b/src/integrations/IntegrationManagers.js index 9df5d80ee1..573d251a7b 100644 --- a/src/integrations/IntegrationManagers.js +++ b/src/integrations/IntegrationManagers.js @@ -18,6 +18,9 @@ import SdkConfig from '../SdkConfig'; import sdk from "../index"; import Modal from '../Modal'; import {IntegrationManagerInstance} from "./IntegrationManagerInstance"; +import type {MatrixClient, MatrixEvent} from "matrix-js-sdk"; +import WidgetUtils from "../utils/WidgetUtils"; +import MatrixClientPeg from "../MatrixClientPeg"; export class IntegrationManagers { static _instance; @@ -30,9 +33,28 @@ export class IntegrationManagers { } _managers: IntegrationManagerInstance[] = []; + _client: MatrixClient; constructor() { + this._compileManagers(); + } + + startWatching(): void { + this.stopWatching(); + this._client = MatrixClientPeg.get(); + this._client.on("accountData", this._onAccountData.bind(this)); + this._compileManagers(); + } + + stopWatching(): void { + if (!this._client) return; + this._client.removeListener("accountData", this._onAccountData.bind(this)); + } + + _compileManagers() { + this._managers = []; this._setupConfiguredManager(); + this._setupAccountManagers(); } _setupConfiguredManager() { @@ -44,14 +66,33 @@ export class IntegrationManagers { } } + _setupAccountManagers() { + const widgets = WidgetUtils.getIntegrationManagerWidgets(); + widgets.forEach(w => { + const data = w.content['data']; + if (!data) return; + + const uiUrl = w.content['url']; + const apiUrl = data['api_url']; + if (!apiUrl || !uiUrl) return; + + this._managers.push(new IntegrationManagerInstance(apiUrl, uiUrl)); + }); + } + + _onAccountData(ev: MatrixEvent): void { + if (ev.getType() === 'm.widgets') { + this._compileManagers(); + } + } + hasManager(): boolean { return this._managers.length > 0; } getPrimaryManager(): IntegrationManagerInstance { if (this.hasManager()) { - // TODO: TravisR - Handle custom integration managers (widgets) - return this._managers[0]; + return this._managers[this._managers.length - 1]; } else { return null; } @@ -66,3 +107,6 @@ export class IntegrationManagers { ); } } + +// For debugging +global.mxIntegrationManagers = IntegrationManagers; diff --git a/src/utils/WidgetUtils.js b/src/utils/WidgetUtils.js index 5e127e48d5..1e47554914 100644 --- a/src/utils/WidgetUtils.js +++ b/src/utils/WidgetUtils.js @@ -340,6 +340,17 @@ export default class WidgetUtils { return widgets.filter((widget) => widget.content && widget.content.type === "m.stickerpicker"); } + /** + * Get all integration manager widgets for this user. + * @returns {Object[]} An array of integration manager user widgets. + */ + static getIntegrationManagerWidgets() { + const widgets = WidgetUtils.getUserWidgetsArray(); + // We'll be using im.vector.integration_manager until MSC1957 or similar is accepted. + const imTypes = ["m.integration_manager", "im.vector.integration_manager"]; + return widgets.filter(w => w.content && imTypes.includes(w.content.type)); + } + /** * Remove all stickerpicker widgets (stickerpickers are user widgets by nature) * @return {Promise} Resolves on account data updated