mirror of
https://github.com/vector-im/element-web.git
synced 2024-12-01 15:11:11 +08:00
Replace MatrixClient.isRoomEncrypted
by MatrixClient.CryptoApi.isEncryptionEnabledInRoom
in RoomView
This commit is contained in:
parent
c2ce7dbc5e
commit
4be697d4c2
@ -9,7 +9,16 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
|||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { ChangeEvent, ComponentProps, createRef, ReactElement, ReactNode, RefObject, useContext } from "react";
|
import React, {
|
||||||
|
ChangeEvent,
|
||||||
|
ComponentProps,
|
||||||
|
createRef,
|
||||||
|
ReactElement,
|
||||||
|
ReactNode,
|
||||||
|
RefObject,
|
||||||
|
useContext,
|
||||||
|
JSX,
|
||||||
|
} from "react";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import {
|
import {
|
||||||
IRecommendedVersion,
|
IRecommendedVersion,
|
||||||
@ -233,6 +242,11 @@ export interface IRoomState {
|
|||||||
liveTimeline?: EventTimeline;
|
liveTimeline?: EventTimeline;
|
||||||
narrow: boolean;
|
narrow: boolean;
|
||||||
msc3946ProcessDynamicPredecessor: boolean;
|
msc3946ProcessDynamicPredecessor: boolean;
|
||||||
|
/**
|
||||||
|
* Whether the room is encrypted or not.
|
||||||
|
* If null, we are still determining the encryption status.
|
||||||
|
*/
|
||||||
|
isRoomEncrypted: boolean | null;
|
||||||
|
|
||||||
canAskToJoin: boolean;
|
canAskToJoin: boolean;
|
||||||
promptAskToJoin: boolean;
|
promptAskToJoin: boolean;
|
||||||
@ -417,6 +431,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
canAskToJoin: this.askToJoinEnabled,
|
canAskToJoin: this.askToJoinEnabled,
|
||||||
promptAskToJoin: false,
|
promptAskToJoin: false,
|
||||||
viewRoomOpts: { buttons: [] },
|
viewRoomOpts: { buttons: [] },
|
||||||
|
isRoomEncrypted: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -847,7 +862,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
return isManuallyShown && widgets.length > 0;
|
return isManuallyShown && widgets.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public async componentDidMount(): Promise<void> {
|
||||||
this.unmounted = false;
|
this.unmounted = false;
|
||||||
|
|
||||||
this.dispatcherRef = defaultDispatcher.register(this.onAction);
|
this.dispatcherRef = defaultDispatcher.register(this.onAction);
|
||||||
@ -914,6 +929,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
const callState = call?.state;
|
const callState = call?.state;
|
||||||
this.setState({
|
this.setState({
|
||||||
callState,
|
callState,
|
||||||
|
isRoomEncrypted: await this.getIsRoomEncrypted(),
|
||||||
});
|
});
|
||||||
|
|
||||||
this.context.legacyCallHandler.on(LegacyCallHandlerEvent.CallState, this.onCallState);
|
this.context.legacyCallHandler.on(LegacyCallHandlerEvent.CallState, this.onCallState);
|
||||||
@ -1377,6 +1393,13 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
return room?.currentState.getStateEvents(EventType.RoomTombstone, "") ?? undefined;
|
return room?.currentState.getStateEvents(EventType.RoomTombstone, "") ?? undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async getIsRoomEncrypted(roomId = this.state.roomId): Promise<boolean> {
|
||||||
|
const crypto = this.context.client?.getCrypto();
|
||||||
|
if (!crypto || !roomId) return false;
|
||||||
|
|
||||||
|
return await crypto.isEncryptionEnabledInRoom(roomId);
|
||||||
|
}
|
||||||
|
|
||||||
private async calculateRecommendedVersion(room: Room): Promise<void> {
|
private async calculateRecommendedVersion(room: Room): Promise<void> {
|
||||||
const upgradeRecommendation = await room.getRecommendedVersion();
|
const upgradeRecommendation = await room.getRecommendedVersion();
|
||||||
if (this.unmounted) return;
|
if (this.unmounted) return;
|
||||||
@ -1411,7 +1434,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
|
|
||||||
private updatePreviewUrlVisibility({ roomId }: Room): void {
|
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
|
// 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 = this.state.isRoomEncrypted ? "urlPreviewsEnabled_e2ee" : "urlPreviewsEnabled";
|
||||||
this.setState({
|
this.setState({
|
||||||
showUrlPreview: SettingsStore.getValue(key, roomId),
|
showUrlPreview: SettingsStore.getValue(key, roomId),
|
||||||
});
|
});
|
||||||
@ -1456,7 +1479,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private async updateE2EStatus(room: Room): Promise<void> {
|
private async updateE2EStatus(room: Room): Promise<void> {
|
||||||
if (!this.context.client?.isRoomEncrypted(room.roomId)) return;
|
if (!this.context.client || !this.state.isRoomEncrypted) return;
|
||||||
|
|
||||||
// If crypto is not currently enabled, we aren't tracking devices at all,
|
// 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
|
// so we don't know what the answer is. Let's error on the safe side and show
|
||||||
@ -1480,7 +1503,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private onRoomStateEvents = (ev: MatrixEvent, state: RoomState): void => {
|
private onRoomStateEvents = async (ev: MatrixEvent, state: RoomState): Promise<void> => {
|
||||||
// ignore if we don't have a room yet
|
// 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) return;
|
||||||
|
|
||||||
@ -1488,7 +1511,14 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
case EventType.RoomTombstone:
|
case EventType.RoomTombstone:
|
||||||
this.setState({ tombstone: this.getRoomTombstone() });
|
this.setState({ tombstone: this.getRoomTombstone() });
|
||||||
break;
|
break;
|
||||||
|
case EventType.RoomEncryption:
|
||||||
|
this.setState({ isRoomEncrypted: await this.getIsRoomEncrypted() }, () => {
|
||||||
|
if (this.state.room) {
|
||||||
|
this.updatePreviewUrlVisibility(this.state.room);
|
||||||
|
this.updateE2EStatus(this.state.room);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
this.updatePermissions(this.state.room);
|
this.updatePermissions(this.state.room);
|
||||||
}
|
}
|
||||||
@ -2027,6 +2057,8 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
|
|
||||||
public render(): ReactNode {
|
public render(): ReactNode {
|
||||||
if (!this.context.client) return null;
|
if (!this.context.client) return null;
|
||||||
|
const { isRoomEncrypted } = this.state;
|
||||||
|
const isRoomEncryptionLoading = isRoomEncrypted === null;
|
||||||
|
|
||||||
if (this.state.room instanceof LocalRoom) {
|
if (this.state.room instanceof LocalRoom) {
|
||||||
if (this.state.room.state === LocalRoomState.CREATING) {
|
if (this.state.room.state === LocalRoomState.CREATING) {
|
||||||
@ -2242,14 +2274,16 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
let aux: JSX.Element | undefined;
|
let aux: JSX.Element | undefined;
|
||||||
let previewBar;
|
let previewBar;
|
||||||
if (this.state.timelineRenderingType === TimelineRenderingType.Search) {
|
if (this.state.timelineRenderingType === TimelineRenderingType.Search) {
|
||||||
|
if (!isRoomEncryptionLoading) {
|
||||||
aux = (
|
aux = (
|
||||||
<RoomSearchAuxPanel
|
<RoomSearchAuxPanel
|
||||||
searchInfo={this.state.search}
|
searchInfo={this.state.search}
|
||||||
onCancelClick={this.onCancelSearchClick}
|
onCancelClick={this.onCancelSearchClick}
|
||||||
onSearchScopeChange={this.onSearchScopeChange}
|
onSearchScopeChange={this.onSearchScopeChange}
|
||||||
isRoomEncrypted={this.context.client.isRoomEncrypted(this.state.room.roomId)}
|
isRoomEncrypted={isRoomEncrypted}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
} else if (showRoomUpgradeBar) {
|
} else if (showRoomUpgradeBar) {
|
||||||
aux = <RoomUpgradeWarningBar room={this.state.room} />;
|
aux = <RoomUpgradeWarningBar room={this.state.room} />;
|
||||||
} else if (myMembership !== KnownMembership.Join) {
|
} else if (myMembership !== KnownMembership.Join) {
|
||||||
@ -2325,8 +2359,10 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
|
|
||||||
let messageComposer;
|
let messageComposer;
|
||||||
const showComposer =
|
const showComposer =
|
||||||
|
!isRoomEncryptionLoading &&
|
||||||
// joined and not showing search results
|
// joined and not showing search results
|
||||||
myMembership === KnownMembership.Join && !this.state.search;
|
myMembership === KnownMembership.Join &&
|
||||||
|
!this.state.search;
|
||||||
if (showComposer) {
|
if (showComposer) {
|
||||||
messageComposer = (
|
messageComposer = (
|
||||||
<MessageComposer
|
<MessageComposer
|
||||||
@ -2367,7 +2403,9 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
highlightedEventId = this.state.initialEventId;
|
highlightedEventId = this.state.initialEventId;
|
||||||
}
|
}
|
||||||
|
|
||||||
const messagePanel = (
|
let messagePanel: JSX.Element | undefined;
|
||||||
|
if (!isRoomEncryptionLoading) {
|
||||||
|
messagePanel = (
|
||||||
<TimelinePanel
|
<TimelinePanel
|
||||||
ref={this.gatherTimelinePanelRef}
|
ref={this.gatherTimelinePanelRef}
|
||||||
timelineSet={this.state.room.getUnfilteredTimelineSet()}
|
timelineSet={this.state.room.getUnfilteredTimelineSet()}
|
||||||
@ -2395,6 +2433,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
editState={this.state.editState}
|
editState={this.state.editState}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let topUnreadMessagesBar: JSX.Element | undefined;
|
let topUnreadMessagesBar: JSX.Element | undefined;
|
||||||
// Do not show TopUnreadMessagesBar if we have search results showing, it makes no sense
|
// Do not show TopUnreadMessagesBar if we have search results showing, it makes no sense
|
||||||
@ -2415,7 +2454,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const showRightPanel = this.state.room && this.state.showRightPanel;
|
const showRightPanel = !isRoomEncryptionLoading && this.state.room && this.state.showRightPanel;
|
||||||
|
|
||||||
const rightPanel = showRightPanel ? (
|
const rightPanel = showRightPanel ? (
|
||||||
<RightPanel
|
<RightPanel
|
||||||
|
@ -75,6 +75,7 @@ const RoomContext = createContext<
|
|||||||
canAskToJoin: false,
|
canAskToJoin: false,
|
||||||
promptAskToJoin: false,
|
promptAskToJoin: false,
|
||||||
viewRoomOpts: { buttons: [] },
|
viewRoomOpts: { buttons: [] },
|
||||||
|
isRoomEncrypted: null,
|
||||||
});
|
});
|
||||||
RoomContext.displayName = "RoomContext";
|
RoomContext.displayName = "RoomContext";
|
||||||
export default RoomContext;
|
export default RoomContext;
|
||||||
|
@ -117,7 +117,7 @@ export function createTestClient(): MatrixClient {
|
|||||||
|
|
||||||
getCrypto: jest.fn().mockReturnValue({
|
getCrypto: jest.fn().mockReturnValue({
|
||||||
getOwnDeviceKeys: jest.fn(),
|
getOwnDeviceKeys: jest.fn(),
|
||||||
getUserDeviceInfo: jest.fn(),
|
getUserDeviceInfo: jest.fn().mockResolvedValue(new Map()),
|
||||||
getUserVerificationStatus: jest.fn(),
|
getUserVerificationStatus: jest.fn(),
|
||||||
getDeviceVerificationStatus: jest.fn(),
|
getDeviceVerificationStatus: jest.fn(),
|
||||||
resetKeyBackup: jest.fn(),
|
resetKeyBackup: jest.fn(),
|
||||||
|
@ -10,16 +10,17 @@ import React, { createRef, RefObject } from "react";
|
|||||||
import { mocked, MockedObject } from "jest-mock";
|
import { mocked, MockedObject } from "jest-mock";
|
||||||
import {
|
import {
|
||||||
ClientEvent,
|
ClientEvent,
|
||||||
|
EventTimeline,
|
||||||
|
EventType,
|
||||||
|
IEvent,
|
||||||
|
JoinRule,
|
||||||
MatrixClient,
|
MatrixClient,
|
||||||
|
MatrixError,
|
||||||
|
MatrixEvent,
|
||||||
Room,
|
Room,
|
||||||
RoomEvent,
|
RoomEvent,
|
||||||
EventType,
|
|
||||||
JoinRule,
|
|
||||||
MatrixError,
|
|
||||||
RoomStateEvent,
|
RoomStateEvent,
|
||||||
MatrixEvent,
|
|
||||||
SearchResult,
|
SearchResult,
|
||||||
IEvent,
|
|
||||||
} from "matrix-js-sdk/src/matrix";
|
} from "matrix-js-sdk/src/matrix";
|
||||||
import { CryptoApi, UserVerificationStatus } from "matrix-js-sdk/src/crypto-api";
|
import { CryptoApi, UserVerificationStatus } from "matrix-js-sdk/src/crypto-api";
|
||||||
import { KnownMembership } from "matrix-js-sdk/src/types";
|
import { KnownMembership } from "matrix-js-sdk/src/types";
|
||||||
@ -87,8 +88,7 @@ describe("RoomView", () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockPlatformPeg({ reload: () => {} });
|
mockPlatformPeg({ reload: () => {} });
|
||||||
stubClient();
|
cli = mocked(stubClient());
|
||||||
cli = mocked(MatrixClientPeg.safeGet());
|
|
||||||
|
|
||||||
room = new Room(`!${roomCount++}:example.org`, cli, "@alice:example.org");
|
room = new Room(`!${roomCount++}:example.org`, cli, "@alice:example.org");
|
||||||
jest.spyOn(room, "findPredecessor");
|
jest.spyOn(room, "findPredecessor");
|
||||||
@ -247,8 +247,9 @@ describe("RoomView", () => {
|
|||||||
|
|
||||||
it("updates url preview visibility on encryption state change", async () => {
|
it("updates url preview visibility on encryption state change", async () => {
|
||||||
room.getMyMembership = jest.fn().mockReturnValue(KnownMembership.Join);
|
room.getMyMembership = jest.fn().mockReturnValue(KnownMembership.Join);
|
||||||
|
jest.spyOn(cli, "getCrypto").mockReturnValue(crypto);
|
||||||
// we should be starting unencrypted
|
// 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();
|
const roomViewInstance = await getRoomViewInstance();
|
||||||
|
|
||||||
@ -263,23 +264,23 @@ describe("RoomView", () => {
|
|||||||
expect(roomViewInstance.state.showUrlPreview).toBe(true);
|
expect(roomViewInstance.state.showUrlPreview).toBe(true);
|
||||||
|
|
||||||
// now enable encryption
|
// 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
|
// and fake an encryption event into the room to prompt it to re-check
|
||||||
await act(() =>
|
act(() => {
|
||||||
room.addLiveEvents([
|
const encryptionEvent = new MatrixEvent({
|
||||||
new MatrixEvent({
|
type: EventType.RoomEncryption,
|
||||||
type: "m.room.encryption",
|
|
||||||
sender: cli.getUserId()!,
|
sender: cli.getUserId()!,
|
||||||
content: {},
|
content: {},
|
||||||
event_id: "someid",
|
event_id: "someid",
|
||||||
room_id: room.roomId,
|
room_id: room.roomId,
|
||||||
}),
|
});
|
||||||
]),
|
const roomState = room.getLiveTimeline().getState(EventTimeline.FORWARDS)!;
|
||||||
);
|
cli.emit(RoomStateEvent.Events, encryptionEvent, roomState, null);
|
||||||
|
});
|
||||||
|
|
||||||
// URL previews should now be disabled
|
// URL previews should now be disabled
|
||||||
expect(roomViewInstance.state.showUrlPreview).toBe(false);
|
await waitFor(() => expect(roomViewInstance.state.showUrlPreview).toBe(false));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("updates live timeline when a timeline reset happens", async () => {
|
it("updates live timeline when a timeline reset happens", async () => {
|
||||||
@ -427,7 +428,8 @@ describe("RoomView", () => {
|
|||||||
]);
|
]);
|
||||||
jest.spyOn(DMRoomMap.shared(), "getUserIdForRoomId").mockReturnValue(cli.getSafeUserId());
|
jest.spyOn(DMRoomMap.shared(), "getUserIdForRoomId").mockReturnValue(cli.getSafeUserId());
|
||||||
jest.spyOn(DMRoomMap.shared(), "getRoomIds").mockReturnValue(new Set([room.roomId]));
|
jest.spyOn(DMRoomMap.shared(), "getRoomIds").mockReturnValue(new Set([room.roomId]));
|
||||||
mocked(cli).isRoomEncrypted.mockReturnValue(true);
|
jest.spyOn(cli, "getCrypto").mockReturnValue(crypto);
|
||||||
|
jest.spyOn(cli.getCrypto()!, "isEncryptionEnabledInRoom").mockResolvedValue(true);
|
||||||
await renderRoomView();
|
await renderRoomView();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user