mirror of
https://github.com/vector-im/element-web.git
synced 2026-05-10 06:36:21 +02:00
Fix circular dependency
This commit is contained in:
parent
353609c05d
commit
fdbe414152
@ -11,45 +11,86 @@ import {
|
||||
Watchable,
|
||||
} from "@element-hq/element-web-module-api";
|
||||
|
||||
import RoomListStoreV3, { LISTS_LOADED_EVENT, LISTS_UPDATE_EVENT } from "../stores/room-list-v3/RoomListStoreV3";
|
||||
import type { RoomListStoreV3Class, RoomListStoreV3Event } from "../stores/room-list-v3/RoomListStoreV3";
|
||||
import { Room as ModuleRoom } from "./models/Room";
|
||||
|
||||
class RoomListStoreApi implements IRoomListStore {
|
||||
interface RlsEvents {
|
||||
LISTS_LOADED_EVENT: RoomListStoreV3Event.ListsLoaded;
|
||||
LISTS_UPDATE_EVENT: RoomListStoreV3Event.ListsUpdate;
|
||||
}
|
||||
|
||||
export class RoomListStoreApi implements IRoomListStore {
|
||||
private rls?: RoomListStoreV3Class;
|
||||
private LISTS_LOADED_EVENT?: RoomListStoreV3Event.ListsLoaded;
|
||||
private LISTS_UPDATE_EVENT?: RoomListStoreV3Event.ListsUpdate;
|
||||
public readonly moduleLoadPromise: Promise<void>;
|
||||
|
||||
public constructor() {
|
||||
this.moduleLoadPromise = this.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the RLS through a dynamic import. This is necessary to prevent
|
||||
* circular dependency issues.
|
||||
*/
|
||||
private async init(): Promise<void> {
|
||||
const module = await import("../stores/room-list-v3/RoomListStoreV3");
|
||||
this.rls = module.default.instance;
|
||||
this.LISTS_LOADED_EVENT = module.LISTS_LOADED_EVENT;
|
||||
this.LISTS_UPDATE_EVENT = module.LISTS_UPDATE_EVENT;
|
||||
}
|
||||
|
||||
public getRooms(): RoomsWatchable {
|
||||
return new RoomsWatchable();
|
||||
return new RoomsWatchable(this.roomListStore, this.events);
|
||||
}
|
||||
|
||||
private get events(): RlsEvents {
|
||||
if (!this.LISTS_LOADED_EVENT || !this.LISTS_UPDATE_EVENT) {
|
||||
throw new Error("Event type was not loaded correctly, did you forget to await waitForReady()?");
|
||||
}
|
||||
return { LISTS_LOADED_EVENT: this.LISTS_LOADED_EVENT, LISTS_UPDATE_EVENT: this.LISTS_UPDATE_EVENT };
|
||||
}
|
||||
|
||||
private get roomListStore(): RoomListStoreV3Class {
|
||||
if (!this.rls) {
|
||||
throw new Error("rls is undefined, did you forget to await waitForReady()?");
|
||||
}
|
||||
return this.rls;
|
||||
}
|
||||
|
||||
public async waitForReady(): Promise<void> {
|
||||
// Check if RLS is already loaded
|
||||
if (!RoomListStoreV3.instance.isLoadingRooms) return;
|
||||
// Wait for the module to load first
|
||||
await this.moduleLoadPromise;
|
||||
|
||||
// Return a promise that resolves when RLS has loaded
|
||||
let resolve: () => void;
|
||||
const promise: Promise<void> = new Promise((_resolve) => {
|
||||
resolve = _resolve;
|
||||
});
|
||||
RoomListStoreV3.instance.once(LISTS_LOADED_EVENT, () => {
|
||||
resolve();
|
||||
});
|
||||
return promise;
|
||||
// Check if RLS is already loaded
|
||||
if (!this.roomListStore.isLoadingRooms) return;
|
||||
|
||||
// Await a promise that resolves when RLS has loaded
|
||||
const { promise, resolve } = Promise.withResolvers<void>();
|
||||
const { LISTS_LOADED_EVENT } = this.events;
|
||||
this.roomListStore.once(LISTS_LOADED_EVENT, resolve);
|
||||
await promise;
|
||||
}
|
||||
}
|
||||
|
||||
class RoomsWatchable extends Watchable<Room[]> {
|
||||
public constructor() {
|
||||
super(RoomListStoreV3.instance.getSortedRooms().map((sdkRoom) => new ModuleRoom(sdkRoom)));
|
||||
public constructor(
|
||||
private readonly rls: RoomListStoreV3Class,
|
||||
private readonly events: RlsEvents,
|
||||
) {
|
||||
super(rls.getSortedRooms().map((sdkRoom) => new ModuleRoom(sdkRoom)));
|
||||
}
|
||||
|
||||
private onRlsUpdate = (): void => {
|
||||
this.value = RoomListStoreV3.instance.getSortedRooms().map((sdkRoom) => new ModuleRoom(sdkRoom));
|
||||
this.value = this.rls.getSortedRooms().map((sdkRoom) => new ModuleRoom(sdkRoom));
|
||||
};
|
||||
|
||||
protected onFirstWatch(): void {
|
||||
RoomListStoreV3.instance.on(LISTS_UPDATE_EVENT, this.onRlsUpdate);
|
||||
this.rls.on(this.events.LISTS_UPDATE_EVENT, this.onRlsUpdate);
|
||||
}
|
||||
|
||||
protected onLastWatch(): void {
|
||||
RoomListStoreV3.instance.off(LISTS_UPDATE_EVENT, this.onRlsUpdate);
|
||||
this.rls.off(this.events.LISTS_UPDATE_EVENT, this.onRlsUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
import { waitFor } from "jest-matrix-react";
|
||||
|
||||
import { StoresApi } from "../../../src/modules/StoresApi";
|
||||
import { type RoomListStoreApi, StoresApi } from "../../../src/modules/StoresApi";
|
||||
import RoomListStoreV3, {
|
||||
LISTS_LOADED_EVENT,
|
||||
LISTS_UPDATE_EVENT,
|
||||
@ -30,6 +30,9 @@ describe("StoresApi", () => {
|
||||
})();
|
||||
// Shouldn't have resolved yet.
|
||||
expect(hasResolved).toStrictEqual(false);
|
||||
|
||||
// Wait for the module to load so that we can test the listener.
|
||||
await (store.roomListStore as RoomListStoreApi).moduleLoadPromise;
|
||||
// Emit the loaded event.
|
||||
RoomListStoreV3.instance.emit(LISTS_LOADED_EVENT);
|
||||
// Should resolve now.
|
||||
@ -39,28 +42,32 @@ describe("StoresApi", () => {
|
||||
});
|
||||
|
||||
describe("getRooms()", () => {
|
||||
it("should return rooms from RLS", () => {
|
||||
it("should return rooms from RLS", async () => {
|
||||
const cli = stubClient();
|
||||
const room1 = mkRoom(cli, "!foo1:m.org");
|
||||
const room2 = mkRoom(cli, "!foo2:m.org");
|
||||
const room3 = mkRoom(cli, "!foo3:m.org");
|
||||
jest.spyOn(RoomListStoreV3.instance, "getSortedRooms").mockReturnValue([room1, room2, room3]);
|
||||
jest.spyOn(RoomListStoreV3.instance, "isLoadingRooms", "get").mockReturnValue(false);
|
||||
|
||||
const store = new StoresApi();
|
||||
await store.roomListStore.waitForReady();
|
||||
const watchable = store.roomListStore.getRooms();
|
||||
expect(watchable.value).toHaveLength(3);
|
||||
expect(watchable.value[0]).toBeInstanceOf(Room);
|
||||
});
|
||||
|
||||
it("should update from RLS", () => {
|
||||
it("should update from RLS", async () => {
|
||||
const cli = stubClient();
|
||||
const room1 = mkRoom(cli, "!foo1:m.org");
|
||||
const room2 = mkRoom(cli, "!foo2:m.org");
|
||||
const rooms = [room1, room2];
|
||||
|
||||
jest.spyOn(RoomListStoreV3.instance, "getSortedRooms").mockReturnValue(rooms);
|
||||
jest.spyOn(RoomListStoreV3.instance, "isLoadingRooms", "get").mockReturnValue(false);
|
||||
|
||||
const store = new StoresApi();
|
||||
await store.roomListStore.waitForReady();
|
||||
const watchable = store.roomListStore.getRooms();
|
||||
const fn = jest.fn();
|
||||
watchable.watch(fn);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user