diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 486a7fb652..aed1e8c180 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -233,6 +233,10 @@ export interface IRoomState { liveTimeline?: EventTimeline; narrow: boolean; msc3946ProcessDynamicPredecessor: boolean; + /** + * Wether the room is encrypted or not. + */ + isRoomEncrypted: boolean; canAskToJoin: boolean; promptAskToJoin: boolean; @@ -417,6 +421,7 @@ export class RoomView extends React.Component { canAskToJoin: this.askToJoinEnabled, promptAskToJoin: false, viewRoomOpts: { buttons: [] }, + isRoomEncrypted: false, }; this.dispatcherRef = dis.register(this.onAction); @@ -903,13 +908,17 @@ export class RoomView extends React.Component { return isManuallyShown && widgets.length > 0; } - public componentDidMount(): void { + public async componentDidMount(): Promise { this.onRoomViewStoreUpdate(true); + const isRoomEncrypted = Boolean( + this.state.roomId && (await this.context.client?.getCrypto()?.isEncryptionEnabledInRoom(this.state.roomId)), + ); const call = this.getCallForRoom(); const callState = call?.state; this.setState({ callState, + isRoomEncrypted, }); this.context.legacyCallHandler.on(LegacyCallHandlerEvent.CallState, this.onCallState); @@ -1405,10 +1414,12 @@ export class RoomView extends React.Component { }); } - private updatePreviewUrlVisibility({ roomId }: Room): void { + private async updatePreviewUrlVisibility({ roomId }: Room): Promise { + const isRoomEncrypted = Boolean(await this.context.client?.getCrypto()?.isEncryptionEnabledInRoom(roomId)); // URL Previews in E2EE rooms can be a privacy leak so use a different setting which is per-room explicit - const key = this.context.client?.isRoomEncrypted(roomId) ? "urlPreviewsEnabled_e2ee" : "urlPreviewsEnabled"; + const key = isRoomEncrypted ? "urlPreviewsEnabled_e2ee" : "urlPreviewsEnabled"; this.setState({ + isRoomEncrypted, showUrlPreview: SettingsStore.getValue(key, roomId), }); } @@ -1452,7 +1463,11 @@ export class RoomView extends React.Component { }; private async updateE2EStatus(room: Room): Promise { - if (!this.context.client?.isRoomEncrypted(room.roomId)) return; + if (!this.context.client) return; + + const isRoomEncrypted = Boolean(await this.context.client.getCrypto()?.isEncryptionEnabledInRoom(room.roomId)); + this.setState({ isRoomEncrypted }); + if (!isRoomEncrypted) return; // If crypto is not currently enabled, we aren't tracking devices at all, // so we don't know what the answer is. Let's error on the safe side and show @@ -2243,7 +2258,7 @@ export class RoomView extends React.Component { searchInfo={this.state.search} onCancelClick={this.onCancelSearchClick} onSearchScopeChange={this.onSearchScopeChange} - isRoomEncrypted={this.context.client.isRoomEncrypted(this.state.room.roomId)} + isRoomEncrypted={this.state.isRoomEncrypted} /> ); } else if (showRoomUpgradeBar) { diff --git a/test/test-utils/test-utils.ts b/test/test-utils/test-utils.ts index 73ff714ae0..d24805953b 100644 --- a/test/test-utils/test-utils.ts +++ b/test/test-utils/test-utils.ts @@ -117,7 +117,7 @@ export function createTestClient(): MatrixClient { getCrypto: jest.fn().mockReturnValue({ getOwnDeviceKeys: jest.fn(), - getUserDeviceInfo: jest.fn(), + getUserDeviceInfo: jest.fn().mockResolvedValue(new Map()), getUserVerificationStatus: jest.fn(), getDeviceVerificationStatus: jest.fn(), resetKeyBackup: jest.fn(), diff --git a/test/unit-tests/components/structures/RoomView-test.tsx b/test/unit-tests/components/structures/RoomView-test.tsx index b6a0f28637..67f9d844cf 100644 --- a/test/unit-tests/components/structures/RoomView-test.tsx +++ b/test/unit-tests/components/structures/RoomView-test.tsx @@ -21,6 +21,7 @@ import { SearchResult, IEvent, } from "matrix-js-sdk/src/matrix"; +import { CryptoApi, UserVerificationStatus } from "matrix-js-sdk/src/crypto-api"; import { KnownMembership } from "matrix-js-sdk/src/types"; import { fireEvent, render, screen, RenderResult, waitForElementToBeRemoved, waitFor } from "jest-matrix-react"; import userEvent from "@testing-library/user-event"; @@ -72,14 +73,14 @@ describe("RoomView", () => { let rooms: Map; let roomCount = 0; let stores: SdkContextClass; + let cryptoApi: CryptoApi; // mute some noise filterConsole("RVS update", "does not have an m.room.create event", "Current version: 1", "Version capability"); beforeEach(() => { mockPlatformPeg({ reload: () => {} }); - stubClient(); - cli = mocked(MatrixClientPeg.safeGet()); + cli = mocked(stubClient()); room = new Room(`!${roomCount++}:example.org`, cli, "@alice:example.org"); jest.spyOn(room, "findPredecessor"); @@ -97,6 +98,7 @@ describe("RoomView", () => { stores.rightPanelStore.useUnitTestClient(cli); jest.spyOn(VoipUserMapper.sharedInstance(), "getVirtualRoomForRoom").mockResolvedValue(undefined); + cryptoApi = cli.getCrypto()!; jest.spyOn(cli, "getCrypto").mockReturnValue(undefined); }); @@ -230,8 +232,9 @@ describe("RoomView", () => { it("updates url preview visibility on encryption state change", async () => { room.getMyMembership = jest.fn().mockReturnValue(KnownMembership.Join); + jest.spyOn(cli, "getCrypto").mockReturnValue(cryptoApi); // we should be starting unencrypted - expect(cli.isRoomEncrypted(room.roomId)).toEqual(false); + expect(await cli.getCrypto()?.isEncryptionEnabledInRoom(room.roomId)).toEqual(false); const roomViewInstance = await getRoomViewInstance(); @@ -246,10 +249,10 @@ describe("RoomView", () => { expect(roomViewInstance.state.showUrlPreview).toBe(true); // now enable encryption - cli.isRoomEncrypted.mockReturnValue(true); + jest.spyOn(cli.getCrypto()!, "isEncryptionEnabledInRoom").mockResolvedValue(true); // and fake an encryption event into the room to prompt it to re-check - room.addLiveEvents([ + await room.addLiveEvents([ new MatrixEvent({ type: "m.room.encryption", sender: cli.getUserId()!, @@ -341,6 +344,11 @@ describe("RoomView", () => { describe("that is encrypted", () => { beforeEach(() => { + jest.spyOn(cli, "getCrypto").mockReturnValue(cryptoApi); + jest.spyOn(cli.getCrypto()!, "isEncryptionEnabledInRoom").mockResolvedValue(true); + jest.spyOn(cli.getCrypto()!, "getUserVerificationStatus").mockResolvedValue( + new UserVerificationStatus(false, true, false), + ); mocked(cli.isRoomEncrypted).mockReturnValue(true); localRoom.encrypted = true; localRoom.currentState.setStateEvents([ @@ -402,7 +410,8 @@ describe("RoomView", () => { ]); jest.spyOn(DMRoomMap.shared(), "getUserIdForRoomId").mockReturnValue(cli.getSafeUserId()); jest.spyOn(DMRoomMap.shared(), "getRoomIds").mockReturnValue(new Set([room.roomId])); - mocked(cli).isRoomEncrypted.mockReturnValue(true); + jest.spyOn(cli, "getCrypto").mockReturnValue(cryptoApi); + jest.spyOn(cli.getCrypto()!, "isEncryptionEnabledInRoom").mockResolvedValue(true); await renderRoomView(); });