mirror of
https://github.com/vector-im/element-web.git
synced 2025-11-07 11:41:08 +01:00
Change module API to be an instance getter (#31025)
* Change module API to be an instance getter Helps with circular dependencies by not instantating the module API on the initial evaluation of the files. * Add basic test * add another test
This commit is contained in:
parent
6cfe197a38
commit
146e4772ac
@ -31,7 +31,7 @@ import { UIFeature } from "../../../settings/UIFeature";
|
||||
import { ModuleRunner } from "../../../modules/ModuleRunner";
|
||||
import { Icon as AskToJoinIcon } from "../../../../res/img/element-icons/ask-to-join.svg";
|
||||
import Field from "../elements/Field";
|
||||
import ModuleApi from "../../../modules/Api.ts";
|
||||
import { ModuleApi } from "../../../modules/Api.ts";
|
||||
|
||||
const MemberEventHtmlReasonField = "io.element.html_reason";
|
||||
|
||||
@ -750,7 +750,7 @@ class RoomPreviewBar extends React.Component<IProps, IState> {
|
||||
}
|
||||
|
||||
const WrappedRoomPreviewBar = (props: IProps): JSX.Element => {
|
||||
const moduleRenderer = ModuleApi.customComponents.roomPreviewBarRenderer;
|
||||
const moduleRenderer = ModuleApi.instance.customComponents.roomPreviewBarRenderer;
|
||||
if (moduleRenderer) {
|
||||
return moduleRenderer(
|
||||
{
|
||||
|
||||
@ -41,7 +41,7 @@ import HiddenBody from "../components/views/messages/HiddenBody";
|
||||
import ViewSourceEvent from "../components/views/messages/ViewSourceEvent";
|
||||
import { shouldDisplayAsBeaconTile } from "../utils/beacon/timeline";
|
||||
import { type IBodyProps } from "../components/views/messages/IBodyProps";
|
||||
import ModuleApi from "../modules/Api";
|
||||
import { ModuleApi } from "../modules/Api";
|
||||
import { TextualEventViewModel } from "../viewmodels/event-tiles/TextualEventViewModel";
|
||||
import { TextualEventView } from "../../packages/shared-components/src/event-tiles/TextualEventView";
|
||||
import { ElementCallEventType } from "../call-types";
|
||||
@ -266,7 +266,7 @@ export function renderTile(
|
||||
// If we don't have a factory for this event, attempt
|
||||
// to find a custom component that can render it.
|
||||
// Will return null if no custom component can render it.
|
||||
return ModuleApi.customComponents.renderMessage({
|
||||
return ModuleApi.instance.customComponents.renderMessage({
|
||||
mxEvent: props.mxEvent,
|
||||
});
|
||||
}
|
||||
@ -297,7 +297,7 @@ export function renderTile(
|
||||
case TimelineRenderingType.File:
|
||||
case TimelineRenderingType.Notification:
|
||||
case TimelineRenderingType.Thread:
|
||||
return ModuleApi.customComponents.renderMessage(
|
||||
return ModuleApi.instance.customComponents.renderMessage(
|
||||
{
|
||||
mxEvent: props.mxEvent,
|
||||
},
|
||||
@ -318,7 +318,7 @@ export function renderTile(
|
||||
}),
|
||||
);
|
||||
default:
|
||||
return ModuleApi.customComponents.renderMessage(
|
||||
return ModuleApi.instance.customComponents.renderMessage(
|
||||
{
|
||||
mxEvent: props.mxEvent,
|
||||
},
|
||||
@ -363,7 +363,7 @@ export function renderReplyTile(
|
||||
// If we don't have a factory for this event, attempt
|
||||
// to find a custom component that can render it.
|
||||
// Will return null if no custom component can render it.
|
||||
return ModuleApi.customComponents.renderMessage({
|
||||
return ModuleApi.instance.customComponents.renderMessage({
|
||||
mxEvent: props.mxEvent,
|
||||
});
|
||||
}
|
||||
@ -384,7 +384,7 @@ export function renderReplyTile(
|
||||
permalinkCreator,
|
||||
} = props;
|
||||
|
||||
return ModuleApi.customComponents.renderMessage(
|
||||
return ModuleApi.instance.customComponents.renderMessage(
|
||||
{
|
||||
mxEvent: props.mxEvent,
|
||||
},
|
||||
@ -429,7 +429,7 @@ export function haveRendererForEvent(
|
||||
|
||||
// Check to see if we have any hints for this message, which indicates
|
||||
// there is a custom renderer for the event.
|
||||
if (ModuleApi.customComponents.getHintsForMessage(mxEvent)) {
|
||||
if (ModuleApi.instance.customComponents.getHintsForMessage(mxEvent)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -15,7 +15,7 @@ import { _t } from "../languageHandler";
|
||||
import Modal from "../Modal";
|
||||
import { FileDownloader } from "../utils/FileDownloader";
|
||||
import { MediaEventHelper } from "../utils/MediaEventHelper";
|
||||
import ModuleApi from "../modules/Api";
|
||||
import { ModuleApi } from "../modules/Api";
|
||||
|
||||
export interface UseDownloadMediaReturn {
|
||||
download: () => Promise<void>;
|
||||
@ -34,7 +34,7 @@ export function useDownloadMedia(url: string, fileName?: string, mxEvent?: Matri
|
||||
useEffect(() => {
|
||||
if (!mxEvent) return;
|
||||
|
||||
const hints = ModuleApi.customComponents.getHintsForMessage(mxEvent);
|
||||
const hints = ModuleApi.instance.customComponents.getHintsForMessage(mxEvent);
|
||||
if (hints?.allowDownloadingMedia) {
|
||||
setCanDownload(false);
|
||||
hints
|
||||
|
||||
@ -39,7 +39,17 @@ const legacyCustomisationsFactory = <T extends object>(baseCustomisations: T) =>
|
||||
/**
|
||||
* Implementation of the @element-hq/element-web-module-api runtime module API.
|
||||
*/
|
||||
class ModuleApi implements Api {
|
||||
export class ModuleApi implements Api {
|
||||
private static _instance: ModuleApi;
|
||||
|
||||
public static get instance(): ModuleApi {
|
||||
if (!ModuleApi._instance) {
|
||||
ModuleApi._instance = new ModuleApi();
|
||||
window.mxModuleApi = ModuleApi._instance;
|
||||
}
|
||||
return ModuleApi._instance;
|
||||
}
|
||||
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
public async _registerLegacyModule(LegacyModule: RuntimeModuleConstructor): Promise<void> {
|
||||
ModuleRunner.instance.registerModule((api) => new LegacyModule(api));
|
||||
@ -77,8 +87,3 @@ class ModuleApi implements Api {
|
||||
}
|
||||
|
||||
export type ModuleApiType = ModuleApi;
|
||||
|
||||
if (!window.mxModuleApi) {
|
||||
window.mxModuleApi = new ModuleApi();
|
||||
}
|
||||
export default window.mxModuleApi;
|
||||
|
||||
@ -30,7 +30,7 @@ import { type TimelineRenderingType } from "../contexts/RoomContext";
|
||||
import { launchPollEditor } from "../components/views/messages/MPollBody";
|
||||
import { Action } from "../dispatcher/actions";
|
||||
import { type ViewRoomPayload } from "../dispatcher/payloads/ViewRoomPayload";
|
||||
import ModuleApi from "../modules/Api";
|
||||
import { ModuleApi } from "../modules/Api";
|
||||
|
||||
/**
|
||||
* Returns whether an event should allow actions like reply, reactions, edit, etc.
|
||||
@ -78,7 +78,7 @@ export function canEditContent(matrixClient: MatrixClient, mxEvent: MatrixEvent)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ModuleApi.customComponents.getHintsForMessage(mxEvent)?.allowEditingEvent === false) {
|
||||
if (ModuleApi.instance.customComponents.getHintsForMessage(mxEvent)?.allowEditingEvent === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -25,7 +25,7 @@ import ElectronPlatform from "./platform/ElectronPlatform";
|
||||
import PWAPlatform from "./platform/PWAPlatform";
|
||||
import WebPlatform from "./platform/WebPlatform";
|
||||
import { initRageshake, initRageshakeStore } from "./rageshakesetup";
|
||||
import ModuleApi from "../modules/Api.ts";
|
||||
import { ModuleApi } from "../modules/Api.ts";
|
||||
|
||||
export const rageshakePromise = initRageshake();
|
||||
|
||||
@ -145,7 +145,7 @@ export async function loadPlugins(): Promise<void> {
|
||||
|
||||
const modules = SdkConfig.get("modules");
|
||||
if (!modules?.length) return;
|
||||
const moduleLoader = new ModuleLoader(ModuleApi);
|
||||
const moduleLoader = new ModuleLoader(ModuleApi.instance);
|
||||
window.mxModuleLoader = moduleLoader;
|
||||
for (const src of modules) {
|
||||
// We need to instruct webpack to not mangle this import as it is not available at compile time
|
||||
|
||||
@ -16,7 +16,7 @@ import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
||||
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
|
||||
import RoomPreviewBar from "../../../../../src/components/views/rooms/RoomPreviewBar";
|
||||
import defaultDispatcher from "../../../../../src/dispatcher/dispatcher";
|
||||
import ModuleApi from "../../../../../src/modules/Api.ts";
|
||||
import { ModuleApi } from "../../../../../src/modules/Api.ts";
|
||||
|
||||
jest.mock("../../../../../src/IdentityAuthClient", () => {
|
||||
return jest.fn().mockImplementation(() => {
|
||||
@ -500,7 +500,7 @@ describe("<RoomPreviewBar />", () => {
|
||||
});
|
||||
|
||||
it("should render Module roomPreviewBarRenderer if specified", () => {
|
||||
jest.spyOn(ModuleApi.customComponents, "roomPreviewBarRenderer", "get").mockReturnValue(() => (
|
||||
jest.spyOn(ModuleApi.instance.customComponents, "roomPreviewBarRenderer", "get").mockReturnValue(() => (
|
||||
<>Test component</>
|
||||
));
|
||||
const { getByText } = render(<RoomPreviewBar />);
|
||||
|
||||
@ -13,10 +13,13 @@ import {
|
||||
JSONEventFactory,
|
||||
MessageEventFactory,
|
||||
pickFactory,
|
||||
renderTile,
|
||||
RoomCreateEventFactory,
|
||||
} from "../../../src/events/EventTileFactory";
|
||||
import SettingsStore from "../../../src/settings/SettingsStore";
|
||||
import { createTestClient, mkEvent } from "../../test-utils";
|
||||
import { TimelineRenderingType } from "../../../src/contexts/RoomContext";
|
||||
import { ModuleApi } from "../../../src/modules/Api";
|
||||
|
||||
const roomId = "!room:example.com";
|
||||
|
||||
@ -205,3 +208,54 @@ describe("pickFactory", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("renderTile", () => {
|
||||
let client: MatrixClient;
|
||||
|
||||
beforeEach(() => {
|
||||
client = createTestClient();
|
||||
});
|
||||
|
||||
it("rendering a tile defers to the module API", () => {
|
||||
ModuleApi.instance.customComponents.renderMessage = jest.fn();
|
||||
|
||||
const messageEvent = mkEvent({
|
||||
event: true,
|
||||
type: EventType.RoomMessage,
|
||||
user: client.getUserId()!,
|
||||
room: roomId,
|
||||
content: {
|
||||
msgtype: MsgType.Text,
|
||||
},
|
||||
});
|
||||
|
||||
renderTile(TimelineRenderingType.Room, { mxEvent: messageEvent, showHiddenEvents: false }, client);
|
||||
|
||||
expect(ModuleApi.instance.customComponents.renderMessage).toHaveBeenCalledWith(
|
||||
{
|
||||
mxEvent: messageEvent,
|
||||
},
|
||||
expect.any(Function),
|
||||
);
|
||||
});
|
||||
|
||||
it("rendering a tile for a message of unknown type defers to the module API", () => {
|
||||
ModuleApi.instance.customComponents.renderMessage = jest.fn();
|
||||
|
||||
const messageEvent = mkEvent({
|
||||
event: true,
|
||||
type: "weird.type",
|
||||
user: client.getUserId()!,
|
||||
room: roomId,
|
||||
content: {
|
||||
msgtype: MsgType.Text,
|
||||
},
|
||||
});
|
||||
|
||||
renderTile(TimelineRenderingType.Room, { mxEvent: messageEvent, showHiddenEvents: false }, client);
|
||||
|
||||
expect(ModuleApi.instance.customComponents.renderMessage).toHaveBeenCalledWith({
|
||||
mxEvent: messageEvent,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user