Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Expose apps/widgets #12071

Merged
merged 3 commits into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
"@matrix-org/emojibase-bindings": "^1.1.2",
"@matrix-org/matrix-wysiwyg": "2.17.0",
"@matrix-org/olm": "3.2.15",
"@matrix-org/react-sdk-module-api": "^2.2.1",
"@matrix-org/react-sdk-module-api": "^2.3.0",
"@matrix-org/spec": "^1.7.0",
"@sentry/browser": "^7.0.0",
"@testing-library/react-hooks": "^8.0.1",
Expand Down
36 changes: 36 additions & 0 deletions src/modules/ProxiedModuleApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ import { Action } from "../dispatcher/actions";
import { OverwriteLoginPayload } from "../dispatcher/payloads/OverwriteLoginPayload";
import { ActionPayload } from "../dispatcher/payloads";
import SettingsStore from "../settings/SettingsStore";
import WidgetStore, { IApp } from "../stores/WidgetStore";
import { Container, WidgetLayoutStore } from "../stores/widgets/WidgetLayoutStore";

/**
* Glue between the `ModuleApi` interface and the react-sdk. Anticipates one instance
Expand Down Expand Up @@ -218,4 +220,38 @@ export class ProxiedModuleApi implements ModuleApi {
if (!maybeObj || !(typeof maybeObj === "object")) return undefined;
return maybeObj[key];
}

/**
* @override
*/
public getApps(roomId: string): IApp[] {
return WidgetStore.instance.getApps(roomId);
}

/**
* @override
*/
public getAppAvatarUrl(app: IApp, width?: number, height?: number, resizeMethod?: string): string | null {
if (!app.avatar_url) return null;
// eslint-disable-next-line no-restricted-properties
return MatrixClientPeg.safeGet().mxcUrlToHttp(app.avatar_url, width, height, resizeMethod);
}

/**
* @override
*/
public isAppInContainer(app: IApp, container: Container, roomId: string): boolean {
const room = MatrixClientPeg.safeGet().getRoom(roomId);
if (!room) return false;
return WidgetLayoutStore.instance.isInContainer(room, app, container);
}

