Merge branch 'develop' of https://github.com/vector-im/element-web into t3chguy/scoped-room-context

# Conflicts:
#	src/components/structures/RoomView.tsx
This commit is contained in:
Michael Telatynski 2024-11-27 09:11:10 +00:00
commit df8f4e5503
No known key found for this signature in database
GPG key ID: A2B008A5F49F5D0D
17 changed files with 986 additions and 253 deletions

View file

@ -39,7 +39,7 @@ jobs:
- name: Docker meta - name: Docker meta
id: meta id: meta
uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5 uses: docker/metadata-action@369eb591f429131d6889c46b94e711f089e6ca96 # v5
with: with:
images: | images: |
vectorim/element-web vectorim/element-web
@ -51,7 +51,7 @@ jobs:
- name: Build and push - name: Build and push
id: build-and-push id: build-and-push
uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6 uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6
with: with:
context: . context: .
push: true push: true

View file

@ -104,7 +104,7 @@ jobs:
- name: Skip SonarCloud in merge queue - name: Skip SonarCloud in merge queue
if: github.event_name == 'merge_group' || inputs.disable_coverage == 'true' if: github.event_name == 'merge_group' || inputs.disable_coverage == 'true'
uses: guibranco/github-status-action-v2@1f26a0237cd1a57626fbb5a0eb2494c9b8797d07 uses: guibranco/github-status-action-v2@66088c44e212a906c32a047529a213d81809ec1c
with: with:
authToken: ${{ secrets.GITHUB_TOKEN }} authToken: ${{ secrets.GITHUB_TOKEN }}
state: success state: success

View file

@ -114,10 +114,10 @@
"jsrsasign": "^11.0.0", "jsrsasign": "^11.0.0",
"jszip": "^3.7.0", "jszip": "^3.7.0",
"katex": "^0.16.0", "katex": "^0.16.0",
"linkify-element": "4.1.3", "linkify-element": "4.1.4",
"linkify-react": "4.1.3", "linkify-react": "4.1.4",
"linkify-string": "4.1.3", "linkify-string": "4.1.4",
"linkifyjs": "4.1.3", "linkifyjs": "4.1.4",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"maplibre-gl": "^4.0.0", "maplibre-gl": "^4.0.0",
"matrix-encrypt-attachment": "^1.0.3", "matrix-encrypt-attachment": "^1.0.3",

View file

@ -1,4 +1,4 @@
FROM mcr.microsoft.com/playwright:v1.48.2-jammy FROM mcr.microsoft.com/playwright:v1.49.0-jammy
WORKDIR /work WORKDIR /work

View file

@ -67,6 +67,9 @@ test.describe("Cryptography", function () {
await page.locator(".mx_AuthPage").getByRole("button", { name: "I'll verify later" }).click(); await page.locator(".mx_AuthPage").getByRole("button", { name: "I'll verify later" }).click();
await app.viewRoomByName("Test room"); await app.viewRoomByName("Test room");
// In this case, the call to cryptoApi.isEncryptionEnabledInRoom is taking a long time to resolve
await page.waitForTimeout(1000);
// There should be two historical events in the timeline // There should be two historical events in the timeline
const tiles = await page.locator(".mx_EventTile").all(); const tiles = await page.locator(".mx_EventTile").all();
expect(tiles.length).toBeGreaterThanOrEqual(2); expect(tiles.length).toBeGreaterThanOrEqual(2);

View file

@ -16,6 +16,7 @@ import {
logOutOfElement, logOutOfElement,
verify, verify,
} from "./utils"; } from "./utils";
import { bootstrapCrossSigningForClient } from "../../pages/client.ts";
test.describe("Cryptography", function () { test.describe("Cryptography", function () {
test.use({ test.use({
@ -307,5 +308,30 @@ test.describe("Cryptography", function () {
const penultimate = page.locator(".mx_EventTile").filter({ hasText: "test encrypted from verified" }); const penultimate = page.locator(".mx_EventTile").filter({ hasText: "test encrypted from verified" });
await expect(penultimate.locator(".mx_EventTile_e2eIcon")).not.toBeVisible(); await expect(penultimate.locator(".mx_EventTile_e2eIcon")).not.toBeVisible();
}); });
test("should show correct shields on events sent by users with changed identity", async ({
page,
app,
bot: bob,
homeserver,
}) => {
// Verify Bob
await verify(app, bob);
// Bob logs in a new device and resets cross-signing
const bobSecondDevice = await createSecondBotDevice(page, homeserver, bob);
await bootstrapCrossSigningForClient(await bobSecondDevice.prepareClient(), bob.credentials, true);
/* should show an error for a message from a previously verified device */
await bobSecondDevice.sendMessage(testRoomId, "test encrypted from user that was previously verified");
const last = page.locator(".mx_EventTile_last");
await expect(last).toContainText("test encrypted from user that was previously verified");
const lastE2eIcon = last.locator(".mx_EventTile_e2eIcon");
await expect(lastE2eIcon).toHaveClass(/mx_EventTile_e2eIcon_warning/);
await lastE2eIcon.focus();
await expect(await app.getTooltipForElement(lastE2eIcon)).toContainText(
"Sender's verified identity has changed",
);
});
}); });
}); });

View file

@ -9,7 +9,15 @@ 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 } from "react"; import React, {
ChangeEvent,
ComponentProps,
createRef,
ReactElement,
ReactNode,
RefObject,
JSX,
} from "react";
import classNames from "classnames"; import classNames from "classnames";
import { import {
IRecommendedVersion, IRecommendedVersion,
@ -29,6 +37,7 @@ import {
MatrixError, MatrixError,
ISearchResults, ISearchResults,
THREAD_RELATION_TYPE, THREAD_RELATION_TYPE,
MatrixClient,
} from "matrix-js-sdk/src/matrix"; } from "matrix-js-sdk/src/matrix";
import { KnownMembership } from "matrix-js-sdk/src/types"; import { KnownMembership } from "matrix-js-sdk/src/types";
import { logger } from "matrix-js-sdk/src/logger"; import { logger } from "matrix-js-sdk/src/logger";
@ -234,6 +243,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;
@ -420,6 +434,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
canAskToJoin: this.askToJoinEnabled, canAskToJoin: this.askToJoinEnabled,
promptAskToJoin: false, promptAskToJoin: false,
viewRoomOpts: { buttons: [] }, viewRoomOpts: { buttons: [] },
isRoomEncrypted: null,
}; };
} }
@ -850,7 +865,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);
@ -1345,13 +1360,12 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
this.context.widgetLayoutStore.on(WidgetLayoutStore.emissionForRoom(room), this.onWidgetLayoutChange); this.context.widgetLayoutStore.on(WidgetLayoutStore.emissionForRoom(room), this.onWidgetLayoutChange);
this.calculatePeekRules(room); this.calculatePeekRules(room);
this.updatePreviewUrlVisibility(room);
this.loadMembersIfJoined(room); this.loadMembersIfJoined(room);
this.calculateRecommendedVersion(room); this.calculateRecommendedVersion(room);
this.updateE2EStatus(room);
this.updatePermissions(room); this.updatePermissions(room);
this.checkWidgets(room); this.checkWidgets(room);
this.loadVirtualRoom(room); this.loadVirtualRoom(room);
this.updateRoomEncrypted(room);
if ( if (
this.getMainSplitContentType(room) !== MainSplitContentType.Timeline && this.getMainSplitContentType(room) !== MainSplitContentType.Timeline &&
@ -1380,6 +1394,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;
@ -1412,12 +1433,15 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
}); });
} }
private updatePreviewUrlVisibility({ roomId }: Room): void { private updatePreviewUrlVisibility(room: Room): void {
// URL Previews in E2EE rooms can be a privacy leak so use a different setting which is per-room explicit this.setState(({ isRoomEncrypted }) => ({
const key = this.context.client?.isRoomEncrypted(roomId) ? "urlPreviewsEnabled_e2ee" : "urlPreviewsEnabled"; showUrlPreview: this.getPreviewUrlVisibility(room, isRoomEncrypted),
this.setState({ }));
showUrlPreview: SettingsStore.getValue(key, roomId), }
});
private getPreviewUrlVisibility({ roomId }: Room, isRoomEncrypted: boolean | null): boolean {
const key = isRoomEncrypted ? "urlPreviewsEnabled_e2ee" : "urlPreviewsEnabled";
return SettingsStore.getValue(key, roomId);
} }
private onRoom = (room: Room): void => { private onRoom = (room: Room): void => {
@ -1459,7 +1483,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
@ -1470,33 +1494,54 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
if (this.context.client.getCrypto()) { if (this.context.client.getCrypto()) {
/* At this point, the user has encryption on and cross-signing on */ /* At this point, the user has encryption on and cross-signing on */
e2eStatus = await shieldStatusForRoom(this.context.client, room); e2eStatus = await this.cacheAndGetE2EStatus(room, this.context.client);
RoomView.e2eStatusCache.set(room.roomId, e2eStatus);
if (this.unmounted) return; if (this.unmounted) return;
this.setState({ e2eStatus }); this.setState({ e2eStatus });
} }
} }
private async cacheAndGetE2EStatus(room: Room, client: MatrixClient): Promise<E2EStatus> {
const e2eStatus = await shieldStatusForRoom(client, room);
RoomView.e2eStatusCache.set(room.roomId, e2eStatus);
return e2eStatus;
}
private onUrlPreviewsEnabledChange = (): void => { private onUrlPreviewsEnabledChange = (): void => {
if (this.state.room) { if (this.state.room) {
this.updatePreviewUrlVisibility(this.state.room); this.updatePreviewUrlVisibility(this.state.room);
} }
}; };
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 || !this.context.client) return;
switch (ev.getType()) { switch (ev.getType()) {
case EventType.RoomTombstone: case EventType.RoomTombstone:
this.setState({ tombstone: this.getRoomTombstone() }); this.setState({ tombstone: this.getRoomTombstone() });
break; break;
case EventType.RoomEncryption: {
await this.updateRoomEncrypted();
break;
}
default: default:
this.updatePermissions(this.state.room); this.updatePermissions(this.state.room);
} }
}; };
private async updateRoomEncrypted(room = this.state.room): Promise<void> {
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 => { private onRoomStateUpdate = (state: RoomState): void => {
// ignore members in other rooms // ignore members in other rooms
if (state.roomId !== this.state.room?.roomId) { if (state.roomId !== this.state.room?.roomId) {
@ -2036,6 +2081,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) {
@ -2251,14 +2298,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) {
@ -2334,8 +2383,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
@ -2376,7 +2427,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()}
@ -2404,6 +2457,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
@ -2424,7 +2478,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

View file

@ -758,8 +758,13 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
shieldReasonMessage = _t("encryption|event_shield_reason_mismatched_sender_key"); shieldReasonMessage = _t("encryption|event_shield_reason_mismatched_sender_key");
break; break;
default: case EventShieldReason.SENT_IN_CLEAR:
shieldReasonMessage = _t("error|unknown"); shieldReasonMessage = _t("common|unencrypted");
break;
case EventShieldReason.VERIFICATION_VIOLATION:
shieldReasonMessage = _t("timeline|decryption_failure|sender_identity_previously_verified");
break;
} }
if (this.state.shieldColour === EventShieldColour.GREY) { if (this.state.shieldColour === EventShieldColour.GREY) {

View file

@ -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;

View file

@ -85,7 +85,7 @@ export function getRoomContext(room: Room, override: Partial<IRoomState>): IRoom
canAskToJoin: false, canAskToJoin: false,
promptAskToJoin: false, promptAskToJoin: false,
viewRoomOpts: { buttons: [] }, viewRoomOpts: { buttons: [] },
isRoomEncrypted: false,
...override, ...override,
}; };
} }

