UNPKG

matrix-react-sdk

Version:
177 lines (145 loc) 6.2 kB
/* Copyright 2024 New Vector Ltd. Copyright 2019 The Matrix.org Foundation C.I.C. SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only Please see LICENSE files in the repository root for full details. */ import { logger } from "matrix-js-sdk/src/logger"; import { ClientEvent, IClientWellKnown, MatrixClient } from "matrix-js-sdk/src/matrix"; import type { MatrixEvent } from "matrix-js-sdk/src/matrix"; import SdkConfig from "../SdkConfig"; import Modal from "../Modal"; import { IntegrationManagerInstance, Kind } from "./IntegrationManagerInstance"; import IntegrationsImpossibleDialog from "../components/views/dialogs/IntegrationsImpossibleDialog"; import IntegrationsDisabledDialog from "../components/views/dialogs/IntegrationsDisabledDialog"; import WidgetUtils from "../utils/WidgetUtils"; import { MatrixClientPeg } from "../MatrixClientPeg"; const KIND_PREFERENCE = [ // Ordered: first is most preferred, last is least preferred. Kind.Account, Kind.Homeserver, Kind.Config, ]; export class IntegrationManagers { private static instance?: IntegrationManagers; private managers: IntegrationManagerInstance[] = []; private client?: MatrixClient; private primaryManager: IntegrationManagerInstance | null = null; public static sharedInstance(): IntegrationManagers { if (!IntegrationManagers.instance) { IntegrationManagers.instance = new IntegrationManagers(); } return IntegrationManagers.instance; } public constructor() { this.compileManagers(); } public startWatching(): void { this.stopWatching(); this.client = MatrixClientPeg.safeGet(); this.client.on(ClientEvent.AccountData, this.onAccountData); this.client.on(ClientEvent.ClientWellKnown, this.setupHomeserverManagers); this.compileManagers(); } public stopWatching(): void { if (!this.client) return; this.client.removeListener(ClientEvent.AccountData, this.onAccountData); this.client.removeListener(ClientEvent.ClientWellKnown, this.setupHomeserverManagers); } private compileManagers(): void { this.managers = []; this.setupConfiguredManager(); this.setupAccountManagers(); } private setupConfiguredManager(): void { const apiUrl = SdkConfig.get("integrations_rest_url"); const uiUrl = SdkConfig.get("integrations_ui_url"); if (apiUrl && uiUrl) { this.managers.push(new IntegrationManagerInstance(Kind.Config, apiUrl, uiUrl)); this.primaryManager = null; // reset primary } } private setupHomeserverManagers = async (discoveryResponse: IClientWellKnown): Promise<void> => { logger.log("Updating homeserver-configured integration managers..."); if (discoveryResponse && discoveryResponse["m.integrations"]) { let managers = discoveryResponse["m.integrations"]["managers"]; if (!Array.isArray(managers)) managers = []; // make it an array so we can wipe the HS managers logger.log(`Homeserver has ${managers.length} integration managers`); // Clear out any known managers for the homeserver // TODO: Log out of the scalar clients this.managers = this.managers.filter((m) => m.kind !== Kind.Homeserver); // Now add all the managers the homeserver wants us to have for (const hsManager of managers) { if (!hsManager["api_url"]) continue; this.managers.push( new IntegrationManagerInstance( Kind.Homeserver, hsManager["api_url"], hsManager["ui_url"], // optional ), ); } this.primaryManager = null; // reset primary } else { logger.log("Homeserver has no integration managers"); } }; private setupAccountManagers(): void { if (!this.client || !this.client.getUserId()) return; // not logged in const widgets = WidgetUtils.getIntegrationManagerWidgets(this.client); widgets.forEach((w) => { const data = w.content["data"]; if (!data) return; const uiUrl = w.content["url"]; const apiUrl = data["api_url"] as string; if (!apiUrl || !uiUrl) return; const manager = new IntegrationManagerInstance( Kind.Account, apiUrl, uiUrl, w["id"] || w["state_key"] || "", ); this.managers.push(manager); }); this.primaryManager = null; // reset primary } private onAccountData = (ev: MatrixEvent): void => { if (ev.getType() === "m.widgets") { this.compileManagers(); } }; public hasManager(): boolean { return this.managers.length > 0; } public getOrderedManagers(): IntegrationManagerInstance[] { const collator = new Intl.Collator(); const ordered: IntegrationManagerInstance[] = []; for (const kind of KIND_PREFERENCE) { const managers = this.managers.filter((m) => m.kind === kind); if (!managers || !managers.length) continue; if (kind === Kind.Account) { // Order by state_keys (IDs) managers.sort((a, b) => collator.compare(a.id ?? "", b.id ?? "")); } ordered.push(...managers); } return ordered; } public getPrimaryManager(): IntegrationManagerInstance | null { if (this.hasManager()) { if (this.primaryManager) return this.primaryManager; this.primaryManager = this.getOrderedManagers()[0]; return this.primaryManager; } else { return null; } } public openNoManagerDialog(): void { Modal.createDialog(IntegrationsImpossibleDialog); } public showDisabledDialog(): void { Modal.createDialog(IntegrationsDisabledDialog); } } // For debugging window.mxIntegrationManagers = IntegrationManagers;