Fix local room encryption status always not enabled (#30461)

* Fix local room encryption status always not enabled

* refactor: put back the e2e test after merge

* fix: look at e2eStatus in composer of local room

* doc: add docs to `LocalRoom.isEncryptionEnabled`

* test(e2e): check composer doesn't display unencrypted state

* test: update existing tests

* test(e2e): update existing tests

* refactor: move room encryption check in a dedicated function

* refactor: make `isEncryptionEnabled` cleaner

* test: add tests for `LocalRoom.isEncrypted`

* doc: fix `useIsEncrypted` comment

---------

Co-authored-by: Florian Duros <florian.duros@ormaz.fr>
This commit is contained in:
Valere Fedronic
2025-09-10 15:09:17 +00:00
committed by GitHub
co-authored by Florian Duros
parent 8a1fc65beb
commit cd7f1a0638
9 changed files with 106 additions and 35 deletions
+10 -3
View File
@@ -133,6 +133,7 @@ import { PinnedMessageBanner } from "../views/rooms/PinnedMessageBanner";
import { ScopedRoomContextProvider, useScopedRoomContext } from "../../contexts/ScopedRoomContext";
import { DeclineAndBlockInviteDialog } from "../views/dialogs/DeclineAndBlockInviteDialog";
import { type FocusMessageSearchPayload } from "../../dispatcher/payloads/FocusMessageSearchPayload.ts";
import { isRoomEncrypted } from "../../hooks/useIsEncrypted";
const DEBUG = false;
const PREVENT_MULTIPLE_JITSI_WITHIN = 30_000;
@@ -257,6 +258,7 @@ interface LocalRoomViewProps {
roomView: RefObject<HTMLElement | null>;
onFileDrop: (dataTransfer: DataTransfer) => Promise<void>;
mainSplitContentType: MainSplitContentType;
e2eStatus?: E2EStatus;
}
/**
@@ -304,6 +306,7 @@ function LocalRoomView(props: LocalRoomViewProps): ReactElement {
} else {
composer = (
<MessageComposer
e2eStatus={props.e2eStatus}
room={props.localRoom}
resizeNotifier={props.resizeNotifier}
permalinkCreator={props.permalinkCreator}
@@ -1397,10 +1400,13 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
}
private async getIsRoomEncrypted(roomId = this.state.roomId): Promise<boolean> {
const crypto = this.context.client?.getCrypto();
if (!crypto || !roomId) return false;
if (!roomId) return false;
return await crypto.isEncryptionEnabledInRoom(roomId);
const room = this.context.client?.getRoom(roomId);
const crypto = this.context.client?.getCrypto();
if (!room || !crypto) return false;
return isRoomEncrypted(room, crypto);
}
private async calculateRecommendedVersion(room: Room): Promise<void> {
@@ -2061,6 +2067,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
return (
<ScopedRoomContextProvider {...this.state}>
<LocalRoomView
e2eStatus={this.state.e2eStatus}
localRoom={localRoom}
resizeNotifier={this.props.resizeNotifier}
permalinkCreator={this.permalinkCreator}
+21 -1
View File
@@ -7,9 +7,29 @@ Please see LICENSE files in the repository root for full details.
*/
import { type MatrixClient, type MatrixEvent, type Room, EventType } from "matrix-js-sdk/src/matrix";
import { type CryptoApi } from "matrix-js-sdk/src/crypto-api";
import { useRoomState } from "./useRoomState.ts";
import { useAsyncMemo } from "./useAsyncMemo.ts";
import { LocalRoom } from "../models/LocalRoom.ts";
/**
* Check if a room is encrypted.
* If the room is a LocalRoom, check the state directly.
* Otherwise, use the crypto API to check if encryption is enabled in the room.
*
* @param room - The room to check.
* @param cryptoApi - The crypto API from the Matrix client.
*/
export async function isRoomEncrypted(room: Room, cryptoApi: CryptoApi): Promise<boolean> {
if (room instanceof LocalRoom) {
// For local room check the state.
// The crypto check fails because the room ID is not valid (it is a local id)
return (room as LocalRoom).isEncryptionEnabled();
}
return await cryptoApi.isEncryptionEnabledInRoom(room.roomId);
}
// Hook to simplify watching whether a Matrix room is encrypted, returns null if room is undefined or the state is loading
export function useIsEncrypted(cli: MatrixClient, room?: Room): boolean | null {
@@ -22,7 +42,7 @@ export function useIsEncrypted(cli: MatrixClient, room?: Room): boolean | null {
const crypto = cli.getCrypto();
if (!room || !crypto) return null;
return crypto.isEncryptionEnabledInRoom(room.roomId);
return isRoomEncrypted(room, crypto);
},
[room, encryptionStateEvent],
null,
+24 -1
View File
@@ -6,7 +6,14 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import { type MatrixClient, Room, PendingEventOrdering } from "matrix-js-sdk/src/matrix";
import {
type MatrixClient,
Room,
PendingEventOrdering,
MatrixEvent,
Direction,
EventType,
} from "matrix-js-sdk/src/matrix";
import { type Member } from "../utils/direct-messages";
@@ -50,4 +57,20 @@ export class LocalRoom extends Room {
public get isError(): boolean {
return this.state === LocalRoomState.ERROR;
}
/**
* Check if encryption is enabled in this room.
* True if the room has any encryption state event
*/
public isEncryptionEnabled(): boolean {
const roomState = this.getLiveTimeline().getState(Direction.Forward);
if (!roomState) return false;
const stateEvents = roomState.getStateEvents(EventType.RoomEncryption);
if (stateEvents.length === 0) return false;
// if there is an encryption state event, it is encrypted.
// Regardless of the content/algorithm, we assume it is encrypted.
return stateEvents[0] instanceof MatrixEvent;
}
}