View file

@ -116,7 +116,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(),

View file

@ -10,18 +10,19 @@ 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, CryptoEvent } from "matrix-js-sdk/src/crypto-api";
import { KnownMembership } from "matrix-js-sdk/src/types"; import { KnownMembership } from "matrix-js-sdk/src/types";
import { import {
fireEvent, fireEvent,
@ -34,6 +35,7 @@ import {
cleanup, cleanup,
} from "jest-matrix-react"; } from "jest-matrix-react";
import userEvent from "@testing-library/user-event"; import userEvent from "@testing-library/user-event";
import { defer } from "matrix-js-sdk/src/utils";
import { import {
stubClient, stubClient,
@ -87,8 +89,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 +248,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 +265,38 @@ 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("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<boolean>();
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 () => { it("updates live timeline when a timeline reset happens", async () => {
@ -290,6 +307,32 @@ describe("RoomView", () => {
expect(roomViewInstance.state.liveTimeline).not.toEqual(oldTimeline); expect(roomViewInstance.state.liveTimeline).not.toEqual(oldTimeline);
}); });
it("should update when the e2e status when the user verification changed", async () => {
room.currentState.setStateEvents([
mkRoomMemberJoinEvent(cli.getSafeUserId(), room.roomId),
mkRoomMemberJoinEvent("user@example.com", room.roomId),
]);
room.getMyMembership = jest.fn().mockReturnValue(KnownMembership.Join);
// Not all the calls to cli.isRoomEncrypted are migrated, so we need to mock both.
mocked(cli.isRoomEncrypted).mockReturnValue(true);
jest.spyOn(cli, "getCrypto").mockReturnValue(crypto);
jest.spyOn(cli.getCrypto()!, "isEncryptionEnabledInRoom").mockResolvedValue(true);
jest.spyOn(cli.getCrypto()!, "getUserVerificationStatus").mockResolvedValue(
new UserVerificationStatus(false, false, false),
);
jest.spyOn(cli.getCrypto()!, "getUserDeviceInfo").mockResolvedValue(
new Map([["user@example.com", new Map<string, any>()]]),
);
const { container } = await renderRoomView();
await waitFor(() => expect(container.querySelector(".mx_E2EIcon_normal")).toBeInTheDocument());
const verificationStatus = new UserVerificationStatus(true, true, false);
jest.spyOn(cli.getCrypto()!, "getUserVerificationStatus").mockResolvedValue(verificationStatus);
cli.emit(CryptoEvent.UserTrustStatusChanged, cli.getSafeUserId(), verificationStatus);
await waitFor(() => expect(container.querySelector(".mx_E2EIcon_verified")).toBeInTheDocument());
});
describe("with virtual rooms", () => { describe("with virtual rooms", () => {
it("checks for a virtual room on initial load", async () => { it("checks for a virtual room on initial load", async () => {
const { container } = await renderRoomView(); const { container } = await renderRoomView();
@ -427,7 +470,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();
}); });

View file

@ -62,7 +62,7 @@ exports[`RoomView for a local room in state CREATING should match the snapshot 1
style="--cpd-icon-button-size: 100%;" style="--cpd-icon-button-size: 100%;"
> >
<svg <svg
aria-labelledby=":rbc:" aria-labelledby=":rg4:"
fill="currentColor" fill="currentColor"
height="1em" height="1em"
viewBox="0 0 24 24" viewBox="0 0 24 24"
@ -78,7 +78,7 @@ exports[`RoomView for a local room in state CREATING should match the snapshot 1
<button <button
aria-disabled="false" aria-disabled="false"
aria-label="Voice call" aria-label="Voice call"
aria-labelledby=":rbh:" aria-labelledby=":rg9:"
class="_icon-button_bh2qc_17" class="_icon-button_bh2qc_17"
role="button" role="button"
style="--cpd-icon-button-size: 32px;" style="--cpd-icon-button-size: 32px;"
@ -103,7 +103,7 @@ exports[`RoomView for a local room in state CREATING should match the snapshot 1
</button> </button>
<button <button
aria-label="Room info" aria-label="Room info"
aria-labelledby=":rbm:" aria-labelledby=":rge:"
class="_icon-button_bh2qc_17" class="_icon-button_bh2qc_17"
role="button" role="button"
style="--cpd-icon-button-size: 32px;" style="--cpd-icon-button-size: 32px;"
@ -128,7 +128,7 @@ exports[`RoomView for a local room in state CREATING should match the snapshot 1
</button> </button>
<button <button
aria-label="Threads" aria-label="Threads"
aria-labelledby=":rbr:" aria-labelledby=":rgj:"
class="_icon-button_bh2qc_17" class="_icon-button_bh2qc_17"
role="button" role="button"
style="--cpd-icon-button-size: 32px;" style="--cpd-icon-button-size: 32px;"
@ -157,7 +157,7 @@ exports[`RoomView for a local room in state CREATING should match the snapshot 1
> >
<div <div
aria-label="2 members" aria-label="2 members"
aria-labelledby=":rc0:" aria-labelledby=":rgo:"
class="mx_AccessibleButton mx_FacePile" class="mx_AccessibleButton mx_FacePile"
role="button" role="button"
tabindex="0" tabindex="0"
@ -280,7 +280,7 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`]
style="--cpd-icon-button-size: 100%;" style="--cpd-icon-button-size: 100%;"
> >
<svg <svg
aria-labelledby=":rca:" aria-labelledby=":rh2:"
fill="currentColor" fill="currentColor"
height="1em" height="1em"
viewBox="0 0 24 24" viewBox="0 0 24 24"
@ -296,7 +296,7 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`]
<button <button
aria-disabled="false" aria-disabled="false"
aria-label="Voice call" aria-label="Voice call"
aria-labelledby=":rcf:" aria-labelledby=":rh7:"
class="_icon-button_bh2qc_17" class="_icon-button_bh2qc_17"
role="button" role="button"
style="--cpd-icon-button-size: 32px;" style="--cpd-icon-button-size: 32px;"
@ -321,7 +321,7 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`]
</button> </button>
<button <button
aria-label="Room info" aria-label="Room info"
aria-labelledby=":rck:" aria-labelledby=":rhc:"
class="_icon-button_bh2qc_17" class="_icon-button_bh2qc_17"
role="button" role="button"
style="--cpd-icon-button-size: 32px;" style="--cpd-icon-button-size: 32px;"
@ -346,7 +346,7 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`]
</button> </button>
<button <button
aria-label="Threads" aria-label="Threads"
aria-labelledby=":rcp:" aria-labelledby=":rhh:"
class="_icon-button_bh2qc_17" class="_icon-button_bh2qc_17"
role="button" role="button"
style="--cpd-icon-button-size: 32px;" style="--cpd-icon-button-size: 32px;"
@ -375,7 +375,7 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`]
> >
<div <div
aria-label="2 members" aria-label="2 members"
aria-labelledby=":rcu:" aria-labelledby=":rhm:"
class="mx_AccessibleButton mx_FacePile" class="mx_AccessibleButton mx_FacePile"
role="button" role="button"
tabindex="0" tabindex="0"
@ -583,7 +583,7 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] =
style="--cpd-icon-button-size: 100%;" style="--cpd-icon-button-size: 100%;"
> >
<svg <svg
aria-labelledby=":r70:" aria-labelledby=":rbo:"
fill="currentColor" fill="currentColor"
height="1em" height="1em"
viewBox="0 0 24 24" viewBox="0 0 24 24"
@ -599,7 +599,7 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] =
<button <button
aria-disabled="false" aria-disabled="false"
aria-label="Voice call" aria-label="Voice call"
aria-labelledby=":r75:" aria-labelledby=":rbt:"
class="_icon-button_bh2qc_17" class="_icon-button_bh2qc_17"
role="button" role="button"
style="--cpd-icon-button-size: 32px;" style="--cpd-icon-button-size: 32px;"
@ -624,7 +624,7 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] =
</button> </button>
<button <button
aria-label="Room info" aria-label="Room info"
aria-labelledby=":r7a:" aria-labelledby=":rc2:"
class="_icon-button_bh2qc_17" class="_icon-button_bh2qc_17"
role="button" role="button"
style="--cpd-icon-button-size: 32px;" style="--cpd-icon-button-size: 32px;"
@ -649,7 +649,7 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] =
</button> </button>
<button <button
aria-label="Threads" aria-label="Threads"
aria-labelledby=":r7f:" aria-labelledby=":rc7:"
class="_icon-button_bh2qc_17" class="_icon-button_bh2qc_17"
role="button" role="button"
style="--cpd-icon-button-size: 32px;" style="--cpd-icon-button-size: 32px;"
@ -678,7 +678,7 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] =
> >
<div <div
aria-label="2 members" aria-label="2 members"
aria-labelledby=":r7k:" aria-labelledby=":rcc:"
class="mx_AccessibleButton mx_FacePile" class="mx_AccessibleButton mx_FacePile"
role="button" role="button"
tabindex="0" tabindex="0"
@ -963,7 +963,7 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
style="--cpd-icon-button-size: 100%;" style="--cpd-icon-button-size: 100%;"
> >
<svg <svg
aria-labelledby=":r96:" aria-labelledby=":rdu:"
fill="currentColor" fill="currentColor"
height="1em" height="1em"
viewBox="0 0 24 24" viewBox="0 0 24 24"
@ -979,7 +979,7 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
<button <button
aria-disabled="false" aria-disabled="false"
aria-label="Voice call" aria-label="Voice call"
aria-labelledby=":r9b:" aria-labelledby=":re3:"
class="_icon-button_bh2qc_17" class="_icon-button_bh2qc_17"
role="button" role="button"
style="--cpd-icon-button-size: 32px;" style="--cpd-icon-button-size: 32px;"
@ -1004,7 +1004,7 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
</button> </button>
<button <button
aria-label="Room info" aria-label="Room info"
aria-labelledby=":r9g:" aria-labelledby=":re8:"
class="_icon-button_bh2qc_17" class="_icon-button_bh2qc_17"
role="button" role="button"
style="--cpd-icon-button-size: 32px;" style="--cpd-icon-button-size: 32px;"
@ -1029,7 +1029,7 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
</button> </button>
<button <button
aria-label="Threads" aria-label="Threads"
aria-labelledby=":r9l:" aria-labelledby=":red:"
class="_icon-button_bh2qc_17" class="_icon-button_bh2qc_17"
role="button" role="button"
style="--cpd-icon-button-size: 32px;" style="--cpd-icon-button-size: 32px;"
@ -1058,7 +1058,7 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
> >
<div <div
aria-label="2 members" aria-label="2 members"
aria-labelledby=":r9q:" aria-labelledby=":rei:"
class="mx_AccessibleButton mx_FacePile" class="mx_AccessibleButton mx_FacePile"
role="button" role="button"
tabindex="0" tabindex="0"
@ -1276,6 +1276,571 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
</div> </div>
`; `;
exports[`RoomView should not display the timeline when the room encryption is loading 1`] = `
<DocumentFragment>
<div
class="mx_RoomView"
>
<canvas
aria-hidden="true"
height="768"
style="display: block; z-index: 999999; pointer-events: none; position: fixed; top: 0px; right: 0px;"
width="0"
/>
<div
class="mx_MainSplit"
>
<div
class="mx_RoomView_body mx_MainSplit_timeline"
data-layout="group"
>
<header
class="mx_Flex mx_RoomHeader light-panel"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x);"
>
<button
aria-label="Open room settings"
aria-live="off"
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
data-color="1"
data-testid="avatar-img"
data-type="round"
role="button"
style="--cpd-avatar-size: 40px;"
tabindex="-1"
>
!
</button>
<button
aria-label="Room info"
class="mx_RoomHeader_infoWrapper"
tabindex="0"
>
<div
class="mx_Box mx_RoomHeader_info mx_Box--flex"
style="--mx-box-flex: 1;"
>
<div
aria-level="1"
class="_typography_yh5dq_162 _font-body-lg-semibold_yh5dq_83 mx_RoomHeader_heading"
dir="auto"
role="heading"
>
<span
class="mx_RoomHeader_truncated mx_lineClamp"
>
!5:example.org
</span>
</div>
</div>
</button>
<div
class="mx_Flex"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x);"
>
<button
aria-disabled="true"
aria-label="There's no one here to call"
class="_icon-button_bh2qc_17"
role="button"
style="--cpd-icon-button-size: 32px;"
tabindex="0"
>
<div
class="_indicator-icon_133tf_26"
style="--cpd-icon-button-size: 100%; --cpd-color-icon-tertiary: var(--cpd-color-icon-disabled);"
>
<svg
aria-labelledby=":r2c:"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6 4h10a2 2 0 0 1 2 2v4.286l3.35-2.871a1 1 0 0 1 1.65.76v7.65a1 1 0 0 1-1.65.76L18 13.715V18a2 2 0 0 1-2 2H6a4 4 0 0 1-4-4V8a4 4 0 0 1 4-4Z"
/>
</svg>
</div>
</button>
<button
aria-disabled="true"
aria-label="There's no one here to call"
aria-labelledby=":r2h:"
class="_icon-button_bh2qc_17"
role="button"
style="--cpd-icon-button-size: 32px;"
tabindex="0"
>
<div
class="_indicator-icon_133tf_26"
style="--cpd-icon-button-size: 100%; --cpd-color-icon-tertiary: var(--cpd-color-icon-disabled);"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m20.958 16.374.039 3.527c0 .285-.11.537-.33.756-.22.22-.472.33-.756.33a15.97 15.97 0 0 1-6.57-1.105 16.223 16.223 0 0 1-5.563-3.663 16.084 16.084 0 0 1-3.653-5.573 16.313 16.313 0 0 1-1.115-6.56c0-.285.11-.537.33-.757.22-.22.471-.329.755-.329l3.528.039a1.069 1.069 0 0 1 1.085.93l.543 3.954c.026.181.013.349-.039.504a1.088 1.088 0 0 1-.271.426l-1.64 1.64c.337.672.721 1.308 1.154 1.909.433.6 1.444 1.696 1.444 1.696s1.095 1.01 1.696 1.444c.6.433 1.237.817 1.909 1.153l1.64-1.64a1.08 1.08 0 0 1 .426-.27c.155-.052.323-.065.504-.04l3.954.543a1.069 1.069 0 0 1 .93 1.085Z"
/>
</svg>
</div>
</button>
<button
aria-label="Room info"
aria-labelledby=":r2m:"
class="_icon-button_bh2qc_17"
role="button"
style="--cpd-icon-button-size: 32px;"
tabindex="0"
>
<div
class="_indicator-icon_133tf_26"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 17a.97.97 0 0 0 .713-.288A.968.968 0 0 0 13 16v-4a.968.968 0 0 0-.287-.713A.968.968 0 0 0 12 11a.968.968 0 0 0-.713.287A.968.968 0 0 0 11 12v4c0 .283.096.52.287.712.192.192.43.288.713.288Zm0-8c.283 0 .52-.096.713-.287A.967.967 0 0 0 13 8a.967.967 0 0 0-.287-.713A.968.968 0 0 0 12 7a.968.968 0 0 0-.713.287A.967.967 0 0 0 11 8c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 13a9.738 9.738 0 0 1-3.9-.788 10.099 10.099 0 0 1-3.175-2.137c-.9-.9-1.612-1.958-2.137-3.175A9.738 9.738 0 0 1 2 12a9.74 9.74 0 0 1 .788-3.9 10.099 10.099 0 0 1 2.137-3.175c.9-.9 1.958-1.612 3.175-2.137A9.738 9.738 0 0 1 12 2a9.74 9.74 0 0 1 3.9.788 10.098 10.098 0 0 1 3.175 2.137c.9.9 1.613 1.958 2.137 3.175A9.738 9.738 0 0 1 22 12a9.738 9.738 0 0 1-.788 3.9 10.098 10.098 0 0 1-2.137 3.175c-.9.9-1.958 1.613-3.175 2.137A9.738 9.738 0 0 1 12 22Z"
/>
</svg>
</div>
</button>
<button
aria-label="Threads"
aria-labelledby=":r2r:"
class="_icon-button_bh2qc_17"
role="button"
style="--cpd-icon-button-size: 32px;"
tabindex="0"
>
<div
class="_indicator-icon_133tf_26"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4 3h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H6l-2.293 2.293c-.63.63-1.707.184-1.707-.707V5a2 2 0 0 1 2-2Zm3 7h10a.97.97 0 0 0 .712-.287A.967.967 0 0 0 18 9a.967.967 0 0 0-.288-.713A.968.968 0 0 0 17 8H7a.968.968 0 0 0-.713.287A.968.968 0 0 0 6 9c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 4h6c.283 0 .52-.096.713-.287A.968.968 0 0 0 14 13a.968.968 0 0 0-.287-.713A.968.968 0 0 0 13 12H7a.967.967 0 0 0-.713.287A.968.968 0 0 0 6 13c0 .283.096.52.287.713.192.191.43.287.713.287Z"
/>
</svg>
</div>
</button>
</div>
<div
class="_typography_yh5dq_162 _font-body-sm-medium_yh5dq_50"
>
<div
aria-label="0 members"
aria-labelledby=":r30:"
class="mx_AccessibleButton mx_FacePile"
role="button"
tabindex="0"
>
<div
class="_stacked-avatars_mcap2_111"
/>
0
</div>
</div>
</header>
<div
class="mx_AutoHideScrollbar mx_AuxPanel"
role="region"
tabindex="-1"
>
<div />
</div>
<main
class="mx_RoomView_timeline mx_RoomView_timeline_rr_enabled"
/>
<div
aria-label="Room status bar"
class="mx_RoomView_statusArea"
role="region"
>
<div
class="mx_RoomView_statusAreaBox"
>
<div
class="mx_RoomView_statusAreaBox_line"
/>
</div>
</div>
</div>
</div>
</div>
</DocumentFragment>
`;
exports[`RoomView should not display the timeline when the room encryption is loading 2`] = `
<DocumentFragment>
<div
class="mx_RoomView"
>
<canvas
aria-hidden="true"
height="768"
style="display: block; z-index: 999999; pointer-events: none; position: fixed; top: 0px; right: 0px;"
width="0"
/>
<div
class="mx_MainSplit"
>
<div
class="mx_RoomView_body mx_MainSplit_timeline"
data-layout="group"
>
<header
class="mx_Flex mx_RoomHeader light-panel"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x);"
>
<button
aria-label="Open room settings"
aria-live="off"
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
data-color="1"
data-testid="avatar-img"
data-type="round"
role="button"
style="--cpd-avatar-size: 40px;"
tabindex="-1"
>
!
</button>
<button
aria-label="Room info"
class="mx_RoomHeader_infoWrapper"
tabindex="0"
>
<div
class="mx_Box mx_RoomHeader_info mx_Box--flex"
style="--mx-box-flex: 1;"
>
<div
aria-level="1"
class="_typography_yh5dq_162 _font-body-lg-semibold_yh5dq_83 mx_RoomHeader_heading"
dir="auto"
role="heading"
>
<span
class="mx_RoomHeader_truncated mx_lineClamp"
>
!5:example.org
</span>
</div>
</div>
</button>
<div
class="mx_Flex"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x);"
>
<button
aria-disabled="true"
aria-label="There's no one here to call"
class="_icon-button_bh2qc_17"
role="button"
style="--cpd-icon-button-size: 32px;"
tabindex="0"
>
<div
class="_indicator-icon_133tf_26"
style="--cpd-icon-button-size: 100%; --cpd-color-icon-tertiary: var(--cpd-color-icon-disabled);"
>
<svg
aria-labelledby=":r2c:"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6 4h10a2 2 0 0 1 2 2v4.286l3.35-2.871a1 1 0 0 1 1.65.76v7.65a1 1 0 0 1-1.65.76L18 13.715V18a2 2 0 0 1-2 2H6a4 4 0 0 1-4-4V8a4 4 0 0 1 4-4Z"
/>
</svg>
</div>
</button>
<button
aria-disabled="true"
aria-label="There's no one here to call"
aria-labelledby=":r2h:"
class="_icon-button_bh2qc_17"
role="button"
style="--cpd-icon-button-size: 32px;"
tabindex="0"
>
<div
class="_indicator-icon_133tf_26"
style="--cpd-icon-button-size: 100%; --cpd-color-icon-tertiary: var(--cpd-color-icon-disabled);"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m20.958 16.374.039 3.527c0 .285-.11.537-.33.756-.22.22-.472.33-.756.33a15.97 15.97 0 0 1-6.57-1.105 16.223 16.223 0 0 1-5.563-3.663 16.084 16.084 0 0 1-3.653-5.573 16.313 16.313 0 0 1-1.115-6.56c0-.285.11-.537.33-.757.22-.22.471-.329.755-.329l3.528.039a1.069 1.069 0 0 1 1.085.93l.543 3.954c.026.181.013.349-.039.504a1.088 1.088 0 0 1-.271.426l-1.64 1.64c.337.672.721 1.308 1.154 1.909.433.6 1.444 1.696 1.444 1.696s1.095 1.01 1.696 1.444c.6.433 1.237.817 1.909 1.153l1.64-1.64a1.08 1.08 0 0 1 .426-.27c.155-.052.323-.065.504-.04l3.954.543a1.069 1.069 0 0 1 .93 1.085Z"
/>
</svg>
</div>
</button>
<button
aria-label="Room info"
aria-labelledby=":r2m:"
class="_icon-button_bh2qc_17"
role="button"
style="--cpd-icon-button-size: 32px;"
tabindex="0"
>
<div
class="_indicator-icon_133tf_26"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 17a.97.97 0 0 0 .713-.288A.968.968 0 0 0 13 16v-4a.968.968 0 0 0-.287-.713A.968.968 0 0 0 12 11a.968.968 0 0 0-.713.287A.968.968 0 0 0 11 12v4c0 .283.096.52.287.712.192.192.43.288.713.288Zm0-8c.283 0 .52-.096.713-.287A.967.967 0 0 0 13 8a.967.967 0 0 0-.287-.713A.968.968 0 0 0 12 7a.968.968 0 0 0-.713.287A.967.967 0 0 0 11 8c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 13a9.738 9.738 0 0 1-3.9-.788 10.099 10.099 0 0 1-3.175-2.137c-.9-.9-1.612-1.958-2.137-3.175A9.738 9.738 0 0 1 2 12a9.74 9.74 0 0 1 .788-3.9 10.099 10.099 0 0 1 2.137-3.175c.9-.9 1.958-1.612 3.175-2.137A9.738 9.738 0 0 1 12 2a9.74 9.74 0 0 1 3.9.788 10.098 10.098 0 0 1 3.175 2.137c.9.9 1.613 1.958 2.137 3.175A9.738 9.738 0 0 1 22 12a9.738 9.738 0 0 1-.788 3.9 10.098 10.098 0 0 1-2.137 3.175c-.9.9-1.958 1.613-3.175 2.137A9.738 9.738 0 0 1 12 22Z"
/>
</svg>
</div>
</button>
<button
aria-label="Threads"
aria-labelledby=":r2r:"
class="_icon-button_bh2qc_17"
role="button"
style="--cpd-icon-button-size: 32px;"
tabindex="0"
>
<div
class="_indicator-icon_133tf_26"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4 3h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H6l-2.293 2.293c-.63.63-1.707.184-1.707-.707V5a2 2 0 0 1 2-2Zm3 7h10a.97.97 0 0 0 .712-.287A.967.967 0 0 0 18 9a.967.967 0 0 0-.288-.713A.968.968 0 0 0 17 8H7a.968.968 0 0 0-.713.287A.968.968 0 0 0 6 9c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 4h6c.283 0 .52-.096.713-.287A.968.968 0 0 0 14 13a.968.968 0 0 0-.287-.713A.968.968 0 0 0 13 12H7a.967.967 0 0 0-.713.287A.968.968 0 0 0 6 13c0 .283.096.52.287.713.192.191.43.287.713.287Z"
/>
</svg>
</div>
</button>
</div>
<div
class="_typography_yh5dq_162 _font-body-sm-medium_yh5dq_50"
>
<div
aria-label="0 members"
aria-labelledby=":r30:"
class="mx_AccessibleButton mx_FacePile"
role="button"
tabindex="0"
>
<div
class="_stacked-avatars_mcap2_111"
/>
0
</div>
</div>
</header>
<div
class="mx_AutoHideScrollbar mx_AuxPanel"
role="region"
tabindex="-1"
>
<div />
</div>
<main
class="mx_RoomView_timeline mx_RoomView_timeline_rr_enabled"
>
<div
class="mx_AutoHideScrollbar mx_ScrollPanel mx_RoomView_messagePanel"
tabindex="-1"
>
<div
class="mx_RoomView_messageListWrapper"
>
<ol
aria-live="polite"
class="mx_RoomView_MessageList"
style="height: 400px;"
/>
</div>
</div>
</main>
<div
aria-label="Room status bar"
class="mx_RoomView_statusArea"
role="region"
>
<div
class="mx_RoomView_statusAreaBox"
>
<div
class="mx_RoomView_statusAreaBox_line"
/>
</div>
</div>
<div
aria-label="Message composer"
class="mx_MessageComposer mx_MessageComposer_e2eStatus"
role="region"
>
<div
class="mx_MessageComposer_wrapper"
>
<div
class="mx_MessageComposer_row"
>
<div
class="mx_MessageComposer_e2eIconWrapper"
>
<span
tabindex="0"
>
<div
aria-labelledby=":r3e:"
class="mx_E2EIcon mx_E2EIcon_verified mx_MessageComposer_e2eIcon"
/>
</span>
</div>
<div
class="mx_SendMessageComposer"
>
<div
class="mx_BasicMessageComposer"
>
<div
aria-label="Formatting"
class="mx_MessageComposerFormatBar"
role="toolbar"
>
<button
aria-label="Bold"
class="mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconBold"
role="button"
tabindex="0"
type="button"
/>
<button
aria-label="Italics"
class="mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconItalic"
role="button"
tabindex="-1"
type="button"
/>
<button
aria-label="Strikethrough"
class="mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconStrikethrough"
role="button"
tabindex="-1"
type="button"
/>
<button
aria-label="Code block"
class="mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconCode"
role="button"
tabindex="-1"
type="button"
/>
<button
aria-label="Quote"
class="mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconQuote"
role="button"
tabindex="-1"
type="button"
/>
<button
aria-label="Insert link"
class="mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconInsertLink"
role="button"
tabindex="-1"
type="button"
/>
</div>
<div
aria-autocomplete="list"
aria-disabled="false"
aria-haspopup="listbox"
aria-label="Send an encrypted 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\\ encrypted\\ message…';"
tabindex="0"
translate="no"
>
<div>
<br />
</div>
</div>
</div>
</div>
<div
class="mx_MessageComposer_actions"
>
<div
aria-label="Emoji"
class="mx_AccessibleButton mx_EmojiButton mx_MessageComposer_button mx_EmojiButton_icon"
role="button"
tabindex="0"
/>
<div
aria-label="Attachment"
class="mx_AccessibleButton mx_MessageComposer_button mx_MessageComposer_upload"
role="button"
tabindex="0"
/>
<div
aria-label="More options"
class="mx_AccessibleButton mx_MessageComposer_button mx_MessageComposer_buttonMenu"
role="button"
tabindex="0"
/>
<input
multiple=""
style="display: none;"
type="file"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</DocumentFragment>
`;
exports[`RoomView should show error view if failed to look up room alias 1`] = ` exports[`RoomView should show error view if failed to look up room alias 1`] = `
<DocumentFragment> <DocumentFragment>
<div <div
@ -1332,7 +1897,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
aria-label="Open room settings" aria-label="Open room settings"
aria-live="off" aria-live="off"
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61" class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
data-color="3" data-color="5"
data-testid="avatar-img" data-testid="avatar-img"
data-type="round" data-type="round"
role="button" role="button"
@ -1359,7 +1924,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
<span <span
class="mx_RoomHeader_truncated mx_lineClamp" class="mx_RoomHeader_truncated mx_lineClamp"
> >
!10:example.org !12:example.org
</span> </span>
</div> </div>
</div> </div>
@ -1370,7 +1935,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
> >
<button <button
aria-label="Room info" aria-label="Room info"
aria-labelledby=":r2k:" aria-labelledby=":r7c:"
class="_icon-button_bh2qc_17" class="_icon-button_bh2qc_17"
role="button" role="button"
style="--cpd-icon-button-size: 32px;" style="--cpd-icon-button-size: 32px;"
@ -1395,7 +1960,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
</button> </button>
<button <button
aria-label="Chat" aria-label="Chat"
aria-labelledby=":r2p:" aria-labelledby=":r7h:"
class="_icon-button_bh2qc_17" class="_icon-button_bh2qc_17"
role="button" role="button"
style="--cpd-icon-button-size: 32px;" style="--cpd-icon-button-size: 32px;"
@ -1420,7 +1985,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
</button> </button>
<button <button
aria-label="Threads" aria-label="Threads"
aria-labelledby=":r2u:" aria-labelledby=":r7m:"
class="_icon-button_bh2qc_17" class="_icon-button_bh2qc_17"
role="button" role="button"
style="--cpd-icon-button-size: 32px;" style="--cpd-icon-button-size: 32px;"
@ -1449,7 +2014,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
> >
<div <div
aria-label="0 members" aria-label="0 members"
aria-labelledby=":r33:" aria-labelledby=":r7r:"
class="mx_AccessibleButton mx_FacePile" class="mx_AccessibleButton mx_FacePile"
role="button" role="button"
tabindex="0" tabindex="0"
@ -1487,7 +2052,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
</p> </p>
</div> </div>
<button <button
aria-labelledby=":r3c:" aria-labelledby=":r84:"
class="_icon-button_bh2qc_17 _subtle-bg_bh2qc_38" class="_icon-button_bh2qc_17 _subtle-bg_bh2qc_38"
data-testid="base-card-close-button" data-testid="base-card-close-button"
role="button" role="button"

View file

@ -301,6 +301,8 @@ describe("EventTile", () => {
[EventShieldReason.UNKNOWN_DEVICE, "unknown or deleted device"], [EventShieldReason.UNKNOWN_DEVICE, "unknown or deleted device"],
[EventShieldReason.AUTHENTICITY_NOT_GUARANTEED, "can't be guaranteed"], [EventShieldReason.AUTHENTICITY_NOT_GUARANTEED, "can't be guaranteed"],
[EventShieldReason.MISMATCHED_SENDER_KEY, "Encrypted by an unverified session"], [EventShieldReason.MISMATCHED_SENDER_KEY, "Encrypted by an unverified session"],
[EventShieldReason.SENT_IN_CLEAR, "Not encrypted"],
[EventShieldReason.VERIFICATION_VIOLATION, "Sender's verified identity has changed"],
])("shows the correct reason code for %i (%s)", async (reasonCode: EventShieldReason, expectedText: string) => { ])("shows the correct reason code for %i (%s)", async (reasonCode: EventShieldReason, expectedText: string) => {
mxEvent = await mkEncryptedMatrixEvent({ mxEvent = await mkEncryptedMatrixEvent({
plainContent: { msgtype: "m.text", body: "msg1" }, plainContent: { msgtype: "m.text", body: "msg1" },

View file

@ -77,6 +77,7 @@ describe("<SendMessageComposer/>", () => {
canAskToJoin: false, canAskToJoin: false,
promptAskToJoin: false, promptAskToJoin: false,
viewRoomOpts: { buttons: [] }, viewRoomOpts: { buttons: [] },
isRoomEncrypted: false,
}; };
describe("createMessageContent", () => { describe("createMessageContent", () => {
it("sends plaintext messages correctly", () => { it("sends plaintext messages correctly", () => {

View file

@ -332,7 +332,7 @@ describe("linkify-matrix", () => {
const event = new MouseEvent("mousedown"); const event = new MouseEvent("mousedown");
event.preventDefault = jest.fn(); event.preventDefault = jest.fn();
handlers.click(event); handlers!.click(event);
expect(event.preventDefault).toHaveBeenCalled(); expect(event.preventDefault).toHaveBeenCalled();
expect(dispatchSpy).toHaveBeenCalledWith( expect(dispatchSpy).toHaveBeenCalledWith(
expect.objectContaining({ expect.objectContaining({
@ -372,7 +372,7 @@ describe("linkify-matrix", () => {
const event = new MouseEvent("mousedown"); const event = new MouseEvent("mousedown");
event.preventDefault = jest.fn(); event.preventDefault = jest.fn();
handlers.click(event); handlers!.click(event);
expect(event.preventDefault).toHaveBeenCalled(); expect(event.preventDefault).toHaveBeenCalled();
expect(dispatchSpy).toHaveBeenCalledWith( expect(dispatchSpy).toHaveBeenCalledWith(
expect.objectContaining({ expect.objectContaining({

302
yarn.lock
View file

@ -1585,13 +1585,13 @@
resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.8.tgz#21a907684723bbbaa5f0974cf7730bd797eb8e62" resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.8.tgz#21a907684723bbbaa5f0974cf7730bd797eb8e62"
integrity sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig== integrity sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==
"@formatjs/ecma402-abstract@2.2.3": "@formatjs/ecma402-abstract@2.2.4":
version "2.2.3" version "2.2.4"
resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-2.2.3.tgz#dc5a032e1971c709b32b9ab511fa35504a7d3bc9" resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-2.2.4.tgz#355e42d375678229d46dc8ad7a7139520dd03e7b"
integrity sha512-aElGmleuReGnk2wtYOzYFmNWYoiWWmf1pPPCYg0oiIQSJj0mjc4eUfzUXaSOJ4S8WzI/cLqnCTWjqz904FT2OQ== integrity sha512-lFyiQDVvSbQOpU+WFd//ILolGj4UgA/qXrKeZxdV14uKiAUiPAtX6XAn7WBCRi7Mx6I7EybM9E5yYn4BIpZWYg==
dependencies: dependencies:
"@formatjs/fast-memoize" "2.2.3" "@formatjs/fast-memoize" "2.2.3"
"@formatjs/intl-localematcher" "0.5.7" "@formatjs/intl-localematcher" "0.5.8"
tslib "2" tslib "2"
"@formatjs/fast-memoize@2.2.3": "@formatjs/fast-memoize@2.2.3":
@ -1601,20 +1601,20 @@
dependencies: dependencies:
tslib "2" tslib "2"
"@formatjs/intl-localematcher@0.5.7": "@formatjs/intl-localematcher@0.5.8":
version "0.5.7" version "0.5.8"
resolved "https://registry.yarnpkg.com/@formatjs/intl-localematcher/-/intl-localematcher-0.5.7.tgz#f889d076881b785d11ff993b966f527d199436d0" resolved "https://registry.yarnpkg.com/@formatjs/intl-localematcher/-/intl-localematcher-0.5.8.tgz#b11bbd04bd3551f7cadcb1ef1e231822d0e3c97e"
integrity sha512-GGFtfHGQVFe/niOZp24Kal5b2i36eE2bNL0xi9Sg/yd0TR8aLjcteApZdHmismP5QQax1cMnZM9yWySUUjJteA== integrity sha512-I+WDNWWJFZie+jkfkiK5Mp4hEDyRSEvmyfYadflOno/mmKJKcB17fEpEH0oJu/OWhhCJ8kJBDz2YMd/6cDl7Mg==
dependencies: dependencies:
tslib "2" tslib "2"
"@formatjs/intl-segmenter@^11.5.7": "@formatjs/intl-segmenter@^11.5.7":
version "11.7.3" version "11.7.4"
resolved "https://registry.yarnpkg.com/@formatjs/intl-segmenter/-/intl-segmenter-11.7.3.tgz#aeb49c33c81fec68419922c64c72188b659eaa5a" resolved "https://registry.yarnpkg.com/@formatjs/intl-segmenter/-/intl-segmenter-11.7.4.tgz#f99d87ee3f98515069285438a4913681fc243252"
integrity sha512-IvEDQRe0t0ouqaqZK2KobGt/+BhwDHdtbS8GWhdl+fjmWbhXMz2mHihu5fAYkYChum5eNfGhEF5P+bLCeYq67w== integrity sha512-pyHgFO86/CReKl20oK9jgaTMzSaG/nIMteMW8YuwUcS22EoMI1qbGTZ65oQ38KMT05SiHiMee2CP3WZvCi8YSQ==
dependencies: dependencies:
"@formatjs/ecma402-abstract" "2.2.3" "@formatjs/ecma402-abstract" "2.2.4"
"@formatjs/intl-localematcher" "0.5.7" "@formatjs/intl-localematcher" "0.5.8"
tslib "2" tslib "2"
"@humanwhocodes/config-array@^0.13.0": "@humanwhocodes/config-array@^0.13.0":
@ -2073,11 +2073,11 @@
webcrypto-core "^1.8.0" webcrypto-core "^1.8.0"
"@playwright/test@^1.40.1": "@playwright/test@^1.40.1":
version "1.48.2" version "1.49.0"
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.48.2.tgz#87dd40633f980872283404c8142a65744d3f13d6" resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.49.0.tgz#74227385b58317ee076b86b56d0e1e1b25cff01e"
integrity sha512-54w1xCWfXuax7dz4W2M9uw0gDyh+ti/0K/MxcCUxChFh37kkdxPdfZDw5QBbuPUJHr1CiHJ1hXgSs+GgeQc5Zw== integrity sha512-DMulbwQURa8rNIQrf94+jPJQ4FmOVdpE5ZppRNvWVjvhC+6sOeo28r8MgIpQRYouXRtt/FCCXU7zn20jnHR4Qw==
dependencies: dependencies:
playwright "1.48.2" playwright "1.49.0"
"@polka/url@^1.0.0-next.24": "@polka/url@^1.0.0-next.24":
version "1.0.0-next.28" version "1.0.0-next.28"
@ -2375,43 +2375,39 @@
resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8"
integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==
"@sentry-internal/browser-utils@8.37.1": "@sentry-internal/browser-utils@8.40.0":
version "8.37.1" version "8.40.0"
resolved "https://registry.yarnpkg.com/@sentry-internal/browser-utils/-/browser-utils-8.37.1.tgz#374028d8e37047aeda14b226707e6601de65996e" resolved "https://registry.yarnpkg.com/@sentry-internal/browser-utils/-/browser-utils-8.40.0.tgz#972925a9d600723dd1a022297100e97e92f4c903"
integrity sha512-OSR/V5GCsSCG7iapWtXCT/y22uo3HlawdEgfM1NIKk1mkP15UyGQtGEzZDdih2H+SNuX1mp9jQLTjr5FFp1A5w== integrity sha512-tx7gb/PWMbTEyil/XPETVeRUeS3nKHIvQY2omyebw30TbhyLnibPZsUmXJiaIysL5PcY3k9maub3W/o0Y37T7Q==
dependencies: dependencies:
"@sentry/core" "8.37.1" "@sentry/core" "8.40.0"
"@sentry/types" "8.37.1" "@sentry/types" "8.40.0"
"@sentry/utils" "8.37.1"
"@sentry-internal/feedback@8.37.1": "@sentry-internal/feedback@8.40.0":
version "8.37.1" version "8.40.0"
resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-8.37.1.tgz#e2d5fc934ca3b4925a5f5d0e63549830a1cf147e" resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-8.40.0.tgz#5549f73d32b9a2509ffb0a07bf462ed8085178ec"
integrity sha512-Se25NXbSapgS2S+JssR5YZ48b3OY4UGmAuBOafgnMW91LXMxRNWRbehZuNUmjjHwuywABMxjgu+Yp5uJDATX+g== integrity sha512-1O9F3z80HNE0VfepKS+v+dixdatNqWlrlwgvvWl4BGzzoA+XhqvZo+HWxiOt7yx7+k1TuZNrB6Gy3u/QvpozXA==
dependencies: dependencies:
"@sentry/core" "8.37.1" "@sentry/core" "8.40.0"
"@sentry/types" "8.37.1" "@sentry/types" "8.40.0"
"@sentry/utils" "8.37.1"
"@sentry-internal/replay-canvas@8.37.1": "@sentry-internal/replay-canvas@8.40.0":
version "8.37.1" version "8.40.0"
resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-8.37.1.tgz#e8a5e350e486b16938b3dd99886be23b7b6eff18" resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-8.40.0.tgz#6de0d67ee2fe3e503c6f85faeefab5df742a3ebe"
integrity sha512-1JLAaPtn1VL5vblB0BMELFV0D+KUm/iMGsrl4/JpRm0Ws5ESzQl33DhXVv1IX/ZAbx9i14EjR7MG9+Hj70tieQ== integrity sha512-Zr+m/le0SH4RowZB7rBCM0aRnvH3wZTaOFhwUk03/oGf2BRcgKuDCUMjnXKC9MyOpmey7UYXkzb8ro+81R6Q8w==
dependencies: dependencies:
"@sentry-internal/replay" "8.37.1" "@sentry-internal/replay" "8.40.0"
"@sentry/core" "8.37.1" "@sentry/core" "8.40.0"
"@sentry/types" "8.37.1" "@sentry/types" "8.40.0"
"@sentry/utils" "8.37.1"
"@sentry-internal/replay@8.37.1": "@sentry-internal/replay@8.40.0":
version "8.37.1" version "8.40.0"
resolved "https://registry.yarnpkg.com/@sentry-internal/replay/-/replay-8.37.1.tgz#6dc2e3955879f6e7ab830db1ddee54e0a9b401f3" resolved "https://registry.yarnpkg.com/@sentry-internal/replay/-/replay-8.40.0.tgz#54c7f1e3d115f9324f34e1b8875a95463a23049f"
integrity sha512-E/Plhisk/pXJjOdOU12sg8m/APTXTA21iEniidP6jW3/+O0tD/H/UovEqa4odNTqxPMa798xHQSQNt5loYiaLA== integrity sha512-0SaDsBCSWxNVgNmPKu23frrHEXzN/MKl0hIkfuO55vL5TgjLTwpgkf0Ne4rNvaZQ5omIKk9Qd63HuQP3PHAMaw==
dependencies: dependencies:
"@sentry-internal/browser-utils" "8.37.1" "@sentry-internal/browser-utils" "8.40.0"
"@sentry/core" "8.37.1" "@sentry/core" "8.40.0"
"@sentry/types" "8.37.1" "@sentry/types" "8.40.0"
"@sentry/utils" "8.37.1"
"@sentry/babel-plugin-component-annotate@2.22.5": "@sentry/babel-plugin-component-annotate@2.22.5":
version "2.22.5" version "2.22.5"
@ -2419,17 +2415,16 @@
integrity sha512-+93qwB9vTX1nj4hD8AMWowXZsZVkvmP9OwTqSh5d4kOeiJ+dZftUk4+FKeKkAX9lvY2reyHV8Gms5mo67c27RQ== integrity sha512-+93qwB9vTX1nj4hD8AMWowXZsZVkvmP9OwTqSh5d4kOeiJ+dZftUk4+FKeKkAX9lvY2reyHV8Gms5mo67c27RQ==
"@sentry/browser@^8.0.0": "@sentry/browser@^8.0.0":
version "8.37.1" version "8.40.0"
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-8.37.1.tgz#2e6e4accc395ad9e6313e07b09415370c71e5874" resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-8.40.0.tgz#de7b4531be2ac4667755e9e1b5da3808851392ae"
integrity sha512-5ym+iGiIpjIKKpMWi9S3/tXh9xneS+jqxwRTJqed3cb8i4ydfMAAP8sM3U8xMCWWABpWyIUW+fpewC0tkhE1aQ== integrity sha512-m/Yor6IDBeDHtQochu8n6z4HXrXkrPhu6+o5Ouve0Zi3ptthSoK1FOGvJxVBat3nRq0ydQyuuPuTB6WfdWbwHQ==
dependencies: dependencies:
"@sentry-internal/browser-utils" "8.37.1" "@sentry-internal/browser-utils" "8.40.0"
"@sentry-internal/feedback" "8.37.1" "@sentry-internal/feedback" "8.40.0"
"@sentry-internal/replay" "8.37.1" "@sentry-internal/replay" "8.40.0"
"@sentry-internal/replay-canvas" "8.37.1" "@sentry-internal/replay-canvas" "8.40.0"
"@sentry/core" "8.37.1" "@sentry/core" "8.40.0"
"@sentry/types" "8.37.1" "@sentry/types" "8.40.0"
"@sentry/utils" "8.37.1"
"@sentry/bundler-plugin-core@2.22.5": "@sentry/bundler-plugin-core@2.22.5":
version "2.22.5" version "2.22.5"
@ -2499,25 +2494,17 @@
"@sentry/cli-win32-i686" "2.37.0" "@sentry/cli-win32-i686" "2.37.0"
"@sentry/cli-win32-x64" "2.37.0" "@sentry/cli-win32-x64" "2.37.0"
"@sentry/core@8.37.1": "@sentry/core@8.40.0":
version "8.37.1" version "8.40.0"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.37.1.tgz#4bafb25c762ec8680874056f6160df276c1cc7c6" resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.40.0.tgz#cb5c02d12e29070bf88692c64cfd7db7700be4ea"
integrity sha512-82csXby589iDupM3VgCHJeWZagUyEEaDnbFcoZ/Z91QX2Sjq8FcF5OsforoXjw09i0XTFqlkFAnQVpDBmMXcpQ== integrity sha512-u/U2CJpG/+SmTR2bPM4ZZoPYTJAOUuxzj/0IURnvI0v9+rNu939J/fzrO9huA5IJVxS5TiYykhQm7o6I3Zuo3Q==
dependencies: dependencies:
"@sentry/types" "8.37.1" "@sentry/types" "8.40.0"
"@sentry/utils" "8.37.1"
"@sentry/types@8.37.1": "@sentry/types@8.40.0":
version "8.37.1" version "8.40.0"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.37.1.tgz#e92a7d346cfa29116568f4ffb58f65caedee0149" resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.40.0.tgz#a98d2bcc48adbc066b403713688ded3ac5eb1cec"
integrity sha512-ryMOTROLSLINKFEbHWvi7GigNrsQhsaScw2NddybJGztJQ5UhxIGESnxGxWCufBmWFDwd7+5u0jDPCVUJybp7w== integrity sha512-nuCf3U3deolPM9BjNnwCc33UtFl9ec15/r74ngAkNccn+A2JXdIAsDkGJMO/9mgSFykLe1QyeJ0pQFRisCGOiA==
"@sentry/utils@8.37.1":
version "8.37.1"
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-8.37.1.tgz#6e020cd222d56d79953ea9d4630d91b3e323ceda"
integrity sha512-Qtn2IfpII12K17txG/ZtTci35XYjYi4CxbQ3j7nXY7toGv/+MqPXwV5q2i9g94XaSXlE5Wy9/hoCZoZpZs/djA==
dependencies:
"@sentry/types" "8.37.1"
"@sentry/webpack-plugin@^2.7.1": "@sentry/webpack-plugin@^2.7.1":
version "2.22.5" version "2.22.5"
@ -2562,11 +2549,11 @@
p-map "^4.0.0" p-map "^4.0.0"
"@stylistic/eslint-plugin@^2.9.0": "@stylistic/eslint-plugin@^2.9.0":
version "2.10.1" version "2.11.0"
resolved "https://registry.yarnpkg.com/@stylistic/eslint-plugin/-/eslint-plugin-2.10.1.tgz#809924752a1a13ebff2b0b6d7884fd61d389a907" resolved "https://registry.yarnpkg.com/@stylistic/eslint-plugin/-/eslint-plugin-2.11.0.tgz#50d0289f36f7201055b7fa1729fdc1d8c46e93fa"
integrity sha512-U+4yzNXElTf9q0kEfnloI9XbOyD4cnEQCxjUI94q0+W++0GAEQvJ/slwEj9lwjDHfGADRSr+Tco/z0XJvmDfCQ== integrity sha512-PNRHbydNG5EH8NK4c+izdJlxajIR6GxcUhzsYNRsn6Myep4dsZt0qFCz3rCPnkvgO5FYibDcMqgNHUT+zvjYZw==
dependencies: dependencies:
"@typescript-eslint/utils" "^8.12.2" "@typescript-eslint/utils" "^8.13.0"
eslint-visitor-keys "^4.2.0" eslint-visitor-keys "^4.2.0"
espree "^10.3.0" espree "^10.3.0"
estraverse "^5.3.0" estraverse "^5.3.0"
@ -2836,9 +2823,9 @@
integrity sha512-aqBg5oAGo/qh/+wxUfuMadDu2WO0MEWOblyzwaM1Ske2xilUxBfgPqapAFVAfrVTDMVwa0UMarzGot8m64IAzA== integrity sha512-aqBg5oAGo/qh/+wxUfuMadDu2WO0MEWOblyzwaM1Ske2xilUxBfgPqapAFVAfrVTDMVwa0UMarzGot8m64IAzA==
"@types/css-tree@^2.3.8": "@types/css-tree@^2.3.8":
version "2.3.8" version "2.3.9"
resolved "https://registry.yarnpkg.com/@types/css-tree/-/css-tree-2.3.8.tgz#0eabc115e45051b2f7abe51ee1531074b234ed19" resolved "https://registry.yarnpkg.com/@types/css-tree/-/css-tree-2.3.9.tgz#54c404e0a803e7e660fdc08c84fe73ee5266cece"
integrity sha512-zABG3nI2UENsx7AQv63tI5/ptoAG/7kQR1H0OvG+WTWYHOR5pfAT3cGgC8SdyCrgX/TTxJBZNmx82IjCXs1juQ== integrity sha512-g1FE6xkPDP4tsccmTd6jIugjKZdxIDqAf9h2pc+4LsGgYbOyfa9phNjBHYbm6FtwIlNfT1NBx3f2zSeqO7aRAw==
"@types/diff-match-patch@^1.0.32": "@types/diff-match-patch@^1.0.32":
version "1.0.36" version "1.0.36"
@ -2861,9 +2848,9 @@
integrity sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g== integrity sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g==
"@types/express-serve-static-core@*", "@types/express-serve-static-core@^5.0.0": "@types/express-serve-static-core@*", "@types/express-serve-static-core@^5.0.0":
version "5.0.0" version "5.0.2"
resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-5.0.0.tgz#91f06cda1049e8f17eeab364798ed79c97488a1c" resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-5.0.2.tgz#812d2871e5eea17fb0bd5214dda7a7b748c0e12a"
integrity sha512-AbXMTZGt40T+KON9/Fdxx0B2WK5hsgxcfXJLr5bFpZ7b4JCex2WyQPTEKdXqfHiY5nKKBScZ7yCoO6Pvgxfvnw== integrity sha512-vluaspfvWEtE4vcSDlKRNer52DvOGrB2xv6diXy6UKyKW0lqZiWHGNApSyxOv+8DE5Z27IzVvE7hNkxg7EXIcg==
dependencies: dependencies:
"@types/node" "*" "@types/node" "*"
"@types/qs" "*" "@types/qs" "*"
@ -3065,9 +3052,9 @@
integrity sha512-yslwR0zZ3zAT1qXcCPxIcD23CZ6W6nKsl6JufSJHAmdwOBuYwCVJkaMsEo9yzxGV7ATfoX8S+RgtnajOEtKxYA== integrity sha512-yslwR0zZ3zAT1qXcCPxIcD23CZ6W6nKsl6JufSJHAmdwOBuYwCVJkaMsEo9yzxGV7ATfoX8S+RgtnajOEtKxYA==
"@types/node-fetch@^2.6.2": "@types/node-fetch@^2.6.2":
version "2.6.11" version "2.6.12"
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.11.tgz#9b39b78665dae0e82a08f02f4967d62c66f95d24" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.12.tgz#8ab5c3ef8330f13100a7479e2cd56d3386830a03"
integrity sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g== integrity sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==
dependencies: dependencies:
"@types/node" "*" "@types/node" "*"
form-data "^4.0.0" form-data "^4.0.0"
@ -3080,16 +3067,16 @@
"@types/node" "*" "@types/node" "*"
"@types/node@*": "@types/node@*":
version "22.7.6" version "22.10.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.7.6.tgz#3ec3e2b071e136cd11093c19128405e1d1f92f33" resolved "https://registry.yarnpkg.com/@types/node/-/node-22.10.0.tgz#89bfc9e82496b9c7edea3382583fa94f75896e81"
integrity sha512-/d7Rnj0/ExXDMcioS78/kf1lMzYk4BZV8MZGTBKzTGZ6/406ukkbYlIsZmMPhcR5KlkunDHQLrtAVmSq7r+mSw== integrity sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==
dependencies: dependencies:
undici-types "~6.19.2" undici-types "~6.20.0"
"@types/node@18": "@types/node@18":
version "18.19.64" version "18.19.66"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.64.tgz#122897fb79f2a9ec9c979bded01c11461b2b1478" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.66.tgz#0937a47904ceba5994eedf5cf4b6d503d8d6136c"
integrity sha512-955mDqvO2vFf/oL7V3WiUtiz+BugyX8uVbaT2H8oj3+8dRyH2FLiNdowe7eNqRM7IOIZvzDH76EoAT+gwm6aIQ== integrity sha512-14HmtUdGxFUalGRfLLn9Gc1oNWvWh5zNbsyOLo5JV6WARSeN1QcEBKRnZm9QqNfrutgsl/hY4eJW63aZ44aBCg==
dependencies: dependencies:
undici-types "~5.26.4" undici-types "~5.26.4"
@ -3317,6 +3304,14 @@
"@typescript-eslint/types" "8.14.0" "@typescript-eslint/types" "8.14.0"
"@typescript-eslint/visitor-keys" "8.14.0" "@typescript-eslint/visitor-keys" "8.14.0"
"@typescript-eslint/scope-manager@8.16.0":
version "8.16.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.16.0.tgz#ebc9a3b399a69a6052f3d88174456dd399ef5905"
integrity sha512-mwsZWubQvBki2t5565uxF0EYvG+FwdFb8bMtDuGQLdCCnGPrDEDvm1gtfynuKlnpzeBRqdFCkMf9jg1fnAK8sg==
dependencies:
"@typescript-eslint/types" "8.16.0"
"@typescript-eslint/visitor-keys" "8.16.0"
"@typescript-eslint/scope-manager@8.9.0": "@typescript-eslint/scope-manager@8.9.0":
version "8.9.0" version "8.9.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.9.0.tgz#c98fef0c4a82a484e6a1eb610a55b154d14d46f3" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.9.0.tgz#c98fef0c4a82a484e6a1eb610a55b154d14d46f3"
@ -3340,6 +3335,11 @@
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.14.0.tgz#0d33d8d0b08479c424e7d654855fddf2c71e4021" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.14.0.tgz#0d33d8d0b08479c424e7d654855fddf2c71e4021"
integrity sha512-yjeB9fnO/opvLJFAsPNYlKPnEM8+z4og09Pk504dkqonT02AyL5Z9SSqlE0XqezS93v6CXn49VHvB2G7XSsl0g== integrity sha512-yjeB9fnO/opvLJFAsPNYlKPnEM8+z4og09Pk504dkqonT02AyL5Z9SSqlE0XqezS93v6CXn49VHvB2G7XSsl0g==
"@typescript-eslint/types@8.16.0":
version "8.16.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.16.0.tgz#49c92ae1b57942458ab83d9ec7ccab3005e64737"
integrity sha512-NzrHj6thBAOSE4d9bsuRNMvk+BvaQvmY4dDglgkgGC0EW/tB3Kelnp3tAKH87GEwzoxgeQn9fNGRyFJM/xd+GQ==
"@typescript-eslint/types@8.9.0": "@typescript-eslint/types@8.9.0":
version "8.9.0" version "8.9.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.9.0.tgz#b733af07fb340b32e962c6c63b1062aec2dc0fe6" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.9.0.tgz#b733af07fb340b32e962c6c63b1062aec2dc0fe6"
@ -3359,6 +3359,20 @@
semver "^7.6.0" semver "^7.6.0"
ts-api-utils "^1.3.0" ts-api-utils "^1.3.0"
"@typescript-eslint/typescript-estree@8.16.0":
version "8.16.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.16.0.tgz#9d741e56e5b13469b5190e763432ce5551a9300c"
integrity sha512-E2+9IzzXMc1iaBy9zmo+UYvluE3TW7bCGWSF41hVWUE01o8nzr1rvOQYSxelxr6StUvRcTMe633eY8mXASMaNw==
dependencies:
"@typescript-eslint/types" "8.16.0"
"@typescript-eslint/visitor-keys" "8.16.0"
debug "^4.3.4"
fast-glob "^3.3.2"
is-glob "^4.0.3"
minimatch "^9.0.4"
semver "^7.6.0"
ts-api-utils "^1.3.0"
"@typescript-eslint/typescript-estree@8.9.0": "@typescript-eslint/typescript-estree@8.9.0":
version "8.9.0" version "8.9.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.9.0.tgz#1714f167e9063062dc0df49c1d25afcbc7a96199" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.9.0.tgz#1714f167e9063062dc0df49c1d25afcbc7a96199"
@ -3373,7 +3387,7 @@
semver "^7.6.0" semver "^7.6.0"
ts-api-utils "^1.3.0" ts-api-utils "^1.3.0"
"@typescript-eslint/utils@8.14.0", "@typescript-eslint/utils@^8.12.2": "@typescript-eslint/utils@8.14.0":
version "8.14.0" version "8.14.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.14.0.tgz#ac2506875e03aba24e602364e43b2dfa45529dbd" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.14.0.tgz#ac2506875e03aba24e602364e43b2dfa45529dbd"
integrity sha512-OGqj6uB8THhrHj0Fk27DcHPojW7zKwKkPmHXHvQ58pLYp4hy8CSUdTKykKeh+5vFqTTVmjz0zCOOPKRovdsgHA== integrity sha512-OGqj6uB8THhrHj0Fk27DcHPojW7zKwKkPmHXHvQ58pLYp4hy8CSUdTKykKeh+5vFqTTVmjz0zCOOPKRovdsgHA==
@ -3393,6 +3407,16 @@
"@typescript-eslint/types" "8.9.0" "@typescript-eslint/types" "8.9.0"
"@typescript-eslint/typescript-estree" "8.9.0" "@typescript-eslint/typescript-estree" "8.9.0"
"@typescript-eslint/utils@^8.13.0":
version "8.16.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.16.0.tgz#c71264c437157feaa97842809836254a6fc833c3"
integrity sha512-C1zRy/mOL8Pj157GiX4kaw7iyRLKfJXBR3L82hk5kS/GyHcOFmy4YUq/zfZti72I9wnuQtA/+xzft4wCC8PJdA==
dependencies:
"@eslint-community/eslint-utils" "^4.4.0"
"@typescript-eslint/scope-manager" "8.16.0"
"@typescript-eslint/types" "8.16.0"
"@typescript-eslint/typescript-estree" "8.16.0"
"@typescript-eslint/visitor-keys@8.14.0": "@typescript-eslint/visitor-keys@8.14.0":
version "8.14.0" version "8.14.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.14.0.tgz#2418d5a54669af9658986ade4e6cfb7767d815ad" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.14.0.tgz#2418d5a54669af9658986ade4e6cfb7767d815ad"
@ -3401,6 +3425,14 @@
"@typescript-eslint/types" "8.14.0" "@typescript-eslint/types" "8.14.0"
eslint-visitor-keys "^3.4.3" eslint-visitor-keys "^3.4.3"
"@typescript-eslint/visitor-keys@8.16.0":
version "8.16.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.16.0.tgz#d5086afc060b01ff7a4ecab8d49d13d5a7b07705"
integrity sha512-pq19gbaMOmFE3CbL0ZB8J8BFCo2ckfHBfaIsaOZgBIF4EoISJIdLX5xRhd0FGB0LlHReNRuzoJoMGpTjq8F2CQ==
dependencies:
"@typescript-eslint/types" "8.16.0"
eslint-visitor-keys "^4.2.0"
"@typescript-eslint/visitor-keys@8.9.0": "@typescript-eslint/visitor-keys@8.9.0":
version "8.9.0" version "8.9.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.9.0.tgz#5f11f4d9db913f37da42776893ffe0dd1ae78f78" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.9.0.tgz#5f11f4d9db913f37da42776893ffe0dd1ae78f78"
@ -3415,9 +3447,9 @@
integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==
"@vector-im/compound-design-tokens@^2.0.1": "@vector-im/compound-design-tokens@^2.0.1":
version "2.0.1" version "2.1.1"
resolved "https://registry.yarnpkg.com/@vector-im/compound-design-tokens/-/compound-design-tokens-2.0.1.tgz#add14494caab16cdbe98f2bdabe726908739def4" resolved "https://registry.yarnpkg.com/@vector-im/compound-design-tokens/-/compound-design-tokens-2.1.1.tgz#d6175a99fe4b97688464126f255386990f3048d6"
integrity sha512-4nkPcrPII+sejispn+UkWZYFN7LecN39e4WGBupdceiMq0NJrfXrnVtJ9/6BDLgSqHInb6R/IWQkIbPbzfqRMg== integrity sha512-QnUi2K14D9KTXxcLQKUU3V75cforZLMwhaaJDNftT8F5mG86950hAM+qhgDNEpEU+pkTffQj0/g/5859YmqWzQ==
"@vector-im/compound-web@^7.4.0": "@vector-im/compound-web@^7.4.0":
version "7.4.0" version "7.4.0"
@ -8064,10 +8096,10 @@ lines-and-columns@^1.1.6:
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
linkify-element@4.1.3: linkify-element@4.1.4:
version "4.1.3" version "4.1.4"
resolved "https://registry.yarnpkg.com/linkify-element/-/linkify-element-4.1.3.tgz#c0de98f2a36683bf3a4bfa28eaa23c4c917bd546" resolved "https://registry.yarnpkg.com/linkify-element/-/linkify-element-4.1.4.tgz#d4050b41fb47c44871e5eed93bc11865e403cc90"
integrity sha512-oUoG7BWaR3Q6kAKdlLi8slsu5rkVRxbiDVVlkpoL7vtidY5THggLzRHIBtmcj+tvMpcAUQomJApDxg0ub0qpdA== integrity sha512-XhSTTF7b7OoX4KIkwVG8MET5DSFEHohT0Gp5pjmsByYp+JCyZq5rSZGsar5dYzeuKUV6TqTSLtsH/NzBBwBxgQ==
linkify-it@^4.0.1: linkify-it@^4.0.1:
version "4.0.1" version "4.0.1"
@ -8076,20 +8108,20 @@ linkify-it@^4.0.1:
dependencies: dependencies:
uc.micro "^1.0.1" uc.micro "^1.0.1"
linkify-react@4.1.3: linkify-react@4.1.4:
version "4.1.3" version "4.1.4"
resolved "https://registry.yarnpkg.com/linkify-react/-/linkify-react-4.1.3.tgz#461d348b4bdab3fcd0452ae1b5bbc22536395b97" resolved "https://registry.yarnpkg.com/linkify-react/-/linkify-react-4.1.4.tgz#6c709f3f96543914874982f4b0b00f9c9270ce93"
integrity sha512-rhI3zM/fxn5BfRPHfi4r9N7zgac4vOIxub1wHIWXLA5ENTMs+BGaIaFO1D1PhmxgwhIKmJz3H7uCP0Dg5JwSlA== integrity sha512-UI9nqHtFzHYRUvVRrYeua5GIXkc0Jy3RpLsJBWEht7HwqjAa2qSaIksGmNSLqclNpO/5AkwaxUJv71I/pQsk9Q==
linkify-string@4.1.3: linkify-string@4.1.4:
version "4.1.3" version "4.1.4"
resolved "https://registry.yarnpkg.com/linkify-string/-/linkify-string-4.1.3.tgz#a47dbbf64c9fbd2f6ae5e26cd41ec2e5748a54d1" resolved "https://registry.yarnpkg.com/linkify-string/-/linkify-string-4.1.4.tgz#89fb814e05c5b22f76d2a2a640bc8b1db4c6694f"
integrity sha512-6dAgx4MiTcvEX87OS5aNpAioO7cSELUXp61k7azOvMYOLSmREx0w4yM1Uf0+O3JLC08YdkUyZhAX+YkasRt/mw== integrity sha512-4z2UEzEi4SxnhWMzzZ8Pa8vIOwX/2U0XWxk/0UIA7lI+Dn0ZRKqTE9ildnO6Jl6K5hqVuLKTeMD8p4bdFW6P8g==
linkifyjs@4.1.3: linkifyjs@4.1.4:
version "4.1.3" version "4.1.4"
resolved "https://registry.yarnpkg.com/linkifyjs/-/linkifyjs-4.1.3.tgz#0edbc346428a7390a23ea2e5939f76112c9ae07f" resolved "https://registry.yarnpkg.com/linkifyjs/-/linkifyjs-4.1.4.tgz#2766605a20078d50c90f35af22275a42dfb7dfc4"
integrity sha512-auMesunaJ8yfkHvK4gfg1K0SaKX/6Wn9g2Aac/NwX+l5VdmFZzo/hdPGxEOETj+ryRa4/fiOPjeeKURSAJx1sg== integrity sha512-0/NxkHNpiJ0k9VrYCkAn9OtU1eu8xEr1tCCpDtSsVRm/SF0xAak2Gzv3QimSfgUgqLBCDlfhMbu73XvaEHUTPQ==
lint-staged@^15.0.2: lint-staged@^15.0.2:
version "15.2.10" version "15.2.10"
@ -9163,17 +9195,17 @@ pkg-dir@^7.0.0:
dependencies: dependencies:
find-up "^6.3.0" find-up "^6.3.0"
playwright-core@1.48.2, playwright-core@^1.45.1: playwright-core@1.49.0, playwright-core@^1.45.1:
version "1.48.2" version "1.49.0"
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.48.2.tgz#cd76ed8af61690edef5c05c64721c26a8db2f3d7" resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.49.0.tgz#8e69ffed3f41855b854982f3632f2922c890afcb"
integrity sha512-sjjw+qrLFlriJo64du+EK0kJgZzoQPsabGF4lBvsid+3CNIZIYLgnMj9V6JY5VhM2Peh20DJWIVpVljLLnlawA== integrity sha512-R+3KKTQF3npy5GTiKH/T+kdhoJfJojjHESR1YEWhYuEKRVfVaxH3+4+GvXE5xyCngCxhxnykk0Vlah9v8fs3jA==
playwright@1.48.2: playwright@1.49.0:
version "1.48.2" version "1.49.0"
resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.48.2.tgz#fca45ae8abdc34835c715718072aaff7e305167e" resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.49.0.tgz#df6b9e05423377a99658202844a294a8afb95d0a"
integrity sha512-NjYvYgp4BPmiwfe31j4gHLa3J7bD2WiBz8Lk2RoSsmX38SVIARZ18VYjxLjAcDsAhA+F4iSEXTSGgjua0rrlgQ== integrity sha512-eKpmys0UFDnfNb3vfsf8Vx2LEOtflgRebl0Im2eQQnYMA4Aqd+Zw8bEOB+7ZKvN76901mRnqdsiOGKxzVTbi7A==
dependencies: dependencies:
playwright-core "1.48.2" playwright-core "1.49.0"
optionalDependencies: optionalDependencies:
fsevents "2.3.2" fsevents "2.3.2"
@ -11114,9 +11146,9 @@ stylelint-config-standard@^36.0.0:
stylelint-config-recommended "^14.0.1" stylelint-config-recommended "^14.0.1"
stylelint-scss@^6.0.0: stylelint-scss@^6.0.0:
version "6.9.0" version "6.10.0"
resolved "https://registry.yarnpkg.com/stylelint-scss/-/stylelint-scss-6.9.0.tgz#a5ab9b2a8ed7e0a9c113558fdbd1b66ad673b259" resolved "https://registry.yarnpkg.com/stylelint-scss/-/stylelint-scss-6.10.0.tgz#ba5b807793e145421e9879dd15ae672af6820a45"
integrity sha512-oWOR+g6ccagfrENecImGmorWWjVyWpt2R8bmkhOW8FkNNPGStZPQMqb8QWMW4Lwu9TyPqmyjHkkAsy3weqsnNw== integrity sha512-y03if6Qw9xBMoVaf7tzp5BbnYhYvudIKzURkhSHzcHG0bW0fAYvQpTUVJOe7DyhHaxeThBil4ObEMvGbV7+M+w==
dependencies: dependencies:
css-tree "^3.0.1" css-tree "^3.0.1"
is-plain-object "^5.0.0" is-plain-object "^5.0.0"
@ -11124,7 +11156,7 @@ stylelint-scss@^6.0.0:
mdn-data "^2.12.2" mdn-data "^2.12.2"
postcss-media-query-parser "^0.2.3" postcss-media-query-parser "^0.2.3"
postcss-resolve-nested-selector "^0.1.6" postcss-resolve-nested-selector "^0.1.6"
postcss-selector-parser "^6.1.2" postcss-selector-parser "^7.0.0"
postcss-value-parser "^4.2.0" postcss-value-parser "^4.2.0"
stylelint-value-no-unknown-custom-properties@^6.0.1: stylelint-value-no-unknown-custom-properties@^6.0.1:
@ -11438,9 +11470,9 @@ truncate-utf8-bytes@^1.0.0:
utf8-byte-length "^1.0.1" utf8-byte-length "^1.0.1"
ts-api-utils@^1.3.0: ts-api-utils@^1.3.0:
version "1.4.0" version "1.4.2"
resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.0.tgz#709c6f2076e511a81557f3d07a0cbd566ae8195c" resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.2.tgz#a6a6dff26117ac7965624fc118525971edc6a82a"
integrity sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ== integrity sha512-ZF5gQIQa/UmzfvxbHZI3JXN0/Jt+vnAfAviNRAMc491laiK6YCLpCW9ft8oaCRFOTxCZtUTE6XB0ZQAe3olntw==
ts-morph@^13.0.1: ts-morph@^13.0.1:
version "13.0.3" version "13.0.3"
@ -11625,10 +11657,10 @@ undici-types@~5.26.4:
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
undici-types@~6.19.2: undici-types@~6.20.0:
version "6.19.8" version "6.20.0"
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433"
integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==
unhomoglyph@^1.0.6: unhomoglyph@^1.0.6:
version "1.0.6" version "1.0.6"