mirror of
https://github.com/element-hq/element-web.git
synced 2025-12-05 01:10:40 +00:00
Compare commits
9 Commits
develop
...
valere/fix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21668d6012 | ||
|
|
e0fac5e9ee | ||
|
|
875bdb4dab | ||
|
|
6ad823782b | ||
|
|
8cf397a912 | ||
|
|
3c20661fa1 | ||
|
|
6529731563 | ||
|
|
37018ade6d | ||
|
|
49a1b1874a |
@@ -154,8 +154,8 @@ test.describe("Cryptography", function () {
|
||||
await app.client.bootstrapCrossSigning(aliceCredentials);
|
||||
await startDMWithBob(page, bob);
|
||||
// send first message
|
||||
await page.getByRole("textbox", { name: "Send an unencrypted message…" }).fill("Hey!");
|
||||
await page.getByRole("textbox", { name: "Send an unencrypted message…" }).press("Enter");
|
||||
await page.getByRole("textbox", { name: "Send a message…" }).fill("Hey!");
|
||||
await page.getByRole("textbox", { name: "Send a message…" }).press("Enter");
|
||||
await checkDMRoom(page);
|
||||
const bobRoomId = await bobJoin(page, bob);
|
||||
// We no longer show the grey badge in the composer, check that it is not there.
|
||||
|
||||
@@ -44,6 +44,21 @@ test.describe("Create Room", () => {
|
||||
},
|
||||
);
|
||||
|
||||
test("should allow us to start a chat and show encryption state", async ({ page, user, app }) => {
|
||||
await page.getByRole("button", { name: "Add", exact: true }).click();
|
||||
await page.getByText("Start new chat").click();
|
||||
|
||||
await page.getByTestId("invite-dialog-input").fill(user.userId);
|
||||
|
||||
await page.getByRole("button", { name: "Go" }).click();
|
||||
|
||||
await expect(page.getByText("Encryption enabled")).toBeVisible();
|
||||
await expect(page.getByText("Send your first message to")).toBeVisible();
|
||||
|
||||
const composer = page.getByRole("region", { name: "Message composer" });
|
||||
await expect(composer.getByRole("textbox", { name: "Send a message…" })).toBeVisible();
|
||||
});
|
||||
|
||||
test("should create a video room", { tag: "@screenshot" }, async ({ page, user, app }) => {
|
||||
await app.settings.setValue("feature_video_rooms", null, SettingLevel.DEVICE, true);
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ async function startDM(app: ElementAppPage, page: Page, name: string): Promise<v
|
||||
await result.first().click();
|
||||
|
||||
// send first message to start DM
|
||||
const locator = page.getByRole("textbox", { name: "Send an unencrypted message…" });
|
||||
const locator = page.getByRole("textbox", { name: "Send a message…" });
|
||||
await expect(locator).toBeFocused();
|
||||
await locator.fill("Hey!");
|
||||
await locator.press("Enter");
|
||||
@@ -260,7 +260,7 @@ test.describe("Spotlight", () => {
|
||||
|
||||
// Send first message to actually start DM
|
||||
await expect(roomHeaderName(page)).toHaveText(bot2.credentials.displayName);
|
||||
const locator = page.getByRole("textbox", { name: "Send an unencrypted message…" });
|
||||
const locator = page.getByRole("textbox", { name: "Send a message…" });
|
||||
await locator.fill("Hey!");
|
||||
await locator.press("Enter");
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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 eventId 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,
|
||||
|
||||
@@ -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,18 @@ 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 {
|
||||
// check the local room state
|
||||
const encryptionState = this.getLiveTimeline()
|
||||
.getState(Direction.Forward)
|
||||
?.getStateEvents(EventType.RoomEncryption)[0];
|
||||
// if there is an encryption state event, it is encrypted.
|
||||
// Regardless of the content/algorithm, we assume it is encrypted.
|
||||
return encryptionState instanceof MatrixEvent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1183,7 +1183,7 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
|
||||
</div>
|
||||
<div
|
||||
aria-label="Message composer"
|
||||
class="mx_MessageComposer mx_MessageComposer_e2eStatus"
|
||||
class="mx_MessageComposer"
|
||||
role="region"
|
||||
>
|
||||
<div
|
||||
@@ -1192,25 +1192,6 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
|
||||
<div
|
||||
class="mx_MessageComposer_row"
|
||||
>
|
||||
<div
|
||||
class="mx_MessageComposer_e2eIconWrapper"
|
||||
>
|
||||
<svg
|
||||
aria-label="Messages in this room are not end-to-end encrypted"
|
||||
aria-labelledby="«rfm»"
|
||||
class="mx_E2EIcon mx_MessageComposer_e2eIcon"
|
||||
color="var(--cpd-color-icon-info-primary)"
|
||||
fill="currentColor"
|
||||
height="12"
|
||||
viewBox="0 0 24 24"
|
||||
width="12"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 22q-.825 0-1.412-.587A1.93 1.93 0 0 1 4 20V10q0-.825.588-1.412a2 2 0 0 1 .702-.463L1.333 4.167a1 1 0 0 1 1.414-1.414L7 7.006v-.012l13 13v.012l1.247 1.247a1 1 0 1 1-1.414 1.414l-.896-.896A1.94 1.94 0 0 1 18 22zm14-4.834V10q0-.825-.587-1.412A1.93 1.93 0 0 0 18 8h-1V6q0-2.075-1.463-3.537Q14.075 1 12 1T8.463 2.463a4.9 4.9 0 0 0-1.22 1.946L9 6.166V6q0-1.25.875-2.125A2.9 2.9 0 0 1 12 3q1.25 0 2.125.875T15 6v2h-4.166z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SendMessageComposer"
|
||||
>
|
||||
@@ -1269,14 +1250,14 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
|
||||
aria-autocomplete="list"
|
||||
aria-disabled="false"
|
||||
aria-haspopup="listbox"
|
||||
aria-label="Send an unencrypted message…"
|
||||
aria-label="Send a message…"
|
||||
aria-multiline="true"
|
||||
class="mx_BasicMessageComposer_input mx_BasicMessageComposer_input_shouldShowPillAvatar mx_BasicMessageComposer_inputEmpty"
|
||||
contenteditable="true"
|
||||
data-testid="basicmessagecomposer"
|
||||
dir="auto"
|
||||
role="textbox"
|
||||
style="--placeholder: 'Send\\ an\\ unencrypted\\ message…';"
|
||||
style="--placeholder: 'Send\\ a\\ message…';"
|
||||
tabindex="0"
|
||||
translate="no"
|
||||
>
|
||||
|
||||
@@ -112,16 +112,19 @@ describe("EncryptionEvent", () => {
|
||||
});
|
||||
|
||||
describe("for an encrypted local room", () => {
|
||||
let localRoom: LocalRoom;
|
||||
|
||||
beforeEach(() => {
|
||||
event.event.content!.algorithm = algorithm;
|
||||
jest.spyOn(client.getCrypto()!, "isEncryptionEnabledInRoom").mockResolvedValue(true);
|
||||
const localRoom = new LocalRoom(roomId, client, client.getUserId()!);
|
||||
// jest.spyOn(client.getCrypto()!, "isEncryptionEnabledInRoom").mockResolvedValue(true);
|
||||
localRoom = new LocalRoom(roomId, client, client.getUserId()!);
|
||||
jest.spyOn(localRoom, "isEncryptionEnabled").mockReturnValue(true);
|
||||
mocked(client.getRoom).mockReturnValue(localRoom);
|
||||
renderEncryptionEvent(client, event);
|
||||
});
|
||||
|
||||
it("should show the expected texts", async () => {
|
||||
expect(client.getCrypto()!.isEncryptionEnabledInRoom).toHaveBeenCalledWith(roomId);
|
||||
expect(localRoom.isEncryptionEnabled).toHaveBeenCalled();
|
||||
await checkTexts("Encryption enabled", "Messages in this chat will be end-to-end encrypted.");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user