From d7736db1afcf28257b41bef604e9a606045e83a8 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 13 May 2025 10:40:26 +0100 Subject: [PATCH 1/3] Initial stable release of the Module API Primarily to get away from semver treating every update as breaking in the 0.x.y series. --- .../element-web-module-api.api.md | 25 +- packages/element-web-module-api/package.json | 6 +- .../element-web-module-api/src/api/config.ts | 28 ++ .../element-web-module-api/src/api/i18n.ts | 52 ++++ .../src/api/index.test.ts | 22 ++ .../element-web-module-api/src/api/index.ts | 96 +++++++ .../src/api/legacy-customisations.ts | 259 ++++++++++++++++++ .../src/api/legacy-modules.ts | 30 ++ packages/element-web-module-api/src/index.ts | 8 +- 9 files changed, 519 insertions(+), 7 deletions(-) create mode 100644 packages/element-web-module-api/src/api/config.ts create mode 100644 packages/element-web-module-api/src/api/i18n.ts create mode 100644 packages/element-web-module-api/src/api/index.test.ts create mode 100644 packages/element-web-module-api/src/api/index.ts create mode 100644 packages/element-web-module-api/src/api/legacy-customisations.ts create mode 100644 packages/element-web-module-api/src/api/legacy-modules.ts diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index e47e7428f8..a4b9456cb6 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -5,6 +5,7 @@ ```ts import { ModuleApi } from '@matrix-org/react-sdk-module-api'; +import { Root } from 'react-dom/client'; import { RuntimeModule } from '@matrix-org/react-sdk-module-api'; // @alpha @deprecated (undocumented) @@ -18,8 +19,10 @@ export interface AliasCustomisations { // // @public export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension { - // (undocumented) - config: ConfigApi; + readonly config: ConfigApi; + createRoot(element: Element): Root; + readonly i18n: I18nApi; + readonly rootNode: HTMLElement; } // @alpha @deprecated (undocumented) @@ -60,6 +63,13 @@ export interface DirectoryCustomisations { requireCanonicalAliasAccessToPublish?(): boolean; } +// @public +export interface I18nApi { + get language(): string; + register(translations: Partial): void; + translate(key: keyof Translations, variables?: Variables): string; +} + // @alpha @deprecated (undocumented) export type LegacyCustomisations = (customisations: T) => void; @@ -179,6 +189,11 @@ export interface RoomListCustomisations { // @alpha @deprecated (undocumented) export type RuntimeModuleConstructor = new (api: ModuleApi) => RuntimeModule; +// @public +export type Translations = Record; + // @alpha @deprecated (undocumented) export interface UserIdentifierCustomisations { getDisplayUserIdentifier(userId: string, opts: { @@ -187,6 +202,12 @@ export interface UserIdentifierCustomisations { }): string | null; } +// @public +export type Variables = { + count?: number; + [key: string]: number | string | undefined; +}; + // @alpha @deprecated (undocumented) export interface WidgetPermissionsCustomisations { preapproveCapabilities?(widget: Widget, requestedCapabilities: Set): Promise>; diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index ad1df4a164..9c0e0cc364 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-module-api", "type": "module", - "version": "0.1.5", + "version": "1.0.0", "description": "Module API surface for element-web", "repository": { "type": "git", @@ -26,7 +26,8 @@ ], "scripts": { "prepare": "vite build && api-extractor run", - "lint": "tsc --noEmit", + "lint:types": "tsc --noEmit", + "lint:codestyle": "echo 'handled by lint:eslint'", "test": "vitest --coverage" }, "devDependencies": { @@ -34,6 +35,7 @@ "@microsoft/api-extractor": "^7.49.1", "@types/node": "^22.10.7", "@types/react": "^19", + "@types/react-dom": "^19.0.4", "@types/semver": "^7.5.8", "@vitest/coverage-v8": "^3.0.4", "matrix-web-i18n": "^3.3.0", diff --git a/packages/element-web-module-api/src/api/config.ts b/packages/element-web-module-api/src/api/config.ts new file mode 100644 index 0000000000..5f708b4983 --- /dev/null +++ b/packages/element-web-module-api/src/api/config.ts @@ -0,0 +1,28 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +/** + * The configuration for the application. + * Should be extended via declaration merging. + * @public + */ +export interface Config { + // The branding name of the application + brand: string; + // Other config options are available but not specified in the types as that would make it difficult to change for element-web + // they are accessible at runtime all the same, see list at https://github.com/element-hq/element-web/blob/develop/docs/config.md +} + +/** + * API for accessing the configuration. + * @public + */ +export interface ConfigApi { + get(): Config; + get(key: K): Config[K]; + get(key?: K): Config | Config[K]; +} diff --git a/packages/element-web-module-api/src/api/i18n.ts b/packages/element-web-module-api/src/api/i18n.ts new file mode 100644 index 0000000000..a93c247d4b --- /dev/null +++ b/packages/element-web-module-api/src/api/i18n.ts @@ -0,0 +1,52 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +/** + * The translations for the module. + * @public + */ +export type Translations = Record< + string, + { + [ietfLanguageTag: string]: string; + } +>; + +/** + * Variables to interpolate into a translation. + * @public + */ +export type Variables = { + /** + * The number of items to count for pluralised translations + */ + count?: number; + [key: string]: number | string | undefined; +}; + +/** + * The API for interacting with translations. + * @public + */ +export interface I18nApi { + /** + * Read the current language of the user in IETF Language Tag format + */ + get language(): string; + + /** + * Register translations for the module, may override app's existing translations + */ + register(translations: Partial): void; + + /** + * Perform a translation, with optional variables + * @param key - The key to translate + * @param variables - Optional variables to interpolate into the translation + */ + translate(key: keyof Translations, variables?: Variables): string; +} diff --git a/packages/element-web-module-api/src/api/index.test.ts b/packages/element-web-module-api/src/api/index.test.ts new file mode 100644 index 0000000000..25f34768a8 --- /dev/null +++ b/packages/element-web-module-api/src/api/index.test.ts @@ -0,0 +1,22 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { expect, test } from "vitest"; + +import { Api, isModule } from "."; + +const TestModule = { + default: class TestModule { + public static moduleApiVersion = "1.0.0"; + public constructor(private readonly api: Api) {} + public async load(): Promise {} + }, +}; + +test("isModule correctly identifies valid modules", () => { + expect(isModule(TestModule)).toBe(true); +}); diff --git a/packages/element-web-module-api/src/api/index.ts b/packages/element-web-module-api/src/api/index.ts new file mode 100644 index 0000000000..f20d9db300 --- /dev/null +++ b/packages/element-web-module-api/src/api/index.ts @@ -0,0 +1,96 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import type { Root } from "react-dom/client"; +import { LegacyModuleApiExtension } from "./legacy-modules"; +import { LegacyCustomisationsApiExtension } from "./legacy-customisations"; +import { ConfigApi } from "./config"; +import { I18nApi } from "./i18n"; + +/** + * Module interface for modules to implement. + * @public + */ +export interface Module { + load(): Promise; +} + +const moduleSignature: Record = { + load: "function", +}; + +/** + * Module interface for modules to export as the default export. + * @public + */ +export interface ModuleFactory { + readonly moduleApiVersion: string; + new (api: Api): Module; + readonly prototype: Module; +} + +const moduleFactorySignature: Record = { + moduleApiVersion: "string", + prototype: "object", +}; + +export interface ModuleExport { + default: ModuleFactory; +} + +const moduleExportSignature: Record = { + default: "function", +}; + +type Type = "function" | "string" | "number" | "boolean" | "object"; + +function isInterface(obj: unknown, type: "object" | "function", keys: Record): obj is T { + if (obj === null || typeof obj !== type) return false; + for (const key in keys) { + if (typeof (obj as Record)[key] !== keys[key]) return false; + } + return true; +} + +export function isModule(module: unknown): module is ModuleExport { + return ( + isInterface(module, "object", moduleExportSignature) && + isInterface(module.default, "function", moduleFactorySignature) && + isInterface(module.default.prototype, "object", moduleSignature) + ); +} + +/** + * The API for modules to interact with the application. + * @public + */ +export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension { + /** + * The API to read config.json values. + * Keys should be scoped to the module in reverse domain name notation. + * @public + */ + readonly config: ConfigApi; + /** + * The internationalisation API. + * @public + */ + readonly i18n: I18nApi; + /** + * The root node the main application is rendered to. + * Intended for rendering sibling React trees. + * @public + */ + readonly rootNode: HTMLElement; + /** + * Create a ReactDOM root for rendering React components. + * Exposed to allow modules to avoid needing to bundle their own ReactDOM. + * @param element - the element to render use as the root. + * @public + */ + createRoot(element: Element): Root; +} diff --git a/packages/element-web-module-api/src/api/legacy-customisations.ts b/packages/element-web-module-api/src/api/legacy-customisations.ts new file mode 100644 index 0000000000..6374789b5b --- /dev/null +++ b/packages/element-web-module-api/src/api/legacy-customisations.ts @@ -0,0 +1,259 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +/** + * The types here suck but these customisations are deprecated and will be removed soon. + */ + +/** + * @alpha + * @deprecated in favour of the new Module API + */ +export interface AliasCustomisations { + // E.g. prefer one of the aliases over another + getDisplayAliasForAliasSet?(canonicalAlias: string | null, altAliases: string[]): string | null; +} + +/** + * @alpha + * @deprecated in favour of the new Module API + */ +export interface ChatExportCustomisations { + /** + * Force parameters in room chat export fields returned here are forced + * and not allowed to be edited in the chat export form + */ + getForceChatExportParameters(): { + format?: ExportFormat; + range?: ExportType; + // must be < 10**8 + // only used when range is 'LastNMessages' + // default is 100 + numberOfMessages?: number; + includeAttachments?: boolean; + // maximum size of exported archive + // must be > 0 and < 8000 + sizeMb?: number; + }; +} + +/** + * @alpha + * @deprecated in favour of the new Module API + */ +export interface ComponentVisibilityCustomisations { + /** + * Determines whether or not the active MatrixClient user should be able to use + * the given UI component. If shown, the user might still not be able to use the + * component depending on their contextual permissions. For example, invite options + * might be shown to the user but they won't have permission to invite users to + * the current room: the button will appear disabled. + * @param component - The component to check visibility for. + * @returns True (default) if the user is able to see the component, false otherwise. + */ + shouldShowComponent?( + component: + | "UIComponent.sendInvites" + | "UIComponent.roomCreation" + | "UIComponent.spaceCreation" + | "UIComponent.exploreRooms" + | "UIComponent.addIntegrations" + | "UIComponent.filterContainer" + | "UIComponent.roomOptionsMenu", + ): boolean; +} + +/** + * @alpha + * @deprecated in favour of the new Module API + */ +export interface DirectoryCustomisations { + requireCanonicalAliasAccessToPublish?(): boolean; +} + +/** + * @alpha + * @deprecated in favour of the new Module API + */ +export interface LifecycleCustomisations { + onLoggedOutAndStorageCleared?(): void; +} + +/** + * @alpha + * @deprecated in favour of the new Module API + */ +export interface Media { + readonly isEncrypted: boolean; + readonly srcMxc: string; + readonly thumbnailMxc: string | null | undefined; + readonly hasThumbnail: boolean; + readonly srcHttp: string | null; + readonly thumbnailHttp: string | null; + getThumbnailHttp(width: number, height: number, mode?: "scale" | "crop"): string | null; + getThumbnailOfSourceHttp(width: number, height: number, mode?: "scale" | "crop"): string | null; + getSquareThumbnailHttp(dim: number): string | null; + downloadSource(): Promise; +} + +/** + * @alpha + * @deprecated in favour of the new Module API + */ +export interface MediaContructable { + new (prepared: PreparedMedia): Media; +} + +/** + * @alpha + * @deprecated in favour of the new Module API + */ +export interface MediaCustomisations { + readonly Media: MediaContructable; + mediaFromContent(content: Content, client?: Client): Media; + mediaFromMxc(mxc?: string, client?: Client): Media; +} + +/** + * @alpha + * @deprecated in favour of the new Module API + */ +export interface RoomListCustomisations { + /** + * Determines if a room is visible in the room list or not. By default, + * all rooms are visible. Where special handling is performed by Element, + * those rooms will not be able to override their visibility in the room + * list - Element will make the decision without calling this function. + * + * This function should be as fast as possible to avoid slowing down the + * client. + * @param room - The room to check the visibility of. + * @returns True if the room should be visible, false otherwise. + */ + isRoomVisible?(room: Room): boolean; +} + +/** + * @alpha + * @deprecated in favour of the new Module API + */ +export interface UserIdentifierCustomisations { + /** + * Customise display of the user identifier + * hide userId for guests, display 3pid + * + * Set withDisplayName to true when user identifier will be displayed alongside user name + */ + getDisplayUserIdentifier(userId: string, opts: { roomId?: string; withDisplayName?: boolean }): string | null; +} + +/** + * @alpha + * @deprecated in favour of the new Module API + */ +export interface WidgetPermissionsCustomisations { + /** + * Approves the widget for capabilities that it requested, if any can be + * approved. Typically this will be used to give certain widgets capabilities + * without having to prompt the user to approve them. This cannot reject + * capabilities that Element will be automatically granting, such as the + * ability for Jitsi widgets to stay on screen - those will be approved + * regardless. + * @param widget - The widget to approve capabilities for. + * @param requestedCapabilities - The capabilities the widget requested. + * @returns Resolves to the capabilities that are approved for use + * by the widget. If none are approved, this should return an empty Set. + */ + preapproveCapabilities?(widget: Widget, requestedCapabilities: Set): Promise>; +} + +/** + * @alpha + * @deprecated in favour of the new Module API + */ +export interface WidgetVariablesCustomisations { + /** + * Provides a partial set of the variables needed to render any widget. If + * variables are missing or not provided then they will be filled with the + * application-determined defaults. + * + * This will not be called until after isReady() resolves. + * @returns The variables. + */ + provideVariables?(): { + currentUserId: string; + userDisplayName?: string; + userHttpAvatarUrl?: string; + clientId?: string; + clientTheme?: string; + clientLanguage?: string; + deviceId?: string; + baseUrl?: string; + }; + /** + * Resolves to whether or not the customisation point is ready for variables + * to be provided. This will block widgets being rendered. + * If not provided, the app will assume that the customisation is always ready. + * @returns a promise which resolves when ready. + */ + isReady?(): Promise; +} + +/** + * @alpha + * @deprecated in favour of the new Module API + */ +export type LegacyCustomisations = (customisations: T) => void; + +/** + * @alpha + * @deprecated in favour of the new Module API + */ +export interface LegacyCustomisationsApiExtension { + /** + * @deprecated in favour of the new Module API + */ + readonly _registerLegacyAliasCustomisations: LegacyCustomisations; + /** + * @deprecated in favour of the new Module API + */ + readonly _registerLegacyChatExportCustomisations: LegacyCustomisations>; + /** + * @deprecated in favour of the new Module API + */ + readonly _registerLegacyComponentVisibilityCustomisations: LegacyCustomisations; + /** + * @deprecated in favour of the new Module API + */ + readonly _registerLegacyDirectoryCustomisations: LegacyCustomisations; + /** + * @deprecated in favour of the new Module API + */ + readonly _registerLegacyLifecycleCustomisations: LegacyCustomisations; + /** + * @deprecated in favour of the new Module API + */ + readonly _registerLegacyMediaCustomisations: LegacyCustomisations>; + /** + * @deprecated in favour of the new Module API + */ + readonly _registerLegacyRoomListCustomisations: LegacyCustomisations>; + /** + * @deprecated in favour of the new Module API + */ + readonly _registerLegacyUserIdentifierCustomisations: LegacyCustomisations; + /** + * @deprecated in favour of the new Module API + */ + readonly _registerLegacyWidgetPermissionsCustomisations: LegacyCustomisations< + WidgetPermissionsCustomisations + >; + /** + * @deprecated in favour of the new Module API + */ + readonly _registerLegacyWidgetVariablesCustomisations: LegacyCustomisations; +} diff --git a/packages/element-web-module-api/src/api/legacy-modules.ts b/packages/element-web-module-api/src/api/legacy-modules.ts new file mode 100644 index 0000000000..baaad96b01 --- /dev/null +++ b/packages/element-web-module-api/src/api/legacy-modules.ts @@ -0,0 +1,30 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore -- optional interface, will gracefully degrade to `any` if `react-sdk-module-api` isn't installed +import type { ModuleApi, RuntimeModule } from "@matrix-org/react-sdk-module-api"; + +/** + * @alpha + * @deprecated in favour of the new module API + */ +export type RuntimeModuleConstructor = new (api: ModuleApi) => RuntimeModule; + +/** + * @alpha + * @deprecated in favour of the new module API + */ +/* eslint-disable @typescript-eslint/naming-convention */ +export interface LegacyModuleApiExtension { + /** + * Register a legacy module based on \@matrix-org/react-sdk-module-api + * @param LegacyModule - the module class to register + * @deprecated provided only as a transition path for legacy modules + */ + _registerLegacyModule(LegacyModule: RuntimeModuleConstructor): Promise; +} diff --git a/packages/element-web-module-api/src/index.ts b/packages/element-web-module-api/src/index.ts index 0b2bf9435c..b503c88188 100644 --- a/packages/element-web-module-api/src/index.ts +++ b/packages/element-web-module-api/src/index.ts @@ -6,6 +6,8 @@ Please see LICENSE files in the repository root for full details. */ export { ModuleLoader, ModuleIncompatibleError } from "./loader"; -export type { Api, Module, ModuleFactory, Config, ConfigApi } from "./api"; -export type * from "./legacy-modules"; -export type * from "./legacy-customisations"; +export type { Api, Module, ModuleFactory } from "./api"; +export type { Config, ConfigApi } from "./api/config"; +export type { I18nApi, Variables, Translations } from "./api/i18n"; +export type * from "./api/legacy-modules"; +export type * from "./api/legacy-customisations"; From 5927495c8883f957d8f1e121a46e1254ef6b2668 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 13 May 2025 10:45:18 +0100 Subject: [PATCH 2/3] Remove stray files --- .../element-web-module-api/src/api.test.ts | 23 ----- packages/element-web-module-api/src/api.ts | 92 ------------------- 2 files changed, 115 deletions(-) delete mode 100644 packages/element-web-module-api/src/api.test.ts delete mode 100644 packages/element-web-module-api/src/api.ts diff --git a/packages/element-web-module-api/src/api.test.ts b/packages/element-web-module-api/src/api.test.ts deleted file mode 100644 index 62dd3973aa..0000000000 --- a/packages/element-web-module-api/src/api.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright 2025 New Vector Ltd. - -SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -Please see LICENSE files in the repository root for full details. -*/ - -import { expect, test } from "vitest"; - -import { Api } from "."; -import { isModule } from "./api.js"; - -const TestModule = { - default: class TestModule { - public static moduleApiVersion = "1.0.0"; - public constructor(private readonly api: Api) {} - public async load(): Promise {} - }, -}; - -test("isModule correctly identifies valid modules", () => { - expect(isModule(TestModule)).toBe(true); -}); diff --git a/packages/element-web-module-api/src/api.ts b/packages/element-web-module-api/src/api.ts deleted file mode 100644 index 5534411375..0000000000 --- a/packages/element-web-module-api/src/api.ts +++ /dev/null @@ -1,92 +0,0 @@ -/* -Copyright 2025 New Vector Ltd. - -SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -Please see LICENSE files in the repository root for full details. -*/ - -import { LegacyModuleApiExtension } from "./legacy-modules"; -import { LegacyCustomisationsApiExtension } from "./legacy-customisations"; - -/** - * Module interface for modules to implement. - * @public - */ -export interface Module { - load(): Promise; -} - -const moduleSignature: Record = { - load: "function", -}; - -/** - * Module interface for modules to export as the default export. - * @public - */ -export interface ModuleFactory { - readonly moduleApiVersion: string; - new (api: Api): Module; - readonly prototype: Module; -} - -const moduleFactorySignature: Record = { - moduleApiVersion: "string", - prototype: "object", -}; - -export interface ModuleExport { - default: ModuleFactory; -} - -const moduleExportSignature: Record = { - default: "function", -}; - -type Type = "function" | "string" | "number" | "boolean" | "object"; - -function isInterface(obj: unknown, type: "object" | "function", keys: Record): obj is T { - if (obj === null || typeof obj !== type) return false; - for (const key in keys) { - if (typeof (obj as Record)[key] !== keys[key]) return false; - } - return true; -} - -export function isModule(module: unknown): module is ModuleExport { - return ( - isInterface(module, "object", moduleExportSignature) && - isInterface(module.default, "function", moduleFactorySignature) && - isInterface(module.default.prototype, "object", moduleSignature) - ); -} - -/** - * The configuration for the application. - * Should be extended via declaration merging. - * @public - */ -export interface Config { - // The branding name of the application - brand: string; - // Other config options are available but not specified in the types as that would make it difficult to change for element-web - // they are accessible at runtime all the same, see list at https://github.com/element-hq/element-web/blob/develop/docs/config.md -} - -/** - * API for accessing the configuration. - * @public - */ -export interface ConfigApi { - get(): Config; - get(key: K): Config[K]; - get(key?: K): Config | Config[K]; -} - -/** - * The API for modules to interact with the application. - * @public - */ -export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiExtension { - config: ConfigApi; -} From d2c2437188bbb7f53eb682e133ad27ad83d7a4d6 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 13 May 2025 10:48:17 +0100 Subject: [PATCH 3/3] Remove stray files --- .../src/legacy-customisations.ts | 259 ------------------ .../src/legacy-modules.ts | 30 -- 2 files changed, 289 deletions(-) delete mode 100644 packages/element-web-module-api/src/legacy-customisations.ts delete mode 100644 packages/element-web-module-api/src/legacy-modules.ts diff --git a/packages/element-web-module-api/src/legacy-customisations.ts b/packages/element-web-module-api/src/legacy-customisations.ts deleted file mode 100644 index 6374789b5b..0000000000 --- a/packages/element-web-module-api/src/legacy-customisations.ts +++ /dev/null @@ -1,259 +0,0 @@ -/* -Copyright 2025 New Vector Ltd. - -SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -Please see LICENSE files in the repository root for full details. -*/ - -/** - * The types here suck but these customisations are deprecated and will be removed soon. - */ - -/** - * @alpha - * @deprecated in favour of the new Module API - */ -export interface AliasCustomisations { - // E.g. prefer one of the aliases over another - getDisplayAliasForAliasSet?(canonicalAlias: string | null, altAliases: string[]): string | null; -} - -/** - * @alpha - * @deprecated in favour of the new Module API - */ -export interface ChatExportCustomisations { - /** - * Force parameters in room chat export fields returned here are forced - * and not allowed to be edited in the chat export form - */ - getForceChatExportParameters(): { - format?: ExportFormat; - range?: ExportType; - // must be < 10**8 - // only used when range is 'LastNMessages' - // default is 100 - numberOfMessages?: number; - includeAttachments?: boolean; - // maximum size of exported archive - // must be > 0 and < 8000 - sizeMb?: number; - }; -} - -/** - * @alpha - * @deprecated in favour of the new Module API - */ -export interface ComponentVisibilityCustomisations { - /** - * Determines whether or not the active MatrixClient user should be able to use - * the given UI component. If shown, the user might still not be able to use the - * component depending on their contextual permissions. For example, invite options - * might be shown to the user but they won't have permission to invite users to - * the current room: the button will appear disabled. - * @param component - The component to check visibility for. - * @returns True (default) if the user is able to see the component, false otherwise. - */ - shouldShowComponent?( - component: - | "UIComponent.sendInvites" - | "UIComponent.roomCreation" - | "UIComponent.spaceCreation" - | "UIComponent.exploreRooms" - | "UIComponent.addIntegrations" - | "UIComponent.filterContainer" - | "UIComponent.roomOptionsMenu", - ): boolean; -} - -/** - * @alpha - * @deprecated in favour of the new Module API - */ -export interface DirectoryCustomisations { - requireCanonicalAliasAccessToPublish?(): boolean; -} - -/** - * @alpha - * @deprecated in favour of the new Module API - */ -export interface LifecycleCustomisations { - onLoggedOutAndStorageCleared?(): void; -} - -/** - * @alpha - * @deprecated in favour of the new Module API - */ -export interface Media { - readonly isEncrypted: boolean; - readonly srcMxc: string; - readonly thumbnailMxc: string | null | undefined; - readonly hasThumbnail: boolean; - readonly srcHttp: string | null; - readonly thumbnailHttp: string | null; - getThumbnailHttp(width: number, height: number, mode?: "scale" | "crop"): string | null; - getThumbnailOfSourceHttp(width: number, height: number, mode?: "scale" | "crop"): string | null; - getSquareThumbnailHttp(dim: number): string | null; - downloadSource(): Promise; -} - -/** - * @alpha - * @deprecated in favour of the new Module API - */ -export interface MediaContructable { - new (prepared: PreparedMedia): Media; -} - -/** - * @alpha - * @deprecated in favour of the new Module API - */ -export interface MediaCustomisations { - readonly Media: MediaContructable; - mediaFromContent(content: Content, client?: Client): Media; - mediaFromMxc(mxc?: string, client?: Client): Media; -} - -/** - * @alpha - * @deprecated in favour of the new Module API - */ -export interface RoomListCustomisations { - /** - * Determines if a room is visible in the room list or not. By default, - * all rooms are visible. Where special handling is performed by Element, - * those rooms will not be able to override their visibility in the room - * list - Element will make the decision without calling this function. - * - * This function should be as fast as possible to avoid slowing down the - * client. - * @param room - The room to check the visibility of. - * @returns True if the room should be visible, false otherwise. - */ - isRoomVisible?(room: Room): boolean; -} - -/** - * @alpha - * @deprecated in favour of the new Module API - */ -export interface UserIdentifierCustomisations { - /** - * Customise display of the user identifier - * hide userId for guests, display 3pid - * - * Set withDisplayName to true when user identifier will be displayed alongside user name - */ - getDisplayUserIdentifier(userId: string, opts: { roomId?: string; withDisplayName?: boolean }): string | null; -} - -/** - * @alpha - * @deprecated in favour of the new Module API - */ -export interface WidgetPermissionsCustomisations { - /** - * Approves the widget for capabilities that it requested, if any can be - * approved. Typically this will be used to give certain widgets capabilities - * without having to prompt the user to approve them. This cannot reject - * capabilities that Element will be automatically granting, such as the - * ability for Jitsi widgets to stay on screen - those will be approved - * regardless. - * @param widget - The widget to approve capabilities for. - * @param requestedCapabilities - The capabilities the widget requested. - * @returns Resolves to the capabilities that are approved for use - * by the widget. If none are approved, this should return an empty Set. - */ - preapproveCapabilities?(widget: Widget, requestedCapabilities: Set): Promise>; -} - -/** - * @alpha - * @deprecated in favour of the new Module API - */ -export interface WidgetVariablesCustomisations { - /** - * Provides a partial set of the variables needed to render any widget. If - * variables are missing or not provided then they will be filled with the - * application-determined defaults. - * - * This will not be called until after isReady() resolves. - * @returns The variables. - */ - provideVariables?(): { - currentUserId: string; - userDisplayName?: string; - userHttpAvatarUrl?: string; - clientId?: string; - clientTheme?: string; - clientLanguage?: string; - deviceId?: string; - baseUrl?: string; - }; - /** - * Resolves to whether or not the customisation point is ready for variables - * to be provided. This will block widgets being rendered. - * If not provided, the app will assume that the customisation is always ready. - * @returns a promise which resolves when ready. - */ - isReady?(): Promise; -} - -/** - * @alpha - * @deprecated in favour of the new Module API - */ -export type LegacyCustomisations = (customisations: T) => void; - -/** - * @alpha - * @deprecated in favour of the new Module API - */ -export interface LegacyCustomisationsApiExtension { - /** - * @deprecated in favour of the new Module API - */ - readonly _registerLegacyAliasCustomisations: LegacyCustomisations; - /** - * @deprecated in favour of the new Module API - */ - readonly _registerLegacyChatExportCustomisations: LegacyCustomisations>; - /** - * @deprecated in favour of the new Module API - */ - readonly _registerLegacyComponentVisibilityCustomisations: LegacyCustomisations; - /** - * @deprecated in favour of the new Module API - */ - readonly _registerLegacyDirectoryCustomisations: LegacyCustomisations; - /** - * @deprecated in favour of the new Module API - */ - readonly _registerLegacyLifecycleCustomisations: LegacyCustomisations; - /** - * @deprecated in favour of the new Module API - */ - readonly _registerLegacyMediaCustomisations: LegacyCustomisations>; - /** - * @deprecated in favour of the new Module API - */ - readonly _registerLegacyRoomListCustomisations: LegacyCustomisations>; - /** - * @deprecated in favour of the new Module API - */ - readonly _registerLegacyUserIdentifierCustomisations: LegacyCustomisations; - /** - * @deprecated in favour of the new Module API - */ - readonly _registerLegacyWidgetPermissionsCustomisations: LegacyCustomisations< - WidgetPermissionsCustomisations - >; - /** - * @deprecated in favour of the new Module API - */ - readonly _registerLegacyWidgetVariablesCustomisations: LegacyCustomisations; -} diff --git a/packages/element-web-module-api/src/legacy-modules.ts b/packages/element-web-module-api/src/legacy-modules.ts deleted file mode 100644 index baaad96b01..0000000000 --- a/packages/element-web-module-api/src/legacy-modules.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* -Copyright 2025 New Vector Ltd. - -SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -Please see LICENSE files in the repository root for full details. -*/ - -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -- optional interface, will gracefully degrade to `any` if `react-sdk-module-api` isn't installed -import type { ModuleApi, RuntimeModule } from "@matrix-org/react-sdk-module-api"; - -/** - * @alpha - * @deprecated in favour of the new module API - */ -export type RuntimeModuleConstructor = new (api: ModuleApi) => RuntimeModule; - -/** - * @alpha - * @deprecated in favour of the new module API - */ -/* eslint-disable @typescript-eslint/naming-convention */ -export interface LegacyModuleApiExtension { - /** - * Register a legacy module based on \@matrix-org/react-sdk-module-api - * @param LegacyModule - the module class to register - * @deprecated provided only as a transition path for legacy modules - */ - _registerLegacyModule(LegacyModule: RuntimeModuleConstructor): Promise; -}