/**
* @override
*/
public moveAppToContainer(app: IApp, container: Container, roomId: string): void {
const room = MatrixClientPeg.safeGet().getRoom(roomId);
if (!room) return;
WidgetLayoutStore.instance.moveToContainer(room, app, container);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ const createMockCompletion = (props: Partial<ICompletion>): ICompletion => {
};

jest.mock("../../../../../../src/Avatar");
jest.mock("../../../../../../src/stores/WidgetStore");
jest.mock("../../../../../../src/stores/widgets/WidgetLayoutStore");

beforeEach(() => jest.clearAllMocks());
afterAll(() => jest.restoreAllMocks());
Expand Down
2 changes: 2 additions & 0 deletions test/components/views/settings/AddPrivilegedUsers-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import UserProvider from "../../../../src/autocomplete/UserProvider";
import { ICompletion } from "../../../../src/autocomplete/Autocompleter";

jest.mock("../../../../src/autocomplete/UserProvider");
jest.mock("../../../../src/stores/WidgetStore");
jest.mock("../../../../src/stores/widgets/WidgetLayoutStore");

const completions: ICompletion[] = [
{
Expand Down
105 changes: 104 additions & 1 deletion test/modules/ProxiedModuleApi-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,18 @@ import { AccountAuthInfo } from "@matrix-org/react-sdk-module-api/lib/types/Acco
import { DialogContent, DialogProps } from "@matrix-org/react-sdk-module-api/lib/components/DialogContent";
import { screen, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { MatrixClient } from "matrix-js-sdk/src/matrix";
import { Mocked } from "jest-mock";

import { ProxiedModuleApi } from "../../src/modules/ProxiedModuleApi";
import { stubClient } from "../test-utils";
import { getMockClientWithEventEmitter, mkRoom, stubClient } from "../test-utils";
import { setLanguage } from "../../src/languageHandler";
import { ModuleRunner } from "../../src/modules/ModuleRunner";
import { registerMockModule } from "./MockModule";
import defaultDispatcher from "../../src/dispatcher/dispatcher";
import { Action } from "../../src/dispatcher/actions";
import WidgetStore, { IApp } from "../../src/stores/WidgetStore";
import { Container, WidgetLayoutStore } from "../../src/stores/widgets/WidgetLayoutStore";

describe("ProxiedApiModule", () => {
afterEach(() => {
Expand Down Expand Up @@ -254,4 +258,103 @@ describe("ProxiedApiModule", () => {
expect(dialog).not.toBeInTheDocument();
});
});

describe("getApps", () => {
it("should return apps from the widget store", () => {
const api = new ProxiedModuleApi();
const app = {} as unknown as IApp;
const apps: IApp[] = [app];

jest.spyOn(WidgetStore.instance, "getApps").mockReturnValue(apps);
expect(api.getApps("!room:example.com")).toEqual(apps);
});
});

describe("getAppAvatarUrl", () => {
const app = {} as unknown as IApp;
const avatarUrl = "https://example.com/avatar.png";

let api: ProxiedModuleApi;
let client: Mocked<MatrixClient>;

beforeEach(() => {
api = new ProxiedModuleApi();
client = getMockClientWithEventEmitter({ mxcUrlToHttp: jest.fn().mockReturnValue(avatarUrl) });
});

it("should return null if the app has no avatar URL", () => {
expect(api.getAppAvatarUrl(app)).toBeNull();
});

it("should return the app avatar URL", () => {
expect(api.getAppAvatarUrl({ ...app, avatar_url: avatarUrl })).toBe(avatarUrl);
});

it("should support optional thumbnail params", () => {
api.getAppAvatarUrl({ ...app, avatar_url: avatarUrl }, 1, 2, "3");
// eslint-disable-next-line no-restricted-properties
expect(client.mxcUrlToHttp).toHaveBeenCalledWith(avatarUrl, 1, 2, "3");
});
});

describe("isAppInContainer", () => {
const app = {} as unknown as IApp;
const roomId = "!room:example.com";

let api: ProxiedModuleApi;
let client: MatrixClient;

beforeEach(() => {
api = new ProxiedModuleApi();
client = stubClient();

jest.spyOn(WidgetLayoutStore.instance, "isInContainer");
});

it("should return false if there is no room", () => {
client.getRoom = jest.fn().mockReturnValue(null);

expect(api.isAppInContainer(app, Container.Top, roomId)).toBe(false);
expect(WidgetLayoutStore.instance.isInContainer).not.toHaveBeenCalled();
});

it("should return false if the app is not in the container", () => {
jest.spyOn(WidgetLayoutStore.instance, "isInContainer").mockReturnValue(false);
expect(api.isAppInContainer(app, Container.Top, roomId)).toBe(false);
});

it("should return true if the app is in the container", () => {
jest.spyOn(WidgetLayoutStore.instance, "isInContainer").mockReturnValue(true);
expect(api.isAppInContainer(app, Container.Top, roomId)).toBe(true);
});
});

describe("moveAppToContainer", () => {
const app = {} as unknown as IApp;
const roomId = "!room:example.com";

let api: ProxiedModuleApi;
let client: MatrixClient;

beforeEach(() => {
api = new ProxiedModuleApi();
client = stubClient();

jest.spyOn(WidgetLayoutStore.instance, "moveToContainer");
});

it("should not move if there is no room", () => {
client.getRoom = jest.fn().mockReturnValue(null);
api.moveAppToContainer(app, Container.Top, roomId);
expect(WidgetLayoutStore.instance.moveToContainer).not.toHaveBeenCalled();
});

it("should move if there is a room", () => {
const room = mkRoom(client, roomId);
client.getRoom = jest.fn().mockReturnValue(room);

api.moveAppToContainer(app, Container.Top, roomId);
expect(WidgetLayoutStore.instance.moveToContainer).toHaveBeenCalledWith(room, app, Container.Top);
});
});
});
2 changes: 2 additions & 0 deletions test/stores/AutoRageshakeStore-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import AutoRageshakeStore from "../../src/stores/AutoRageshakeStore";
import { mkEvent, stubClient } from "../test-utils";

jest.mock("../../src/rageshake/submit-rageshake");
jest.mock("../../src/stores/WidgetStore");
jest.mock("../../src/stores/widgets/WidgetLayoutStore");

describe("AutoRageshakeStore", () => {
const roomId = "!room:example.com";
Expand Down
3 changes: 3 additions & 0 deletions test/stores/RoomViewStore-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ jest.mock("../../src/utils/DMRoomMap", () => {
};
});

jest.mock("../../src/stores/WidgetStore");
jest.mock("../../src/stores/widgets/WidgetLayoutStore");

describe("RoomViewStore", function () {
const userId = "@alice:server";
const roomId = "!randomcharacters:aser.ver";
Expand Down
2 changes: 2 additions & 0 deletions test/utils/SearchInput-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import { parsePermalink } from "../../src/utils/permalinks/Permalinks";
import { transformSearchTerm } from "../../src/utils/SearchInput";

jest.mock("../../src/utils/permalinks/Permalinks");
jest.mock("../../src/stores/WidgetStore");
jest.mock("../../src/stores/widgets/WidgetLayoutStore");

describe("transforming search term", () => {
it("should return the primaryEntityId if the search term was a permalink", () => {
Expand Down
2 changes: 2 additions & 0 deletions test/voice-broadcast/components/VoiceBroadcastBody-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ jest.mock("../../../src/voice-broadcast/components/molecules/VoiceBroadcastPlayb

jest.mock("../../../src/utils/permalinks/Permalinks");
jest.mock("../../../src/utils/MediaEventHelper");
jest.mock("../../../src/stores/WidgetStore");
jest.mock("../../../src/stores/widgets/WidgetLayoutStore");

describe("VoiceBroadcastBody", () => {
const roomId = "!room:example.com";
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1847,10 +1847,10 @@
resolved "https://registry.yarnpkg.com/@matrix-org/olm/-/olm-3.2.15.tgz#55f3c1b70a21bbee3f9195cecd6846b1083451ec"
integrity sha512-S7lOrndAK9/8qOtaTq/WhttJC/o4GAzdfK0MUPpo8ApzsJEC0QjtwrkC3KBXdFP1cD1MXi/mlKR7aaoVMKgs6Q==

"@matrix-org/react-sdk-module-api@^2.2.1":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@matrix-org/react-sdk-module-api/-/react-sdk-module-api-2.2.1.tgz#308bcb42a780200d3e7994235376784b51819379"
integrity sha512-+MXTMEapzGmhArUt86GYDQirOvm19+wvQLDApmHpUQvSZvYm7wOo1EwR9FFvSKve53fu+v6gI1grnj7YLzGQ9Q==
"@matrix-org/react-sdk-module-api@^2.3.0":
version "2.3.0"
resolved "https://registry.yarnpkg.com/@matrix-org/react-sdk-module-api/-/react-sdk-module-api-2.3.0.tgz#85be5cfc73be0494c13d4dc9050cb70c58d7a08b"
integrity sha512-x/ie44yaXNtE5AKcmQiW5yINVEIJ7IjjEc35vj6j52fM8tZ9XbJx9PANKSWsdd0NJp3OqyaeHftmN6ESfx4YoQ==
dependencies:
"@babel/runtime" "^7.17.9"

Expand Down
Loading