diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index cd943c8c8b..58e37606b2 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -38,6 +38,7 @@ import { MatrixError, ISearchResults, THREAD_RELATION_TYPE, + MatrixClient, } from "matrix-js-sdk/src/matrix"; import { KnownMembership } from "matrix-js-sdk/src/types"; import { logger } from "matrix-js-sdk/src/logger"; @@ -431,7 +432,7 @@ export class RoomView extends React.Component { canAskToJoin: this.askToJoinEnabled, promptAskToJoin: false, viewRoomOpts: { buttons: [] }, - isRoomEncrypted: false, + isRoomEncrypted: null, }; } @@ -929,7 +930,6 @@ export class RoomView extends React.Component { const callState = call?.state; this.setState({ callState, - isRoomEncrypted: await this.getIsRoomEncrypted(), }); this.context.legacyCallHandler.on(LegacyCallHandlerEvent.CallState, this.onCallState); @@ -1358,13 +1358,12 @@ export class RoomView extends React.Component { this.context.widgetLayoutStore.on(WidgetLayoutStore.emissionForRoom(room), this.onWidgetLayoutChange); this.calculatePeekRules(room); - this.updatePreviewUrlVisibility(room); this.loadMembersIfJoined(room); this.calculateRecommendedVersion(room); - this.updateE2EStatus(room); this.updatePermissions(room); this.checkWidgets(room); this.loadVirtualRoom(room); + this.updateRoomEncrypted(room); if ( this.getMainSplitContentType(room) !== MainSplitContentType.Timeline && @@ -1432,12 +1431,15 @@ export class RoomView extends React.Component { }); } - private updatePreviewUrlVisibility({ roomId }: Room): void { - // URL Previews in E2EE rooms can be a privacy leak so use a different setting which is per-room explicit - const key = this.state.isRoomEncrypted ? "urlPreviewsEnabled_e2ee" : "urlPreviewsEnabled"; - this.setState({ - showUrlPreview: SettingsStore.getValue(key, roomId), - }); + private updatePreviewUrlVisibility(room: Room): void { + this.setState(({ isRoomEncrypted }) => ({ + showUrlPreview: this.getPreviewUrlVisibility(room, isRoomEncrypted), + })); + } + + private getPreviewUrlVisibility({ roomId }: Room, isRoomEncrypted: boolean | null): boolean { + const key = isRoomEncrypted ? "urlPreviewsEnabled_e2ee" : "urlPreviewsEnabled"; + return SettingsStore.getValue(key, roomId); } private onRoom = (room: Room): void => { @@ -1490,13 +1492,18 @@ export class RoomView extends React.Component { if (this.context.client.getCrypto()) { /* At this point, the user has encryption on and cross-signing on */ - e2eStatus = await shieldStatusForRoom(this.context.client, room); - RoomView.e2eStatusCache.set(room.roomId, e2eStatus); + e2eStatus = await this.cacheAndGetE2EStatus(room, this.context.client); if (this.unmounted) return; this.setState({ e2eStatus }); } } + private async cacheAndGetE2EStatus(room: Room, client: MatrixClient): Promise { + const e2eStatus = await shieldStatusForRoom(client, room); + RoomView.e2eStatusCache.set(room.roomId, e2eStatus); + return e2eStatus; + } + private onUrlPreviewsEnabledChange = (): void => { if (this.state.room) { this.updatePreviewUrlVisibility(this.state.room); @@ -1505,25 +1512,34 @@ export class RoomView extends React.Component { private onRoomStateEvents = async (ev: MatrixEvent, state: RoomState): Promise => { // ignore if we don't have a room yet - if (!this.state.room || this.state.room.roomId !== state.roomId) return; + if (!this.state.room || this.state.room.roomId !== state.roomId || !this.context.client) return; switch (ev.getType()) { case EventType.RoomTombstone: this.setState({ tombstone: this.getRoomTombstone() }); break; - case EventType.RoomEncryption: - this.setState({ isRoomEncrypted: await this.getIsRoomEncrypted() }, () => { - if (this.state.room) { - this.updatePreviewUrlVisibility(this.state.room); - this.updateE2EStatus(this.state.room); - } - }); + case EventType.RoomEncryption: { + await this.updateRoomEncrypted(); break; + } default: this.updatePermissions(this.state.room); } }; + private async updateRoomEncrypted(room = this.state.room): Promise { + if (!room || !this.context.client) return; + + const isRoomEncrypted = await this.getIsRoomEncrypted(room.roomId); + const newE2EStatus = isRoomEncrypted ? await this.cacheAndGetE2EStatus(room, this.context.client) : null; + + this.setState({ + isRoomEncrypted, + showUrlPreview: this.getPreviewUrlVisibility(room, isRoomEncrypted), + ...(newE2EStatus && { e2eStatus: newE2EStatus }), + }); + } + private onRoomStateUpdate = (state: RoomState): void => { // ignore members in other rooms if (state.roomId !== this.state.room?.roomId) { diff --git a/test/unit-tests/components/structures/RoomView-test.tsx b/test/unit-tests/components/structures/RoomView-test.tsx index dc5212d14a..cf6b1c4ebc 100644 --- a/test/unit-tests/components/structures/RoomView-test.tsx +++ b/test/unit-tests/components/structures/RoomView-test.tsx @@ -35,6 +35,7 @@ import { cleanup, } from "jest-matrix-react"; import userEvent from "@testing-library/user-event"; +import { defer } from "matrix-js-sdk/src/utils"; import { stubClient, @@ -283,6 +284,21 @@ describe("RoomView", () => { await waitFor(() => expect(roomViewInstance.state.showUrlPreview).toBe(false)); }); + it("should not display the timeline when the room encryption is loading", async () => { + jest.spyOn(room, "getMyMembership").mockReturnValue(KnownMembership.Join); + jest.spyOn(cli, "getCrypto").mockReturnValue(crypto); + const deferred = defer(); + jest.spyOn(cli.getCrypto()!, "isEncryptionEnabledInRoom").mockImplementation(() => deferred.promise); + + const { asFragment, container } = await mountRoomView(); + expect(container.querySelector(".mx_RoomView_messagePanel")).toBeNull(); + expect(asFragment()).toMatchSnapshot(); + + deferred.resolve(true); + await waitFor(() => expect(container.querySelector(".mx_RoomView_messagePanel")).not.toBeNull()); + expect(asFragment()).toMatchSnapshot(); + }); + it("updates live timeline when a timeline reset happens", async () => { const roomViewInstance = await getRoomViewInstance(); const oldTimeline = roomViewInstance.state.liveTimeline; diff --git a/test/unit-tests/components/structures/__snapshots__/RoomView-test.tsx.snap b/test/unit-tests/components/structures/__snapshots__/RoomView-test.tsx.snap index 626f7f2b9b..eefca12067 100644 --- a/test/unit-tests/components/structures/__snapshots__/RoomView-test.tsx.snap +++ b/test/unit-tests/components/structures/__snapshots__/RoomView-test.tsx.snap @@ -62,7 +62,7 @@ exports[`RoomView for a local room in state CREATING should match the snapshot 1 style="--cpd-icon-button-size: 100%;" >
`; +exports[`RoomView should not display the timeline when the room encryption is loading 1`] = ` + +
+