Skip to content

Commit 049b635

Browse files
committed
Fix circular dependency issue
1 parent 3e55a40 commit 049b635

File tree

3 files changed

+41
-25
lines changed

3 files changed

+41
-25
lines changed

src/modules/BuiltinsApi.tsx

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,39 @@ Please see LICENSE files in the repository root for full details.
88
import React from "react";
99
import { type RoomViewProps, type BuiltinsApi } from "@element-hq/element-web-module-api";
1010

11-
import RoomAvatar from "../components/views/avatars/RoomAvatar";
1211
import { MatrixClientPeg } from "../MatrixClientPeg";
12+
import type { Room } from "matrix-js-sdk/src/matrix";
1313

1414
interface RoomViewPropsWithRoomId extends RoomViewProps {
1515
roomId: string;
1616
}
1717

18+
interface RoomAvatarProps {
19+
room: Room;
20+
size?: string;
21+
}
22+
23+
interface Components {
24+
roomView: React.ComponentType<RoomViewPropsWithRoomId>;
25+
roomAvatar: React.ComponentType<RoomAvatarProps>;
26+
}
27+
1828
export class ElementWebBuiltinsApi implements BuiltinsApi {
1929
private _roomView?: React.ComponentType<RoomViewPropsWithRoomId>;
30+
private _roomAvatar?: React.ComponentType<RoomAvatarProps>;
2031

2132
/**
22-
* Sets the components used to render a RoomView
33+
* Sets the components used by the API.
2334
*
24-
* This only really exists here because referencing RoomView directly causes a nightmare of
35+
* This only really exists here because referencing these components directly causes a nightmare of
2536
* circular dependencies that break the whole app, so instead we avoid referencing it here
2637
* and pass it in from somewhere it's already referenced (see related comment in app.tsx).
2738
*
2839
* @param component The RoomView component
2940
*/
30-
public setRoomViewComponent(component: React.ComponentType<RoomViewPropsWithRoomId>): void {
31-
this._roomView = component;
41+
public setComponents(components: Components): void {
42+
this._roomView = components.roomView;
43+
this._roomAvatar = components.roomAvatar;
3244
}
3345

3446
public getRoomViewComponent(): React.ComponentType<RoomViewPropsWithRoomId> {
@@ -39,6 +51,14 @@ export class ElementWebBuiltinsApi implements BuiltinsApi {
3951
return this._roomView;
4052
}
4153

54+
public getRoomAvatarComponent(): React.ComponentType<RoomAvatarProps> {
55+
if (!this._roomAvatar) {
56+
throw new Error("No RoomAvatar component has been set");
57+
}
58+
59+
return this._roomAvatar;
60+
}
61+
4262
public renderRoomView(roomId: string): React.ReactNode {
4363
const Component = this.getRoomViewComponent();
4464
return <Component roomId={roomId} />;
@@ -49,6 +69,7 @@ export class ElementWebBuiltinsApi implements BuiltinsApi {
4969
if (!room) {
5070
throw new Error(`No room such room: ${roomId}`);
5171
}
52-
return <RoomAvatar room={room} size={size} />;
72+
const Component = this.getRoomAvatarComponent();
73+
return <Component room={room} size={size} />;
5374
}
5475
}

src/vector/app.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import { getInitialScreenAfterLogin, getScreenFromLocation, init as initRouting,
3232
import { UserFriendlyError } from "../languageHandler";
3333
import { ModuleApi } from "../modules/Api";
3434
import { RoomView } from "../components/structures/RoomView";
35+
import RoomAvatar from "../components/views/avatars/RoomAvatar";
3536

3637
logger.log(`Application is running in ${process.env.NODE_ENV} mode`);
3738

@@ -55,10 +56,9 @@ function onTokenLoginCompleted(): void {
5556
}
5657

5758
export async function loadApp(fragParams: QueryDict, matrixChatRef: React.Ref<MatrixChat>): Promise<ReactElement> {
58-
// XXX: This lives here because RoomVew import so many things that importing it in a sensible place (eg.
59-
// the builtins module or init.tsx) causes a circular dependency. Instead, we reference RoomView here where we
60-
// already reference it indirectly via MatrixChat.
61-
ModuleApi.instance.builtins.setRoomViewComponent(RoomView);
59+
// XXX: This lives here because certain components import so many things that importing it in a sensible place (eg.
60+
// the builtins module or init.tsx) causes a circular dependency.
61+
ModuleApi.instance.builtins.setComponents({ roomView: RoomView, roomAvatar: RoomAvatar });
6262

6363
initRouting();
6464
const platform = PlatformPeg.get();

test/unit-tests/modules/BuiltinsApi-test.tsx

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,39 +10,34 @@ import { render } from "jest-matrix-react";
1010
import { ElementWebBuiltinsApi } from "../../../src/modules/BuiltinsApi";
1111
import { stubClient } from "../../test-utils/test-utils";
1212

13-
jest.mock("../../../src/components/views/avatars/RoomAvatar", () => {
14-
const Avatar: React.FC<{ room: { roomId: string }; size: string }> = ({ room, size }) => {
15-
return (
16-
<div>
17-
Avatar, {room.roomId}, {size}
18-
</div>
19-
);
20-
};
21-
return {
22-
__esModule: true,
23-
default: Avatar,
24-
};
25-
});
13+
const Avatar: React.FC<{ room: { roomId: string }; size: string }> = ({ room, size }) => {
14+
return (
15+
<div>
16+
Avatar, {room.roomId}, {size}
17+
</div>
18+
);
19+
};
2620

2721
describe("ElementWebBuiltinsApi", () => {
2822
it("returns the RoomView component thats been set", () => {
2923
const builtinsApi = new ElementWebBuiltinsApi();
3024
const sentinel = {};
31-
builtinsApi.setRoomViewComponent(sentinel as any);
25+
builtinsApi.setComponents({ roomView: sentinel, roomAvatar: Avatar } as any);
3226
expect(builtinsApi.getRoomViewComponent()).toBe(sentinel);
3327
});
3428

3529
it("returns rendered RoomView component", () => {
3630
const builtinsApi = new ElementWebBuiltinsApi();
3731
const RoomView = () => <div>hello world</div>;
38-
builtinsApi.setRoomViewComponent(RoomView as any);
32+
builtinsApi.setComponents({ roomView: RoomView, roomAvatar: Avatar } as any);
3933
const { container } = render(<> {builtinsApi.renderRoomView("!foo:m.org")}</>);
4034
expect(container).toHaveTextContent("hello world");
4135
});
4236

4337
it("returns rendered RoomAvatar component", () => {
4438
stubClient();
4539
const builtinsApi = new ElementWebBuiltinsApi();
40+
builtinsApi.setComponents({ roomView: {}, roomAvatar: Avatar } as any);
4641
const { container } = render(<> {builtinsApi.renderRoomAvatar("!foo:m.org", "50")}</>);
4742
expect(container).toHaveTextContent("Avatar");
4843
expect(container).toHaveTextContent("!foo:m.org");

0 commit comments

Comments
 (0)