Compare commits

..

7 commits

Author SHA1 Message Date
renovate[bot]
943b817194
Update dependency @babel/preset-react to v7.26.3 (#28714)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-11 20:24:53 +00:00
Florian Duros
2aa72bb40b
Update @vector-im/compound-web to 7.5.0 (#28700)
Co-authored-by: David Baker <dbkr@users.noreply.github.com>
2024-12-11 19:35:59 +00:00
David Baker
a755e399cf
Change to en-US locale for date tests (#28723)
* Change to en-US locale for date tests

As per comment. Fixes the tests.

* Spell locale right

Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>

---------

Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
2024-12-11 19:00:24 +00:00
renovate[bot]
8dff758153
Update definitelyTyped (#28704)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-11 15:02:47 +00:00
David Baker
cf3bdbdc7a
Fix Read Receipt Test (#28719)
* Update test snapshot

as the date formatting appears to have gained a comma, and somehow
got through the merge tests on the dependency bump.

* Actually this was the problem
2024-12-11 14:25:56 +00:00
renovate[bot]
ba98c2085d
Update dependency @formatjs/intl-segmenter to v11.7.5 (#28713)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-11 13:13:09 +00:00
David Baker
b330de5d6e
Factor out crypto setup process into a store (#28675)
* Factor out crypto setup process into a store

To make components pure and avoid react 18 dev mode problems due
to components making requests when mounted.

* fix test

* test for the store

* Add comment
2024-12-11 13:10:27 +00:00
27 changed files with 475 additions and 350 deletions

View file

@ -88,7 +88,7 @@
"@matrix-org/spec": "^1.7.0", "@matrix-org/spec": "^1.7.0",
"@sentry/browser": "^8.0.0", "@sentry/browser": "^8.0.0",
"@vector-im/compound-design-tokens": "^2.0.1", "@vector-im/compound-design-tokens": "^2.0.1",
"@vector-im/compound-web": "^7.4.0", "@vector-im/compound-web": "^7.5.0",
"@vector-im/matrix-wysiwyg": "2.37.13", "@vector-im/matrix-wysiwyg": "2.37.13",
"@zxcvbn-ts/core": "^3.0.4", "@zxcvbn-ts/core": "^3.0.4",
"@zxcvbn-ts/language-common": "^3.0.4", "@zxcvbn-ts/language-common": "^3.0.4",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

Before After
Before After

View file

@ -44,6 +44,7 @@ import { IConfigOptions } from "../IConfigOptions";
import { MatrixDispatcher } from "../dispatcher/dispatcher"; import { MatrixDispatcher } from "../dispatcher/dispatcher";
import { DeepReadonly } from "./common"; import { DeepReadonly } from "./common";
import MatrixChat from "../components/structures/MatrixChat"; import MatrixChat from "../components/structures/MatrixChat";
import { InitialCryptoSetupStore } from "../stores/InitialCryptoSetupStore";
/* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/naming-convention */
@ -117,6 +118,7 @@ declare global {
mxPerformanceEntryNames: any; mxPerformanceEntryNames: any;
mxUIStore: UIStore; mxUIStore: UIStore;
mxSetupEncryptionStore?: SetupEncryptionStore; mxSetupEncryptionStore?: SetupEncryptionStore;
mxInitialCryptoStore?: InitialCryptoSetupStore;
mxRoomScrollStateStore?: RoomScrollStateStore; mxRoomScrollStateStore?: RoomScrollStateStore;
mxActiveWidgetStore?: ActiveWidgetStore; mxActiveWidgetStore?: ActiveWidgetStore;
mxOnRecaptchaLoaded?: () => void; mxOnRecaptchaLoaded?: () => void;

View file

@ -132,6 +132,7 @@ import { SessionLockStolenView } from "./auth/SessionLockStolenView";
import { ConfirmSessionLockTheftView } from "./auth/ConfirmSessionLockTheftView"; import { ConfirmSessionLockTheftView } from "./auth/ConfirmSessionLockTheftView";
import { LoginSplashView } from "./auth/LoginSplashView"; import { LoginSplashView } from "./auth/LoginSplashView";
import { cleanUpDraftsIfRequired } from "../../DraftCleaner"; import { cleanUpDraftsIfRequired } from "../../DraftCleaner";
import { InitialCryptoSetupStore } from "../../stores/InitialCryptoSetupStore";
// legacy export // legacy export
export { default as Views } from "../../Views"; export { default as Views } from "../../Views";
@ -428,6 +429,12 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
!(await shouldSkipSetupEncryption(cli)) !(await shouldSkipSetupEncryption(cli))
) { ) {
// if cross-signing is not yet set up, do so now if possible. // if cross-signing is not yet set up, do so now if possible.
InitialCryptoSetupStore.sharedInstance().startInitialCryptoSetup(
cli,
Boolean(this.tokenLogin),
this.stores,
this.onCompleteSecurityE2eSetupFinished,
);
this.setStateForNewView({ view: Views.E2E_SETUP }); this.setStateForNewView({ view: Views.E2E_SETUP });
} else { } else {
this.onLoggedIn(); this.onLoggedIn();
@ -2073,14 +2080,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
} else if (this.state.view === Views.COMPLETE_SECURITY) { } else if (this.state.view === Views.COMPLETE_SECURITY) {
view = <CompleteSecurity onFinished={this.onCompleteSecurityE2eSetupFinished} />; view = <CompleteSecurity onFinished={this.onCompleteSecurityE2eSetupFinished} />;
} else if (this.state.view === Views.E2E_SETUP) { } else if (this.state.view === Views.E2E_SETUP) {
view = ( view = <E2eSetup onFinished={this.onCompleteSecurityE2eSetupFinished} />;
<E2eSetup
matrixClient={MatrixClientPeg.safeGet()}
onFinished={this.onCompleteSecurityE2eSetupFinished}
accountPassword={this.stores.accountPasswordStore.getPassword()}
tokenLogin={!!this.tokenLogin}
/>
);
} else if (this.state.view === Views.LOGGED_IN) { } else if (this.state.view === Views.LOGGED_IN) {
// `ready` and `view==LOGGED_IN` may be set before `page_type` (because the // `ready` and `view==LOGGED_IN` may be set before `page_type` (because the
// latter is set via the dispatcher). If we don't yet have a `page_type`, // latter is set via the dispatcher). If we don't yet have a `page_type`,

View file

@ -7,17 +7,13 @@ Please see LICENSE files in the repository root for full details.
*/ */
import React from "react"; import React from "react";
import { MatrixClient } from "matrix-js-sdk/src/matrix";
import AuthPage from "../../views/auth/AuthPage"; import AuthPage from "../../views/auth/AuthPage";
import CompleteSecurityBody from "../../views/auth/CompleteSecurityBody"; import CompleteSecurityBody from "../../views/auth/CompleteSecurityBody";
import { InitialCryptoSetupDialog } from "../../views/dialogs/security/InitialCryptoSetupDialog"; import { InitialCryptoSetupDialog } from "../../views/dialogs/security/InitialCryptoSetupDialog";
interface IProps { interface IProps {
matrixClient: MatrixClient;
onFinished: () => void; onFinished: () => void;
accountPassword?: string;
tokenLogin: boolean;
} }
export default class E2eSetup extends React.Component<IProps> { export default class E2eSetup extends React.Component<IProps> {
@ -25,12 +21,7 @@ export default class E2eSetup extends React.Component<IProps> {
return ( return (
<AuthPage> <AuthPage>
<CompleteSecurityBody> <CompleteSecurityBody>
<InitialCryptoSetupDialog <InitialCryptoSetupDialog onFinished={this.props.onFinished} />
matrixClient={this.props.matrixClient}
onFinished={this.props.onFinished}
accountPassword={this.props.accountPassword}
tokenLogin={this.props.tokenLogin}
/>
</CompleteSecurityBody> </CompleteSecurityBody>
</AuthPage> </AuthPage>
); );

View file

@ -7,20 +7,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, { useCallback, useEffect, useState } from "react"; import React, { useCallback } from "react";
import { logger } from "matrix-js-sdk/src/logger";
import { MatrixClient } from "matrix-js-sdk/src/matrix";
import { _t } from "../../../../languageHandler"; import { _t } from "../../../../languageHandler";
import DialogButtons from "../../elements/DialogButtons"; import DialogButtons from "../../elements/DialogButtons";
import BaseDialog from "../BaseDialog"; import BaseDialog from "../BaseDialog";
import Spinner from "../../elements/Spinner"; import Spinner from "../../elements/Spinner";
import { createCrossSigning } from "../../../../CreateCrossSigning"; import { InitialCryptoSetupStore, useInitialCryptoSetupStatus } from "../../../../stores/InitialCryptoSetupStore";
interface Props { interface Props {
matrixClient: MatrixClient;
accountPassword?: string;
tokenLogin: boolean;
onFinished: (success?: boolean) => void; onFinished: (success?: boolean) => void;
} }
@ -29,54 +24,27 @@ interface Props {
* In most cases, only a spinner is shown, but for more * In most cases, only a spinner is shown, but for more
* complex auth like SSO, the user may need to complete some steps to proceed. * complex auth like SSO, the user may need to complete some steps to proceed.
*/ */
export const InitialCryptoSetupDialog: React.FC<Props> = ({ export const InitialCryptoSetupDialog: React.FC<Props> = ({ onFinished }) => {
matrixClient, const onRetryClick = useCallback(() => {
accountPassword, InitialCryptoSetupStore.sharedInstance().retry();
tokenLogin, }, []);
onFinished,
}) => {
const [error, setError] = useState(false);
const doSetup = useCallback(async () => { const onCancelClick = useCallback(() => {
const cryptoApi = matrixClient.getCrypto();
if (!cryptoApi) return;
setError(false);
try {
await createCrossSigning(matrixClient, tokenLogin, accountPassword);
onFinished(true);
} catch (e) {
if (tokenLogin) {
// ignore any failures, we are relying on grace period here
onFinished(false);
return;
}
setError(true);
logger.error("Error bootstrapping cross-signing", e);
}
}, [matrixClient, tokenLogin, accountPassword, onFinished]);
const onCancel = useCallback(() => {
onFinished(false); onFinished(false);
}, [onFinished]); }, [onFinished]);
useEffect(() => { const status = useInitialCryptoSetupStatus(InitialCryptoSetupStore.sharedInstance());
doSetup();
}, [doSetup]);
let content; let content;
if (error) { if (status === "error") {
content = ( content = (
<div> <div>
<p>{_t("encryption|unable_to_setup_keys_error")}</p> <p>{_t("encryption|unable_to_setup_keys_error")}</p>
<div className="mx_Dialog_buttons"> <div className="mx_Dialog_buttons">
<DialogButtons <DialogButtons
primaryButton={_t("action|retry")} primaryButton={_t("action|retry")}
onPrimaryButtonClick={doSetup} onPrimaryButtonClick={onRetryClick}
onCancel={onCancel} onCancel={onCancelClick}
/> />
</div> </div>
</div> </div>

View file

@ -0,0 +1,140 @@
/*
Copyright 2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import EventEmitter from "events";
import { MatrixClient } from "matrix-js-sdk/src/matrix";
import { logger } from "matrix-js-sdk/src/logger";
import { useEffect, useState } from "react";
import { createCrossSigning } from "../CreateCrossSigning";
import { SdkContextClass } from "../contexts/SDKContext";
type Status = "in_progress" | "complete" | "error" | undefined;
export const useInitialCryptoSetupStatus = (store: InitialCryptoSetupStore): Status => {
const [status, setStatus] = useState<Status>(store.getStatus());
useEffect(() => {
const update = (): void => {
setStatus(store.getStatus());
};
store.on("update", update);
return () => {
store.off("update", update);
};
}, [store]);
return status;
};
/**
* Logic for setting up crypto state that's done immediately after
* a user registers. Should be transparent to the user, not requiring
* interaction in most cases.
* As distinct from SetupEncryptionStore which is for setting up
* 4S or verifying the device, will always require interaction
* from the user in some form.
*/
export class InitialCryptoSetupStore extends EventEmitter {
private status: Status = undefined;
private client?: MatrixClient;
private isTokenLogin?: boolean;
private stores?: SdkContextClass;
private onFinished?: (success: boolean) => void;
public static sharedInstance(): InitialCryptoSetupStore {
if (!window.mxInitialCryptoStore) window.mxInitialCryptoStore = new InitialCryptoSetupStore();
return window.mxInitialCryptoStore;
}
public getStatus(): Status {
return this.status;
}
/**
* Start the initial crypto setup process.
*
* @param {MatrixClient} client The client to use for the setup
* @param {boolean} isTokenLogin True if the user logged in via a token login, otherwise false
* @param {SdkContextClass} stores The stores to use for the setup
*/
public startInitialCryptoSetup(
client: MatrixClient,
isTokenLogin: boolean,
stores: SdkContextClass,
onFinished: (success: boolean) => void,
): void {
this.client = client;
this.isTokenLogin = isTokenLogin;
this.stores = stores;
this.onFinished = onFinished;
// We just start this process: it's progress is tracked by the events rather
// than returning a promise, so we don't bother.
this.doSetup().catch(() => logger.error("Initial crypto setup failed"));
}
/**
* Retry the initial crypto setup process.
*
* If no crypto setup is currently in process, this will return false.
*
* @returns {boolean} True if a retry was initiated, otherwise false
*/
public retry(): boolean {
if (this.client === undefined || this.isTokenLogin === undefined || this.stores == undefined) return false;
this.doSetup().catch(() => logger.error("Initial crypto setup failed"));
return true;
}
private reset(): void {
this.client = undefined;
this.isTokenLogin = undefined;
this.stores = undefined;
}
private async doSetup(): Promise<void> {
if (this.client === undefined || this.isTokenLogin === undefined || this.stores == undefined) {
throw new Error("No setup is in progress");
}
const cryptoApi = this.client.getCrypto();
if (!cryptoApi) throw new Error("No crypto module found!");
this.status = "in_progress";
this.emit("update");
try {
await createCrossSigning(this.client, this.isTokenLogin, this.stores.accountPasswordStore.getPassword());
this.reset();
this.status = "complete";
this.emit("update");
this.onFinished?.(true);
} catch (e) {
if (this.isTokenLogin) {
// ignore any failures, we are relying on grace period here
this.reset();
this.status = "complete";
this.emit("update");
this.onFinished?.(true);
return;
}
logger.error("Error bootstrapping cross-signing", e);
this.status = "error";
this.emit("update");
}
}
}

View file

@ -33,6 +33,11 @@ export enum Phase {
ConfirmReset = 6, ConfirmReset = 6,
} }
/**
* Logic for setting up 4S and/or verifying the user's device: a process requiring
* ongoing interaction with the user, as distinct from InitialCryptoSetupStore which
* a (usually) non-interactive process that happens immediately after registration.
*/
export class SetupEncryptionStore extends EventEmitter { export class SetupEncryptionStore extends EventEmitter {
private started?: boolean; private started?: boolean;
public phase?: Phase; public phase?: Phase;

View file

@ -7,31 +7,22 @@ Please see LICENSE files in the repository root for full details.
*/ */
import React from "react"; import React from "react";
import { render, screen, waitFor } from "jest-matrix-react"; import { render, screen } from "jest-matrix-react";
import { mocked } from "jest-mock"; import userEvent from "@testing-library/user-event";
import { MatrixClient } from "matrix-js-sdk/src/matrix";
import { createCrossSigning } from "../../../../../src/CreateCrossSigning";
import { InitialCryptoSetupDialog } from "../../../../../src/components/views/dialogs/security/InitialCryptoSetupDialog"; import { InitialCryptoSetupDialog } from "../../../../../src/components/views/dialogs/security/InitialCryptoSetupDialog";
import { createTestClient } from "../../../../test-utils"; import { InitialCryptoSetupStore } from "../../../../../src/stores/InitialCryptoSetupStore";
jest.mock("../../../../../src/CreateCrossSigning", () => ({
createCrossSigning: jest.fn(),
}));
describe("InitialCryptoSetupDialog", () => { describe("InitialCryptoSetupDialog", () => {
let client: MatrixClient; const storeMock = {
let createCrossSigningResolve: () => void; getStatus: jest.fn(),
let createCrossSigningReject: (e: Error) => void; retry: jest.fn(),
on: jest.fn(),
off: jest.fn(),
};
beforeEach(() => { beforeEach(() => {
client = createTestClient(); jest.spyOn(InitialCryptoSetupStore, "sharedInstance").mockReturnValue(storeMock as any);
mocked(createCrossSigning).mockImplementation(() => {
return new Promise((resolve, reject) => {
createCrossSigningResolve = resolve;
createCrossSigningReject = reject;
});
});
}); });
afterEach(() => { afterEach(() => {
@ -39,93 +30,32 @@ describe("InitialCryptoSetupDialog", () => {
jest.restoreAllMocks(); jest.restoreAllMocks();
}); });
it("should call createCrossSigning and show a spinner while it runs", async () => { it("should show a spinner while the setup is in progress", async () => {
const onFinished = jest.fn(); const onFinished = jest.fn();
render( storeMock.getStatus.mockReturnValue("in_progress");
<InitialCryptoSetupDialog
matrixClient={client} render(<InitialCryptoSetupDialog onFinished={onFinished} />);
accountPassword="hunter2"
tokenLogin={false}
onFinished={onFinished}
/>,
);
expect(createCrossSigning).toHaveBeenCalledWith(client, false, "hunter2");
expect(screen.getByTestId("spinner")).toBeInTheDocument(); expect(screen.getByTestId("spinner")).toBeInTheDocument();
createCrossSigningResolve!();
await waitFor(() => expect(onFinished).toHaveBeenCalledWith(true));
}); });
it("should display an error if createCrossSigning fails", async () => { it("should display an error if setup has failed", async () => {
render( storeMock.getStatus.mockReturnValue("error");
<InitialCryptoSetupDialog
matrixClient={client}
accountPassword="hunter2"
tokenLogin={false}
onFinished={jest.fn()}
/>,
);
createCrossSigningReject!(new Error("generic error message")); render(<InitialCryptoSetupDialog onFinished={jest.fn()} />);
await expect(await screen.findByRole("button", { name: "Retry" })).toBeInTheDocument(); await expect(await screen.findByRole("button", { name: "Retry" })).toBeInTheDocument();
}); });
it("ignores failures when tokenLogin is true", async () => { it("calls retry when retry button pressed", async () => {
const onFinished = jest.fn(); const onFinished = jest.fn();
storeMock.getStatus.mockReturnValue("error");
render( render(<InitialCryptoSetupDialog onFinished={onFinished} />);
<InitialCryptoSetupDialog
matrixClient={client}
accountPassword="hunter2"
tokenLogin={true}
onFinished={onFinished}
/>,
);
createCrossSigningReject!(new Error("generic error message")); await userEvent.click(await screen.findByRole("button", { name: "Retry" }));
await waitFor(() => expect(onFinished).toHaveBeenCalledWith(false)); expect(storeMock.retry).toHaveBeenCalled();
});
it("cancels the dialog when the cancel button is clicked", async () => {
const onFinished = jest.fn();
render(
<InitialCryptoSetupDialog
matrixClient={client}
accountPassword="hunter2"
tokenLogin={false}
onFinished={onFinished}
/>,
);
createCrossSigningReject!(new Error("generic error message"));
const cancelButton = await screen.findByRole("button", { name: "Cancel" });
cancelButton.click();
expect(onFinished).toHaveBeenCalledWith(false);
});
it("should retry when the retry button is clicked", async () => {
render(
<InitialCryptoSetupDialog
matrixClient={client}
accountPassword="hunter2"
tokenLogin={false}
onFinished={jest.fn()}
/>,
);
createCrossSigningReject!(new Error("generic error message"));
const retryButton = await screen.findByRole("button", { name: "Retry" });
retryButton.click();
expect(createCrossSigning).toHaveBeenCalledTimes(2);
}); });
}); });

View file

@ -135,8 +135,9 @@ exports[`<RoomSummaryCard /> has button to edit topic 1`] = `
style="--mx-box-flex: 1;" style="--mx-box-flex: 1;"
> >
<a <a
class="_link_1mzip_17" class="_link_ue21z_17"
data-kind="primary" data-kind="primary"
data-size="medium"
rel="noreferrer noopener" rel="noreferrer noopener"
> >
<p <p
@ -752,8 +753,9 @@ exports[`<RoomSummaryCard /> renders the room summary 1`] = `
style="--mx-box-flex: 1;" style="--mx-box-flex: 1;"
> >
<a <a
class="_link_1mzip_17" class="_link_ue21z_17"
data-kind="primary" data-kind="primary"
data-size="medium"
rel="noreferrer noopener" rel="noreferrer noopener"
> >
<p <p
@ -1406,8 +1408,9 @@ exports[`<RoomSummaryCard /> renders the room topic in the summary 1`] = `
style="--mx-box-flex: 1;" style="--mx-box-flex: 1;"
> >
<a <a
class="_link_1mzip_17" class="_link_ue21z_17"
data-kind="primary" data-kind="primary"
data-size="medium"
rel="noreferrer noopener" rel="noreferrer noopener"
> >
<p <p

View file

@ -10,6 +10,7 @@ import React, { ComponentProps } from "react";
import { render, screen, waitFor } from "jest-matrix-react"; import { render, screen, waitFor } from "jest-matrix-react";
import { RoomMember } from "matrix-js-sdk/src/matrix"; import { RoomMember } from "matrix-js-sdk/src/matrix";
import userEvent from "@testing-library/user-event"; import userEvent from "@testing-library/user-event";
import { mocked } from "jest-mock";
import { import {
determineAvatarPosition, determineAvatarPosition,
@ -20,6 +21,9 @@ import * as languageHandler from "../../../../../src/languageHandler";
import { stubClient } from "../../../../test-utils"; import { stubClient } from "../../../../test-utils";
import dispatcher from "../../../../../src/dispatcher/dispatcher"; import dispatcher from "../../../../../src/dispatcher/dispatcher";
import { Action } from "../../../../../src/dispatcher/actions"; import { Action } from "../../../../../src/dispatcher/actions";
import { formatDate } from "../../../../../src/DateUtils";
jest.mock("../../../../../src/DateUtils");
describe("ReadReceiptGroup", () => { describe("ReadReceiptGroup", () => {
describe("TooltipText", () => { describe("TooltipText", () => {
@ -87,6 +91,10 @@ describe("ReadReceiptGroup", () => {
describe("<ReadReceiptPerson />", () => { describe("<ReadReceiptPerson />", () => {
stubClient(); stubClient();
// We pick a fixed time but this can still vary depending on the locale
// the tests are run in. We are not testing date formatting here, so stub it out.
mocked(formatDate).mockReturnValue("==MOCK FORMATTED DATE==");
const ROOM_ID = "roomId"; const ROOM_ID = "roomId";
const USER_ID = "@alice:example.org"; const USER_ID = "@alice:example.org";

View file

@ -84,7 +84,7 @@ exports[`ReadReceiptGroup <ReadReceiptPerson /> should render 1`] = `
<p <p
class="mx_ReadReceiptGroup_secondary" class="mx_ReadReceiptGroup_secondary"
> >
Wed, 15 May, 0:00 ==MOCK FORMATTED DATE==
</p> </p>
</div> </div>
</div> </div>

View file

@ -19,14 +19,14 @@ exports[`<LayoutSwitcher /> should render 1`] = `
class="mx_SettingsSubsection_content mx_SettingsSubsection_content_newUi" class="mx_SettingsSubsection_content mx_SettingsSubsection_content_newUi"
> >
<form <form
class="_root_dgy0u_24 mx_LayoutSwitcher_LayoutSelector" class="_root_ssths_24 mx_LayoutSwitcher_LayoutSelector"
> >
<div <div
class="_field_dgy0u_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio" class="_field_ssths_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio"
> >
<label <label
aria-label="Modern" aria-label="Modern"
class="_label_dgy0u_67" class="_label_ssths_67"
for="radix-:r0:" for="radix-:r0:"
> >
<div <div
@ -149,11 +149,11 @@ exports[`<LayoutSwitcher /> should render 1`] = `
</label> </label>
</div> </div>
<div <div
class="_field_dgy0u_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio" class="_field_ssths_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio"
> >
<label <label
aria-label="Message bubbles" aria-label="Message bubbles"
class="_label_dgy0u_67" class="_label_ssths_67"
for="radix-:r9:" for="radix-:r9:"
> >
<div <div
@ -275,11 +275,11 @@ exports[`<LayoutSwitcher /> should render 1`] = `
</label> </label>
</div> </div>
<div <div
class="_field_dgy0u_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio" class="_field_ssths_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio"
> >
<label <label
aria-label="IRC (experimental)" aria-label="IRC (experimental)"
class="_label_dgy0u_67" class="_label_ssths_67"
for="radix-:ri:" for="radix-:ri:"
> >
<div <div
@ -402,13 +402,13 @@ exports[`<LayoutSwitcher /> should render 1`] = `
</div> </div>
</form> </form>
<form <form
class="_root_dgy0u_24" class="_root_ssths_24"
> >
<div <div
class="_inline-field_dgy0u_40" class="_inline-field_ssths_40"
> >
<div <div
class="_inline-field-control_dgy0u_52" class="_inline-field-control_ssths_52"
> >
<div <div
class="_container_qnvru_18" class="_container_qnvru_18"
@ -427,16 +427,16 @@ exports[`<LayoutSwitcher /> should render 1`] = `
</div> </div>
</div> </div>
<div <div
class="_inline-field-body_dgy0u_46" class="_inline-field-body_ssths_46"
> >
<label <label
class="_label_dgy0u_67" class="_label_ssths_67"
for="radix-:rr:" for="radix-:rr:"
> >
Show compact text and messages Show compact text and messages
</label> </label>
<span <span
class="_message_dgy0u_98 _help-message_dgy0u_104" class="_message_ssths_93 _help-message_ssths_99"
id="radix-:rs:" id="radix-:rs:"
> >
Modern layout must be selected to use this feature. Modern layout must be selected to use this feature.

View file

@ -19,13 +19,13 @@ exports[`<ThemeChoicePanel /> custom theme should display custom theme 1`] = `
class="mx_SettingsSubsection_content mx_SettingsSubsection_content_newUi" class="mx_SettingsSubsection_content mx_SettingsSubsection_content_newUi"
> >
<form <form
class="_root_dgy0u_24" class="_root_ssths_24"
> >
<div <div
class="_inline-field_dgy0u_40" class="_inline-field_ssths_40"
> >
<div <div
class="_inline-field-control_dgy0u_52" class="_inline-field-control_ssths_52"
> >
<div <div
class="_container_qnvru_18" class="_container_qnvru_18"
@ -43,10 +43,10 @@ exports[`<ThemeChoicePanel /> custom theme should display custom theme 1`] = `
</div> </div>
</div> </div>
<div <div
class="_inline-field-body_dgy0u_46" class="_inline-field-body_ssths_46"
> >
<label <label
class="_label_dgy0u_67" class="_label_ssths_67"
for="radix-:r28:" for="radix-:r28:"
> >
Match system theme Match system theme
@ -55,13 +55,13 @@ exports[`<ThemeChoicePanel /> custom theme should display custom theme 1`] = `
</div> </div>
</form> </form>
<form <form
class="_root_dgy0u_24 mx_ThemeChoicePanel_ThemeSelectors" class="_root_ssths_24 mx_ThemeChoicePanel_ThemeSelectors"
> >
<div <div
class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_enabled cpd-theme-light" class="_inline-field_ssths_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_enabled cpd-theme-light"
> >
<div <div
class="_inline-field-control_dgy0u_52" class="_inline-field-control_ssths_52"
> >
<div <div
class="_container_1vw5h_18" class="_container_1vw5h_18"
@ -81,10 +81,10 @@ exports[`<ThemeChoicePanel /> custom theme should display custom theme 1`] = `
</div> </div>
</div> </div>
<div <div
class="_inline-field-body_dgy0u_46" class="_inline-field-body_ssths_46"
> >
<label <label
class="_label_dgy0u_67 mx_ThemeChoicePanel_themeSelector_Label" class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label"
for="radix-:r29:" for="radix-:r29:"
> >
Light Light
@ -92,10 +92,10 @@ exports[`<ThemeChoicePanel /> custom theme should display custom theme 1`] = `
</div> </div>
</div> </div>
<div <div
class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark" class="_inline-field_ssths_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark"
> >
<div <div
class="_inline-field-control_dgy0u_52" class="_inline-field-control_ssths_52"
> >
<div <div
class="_container_1vw5h_18" class="_container_1vw5h_18"
@ -114,10 +114,10 @@ exports[`<ThemeChoicePanel /> custom theme should display custom theme 1`] = `
</div> </div>
</div> </div>
<div <div
class="_inline-field-body_dgy0u_46" class="_inline-field-body_ssths_46"
> >
<label <label
class="_label_dgy0u_67 mx_ThemeChoicePanel_themeSelector_Label" class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label"
for="radix-:r2a:" for="radix-:r2a:"
> >
Dark Dark
@ -125,10 +125,10 @@ exports[`<ThemeChoicePanel /> custom theme should display custom theme 1`] = `
</div> </div>
</div> </div>
<div <div
class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector cpd-theme-light" class="_inline-field_ssths_40 mx_ThemeChoicePanel_themeSelector cpd-theme-light"
> >
<div <div
class="_inline-field-control_dgy0u_52" class="_inline-field-control_ssths_52"
> >
<div <div
class="_container_1vw5h_18" class="_container_1vw5h_18"
@ -147,10 +147,10 @@ exports[`<ThemeChoicePanel /> custom theme should display custom theme 1`] = `
</div> </div>
</div> </div>
<div <div
class="_inline-field-body_dgy0u_46" class="_inline-field-body_ssths_46"
> >
<label <label
class="_label_dgy0u_67 mx_ThemeChoicePanel_themeSelector_Label" class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label"
for="radix-:r2b:" for="radix-:r2b:"
> >
High contrast High contrast
@ -158,10 +158,10 @@ exports[`<ThemeChoicePanel /> custom theme should display custom theme 1`] = `
</div> </div>
</div> </div>
<div <div
class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark" class="_inline-field_ssths_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark"
> >
<div <div
class="_inline-field-control_dgy0u_52" class="_inline-field-control_ssths_52"
> >
<div <div
class="_container_1vw5h_18" class="_container_1vw5h_18"
@ -180,10 +180,10 @@ exports[`<ThemeChoicePanel /> custom theme should display custom theme 1`] = `
</div> </div>
</div> </div>
<div <div
class="_inline-field-body_dgy0u_46" class="_inline-field-body_ssths_46"
> >
<label <label
class="_label_dgy0u_67 mx_ThemeChoicePanel_themeSelector_Label" class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label"
for="radix-:r2c:" for="radix-:r2c:"
> >
Alice theme Alice theme
@ -195,13 +195,13 @@ exports[`<ThemeChoicePanel /> custom theme should display custom theme 1`] = `
class="mx_ThemeChoicePanel_CustomTheme" class="mx_ThemeChoicePanel_CustomTheme"
> >
<form <form
class="_root_dgy0u_24 mx_ThemeChoicePanel_CustomTheme_EditInPlace" class="_root_ssths_24 mx_ThemeChoicePanel_CustomTheme_EditInPlace"
> >
<div <div
class="_field_dgy0u_34" class="_field_ssths_34"
> >
<label <label
class="_label_dgy0u_67" class="_label_ssths_67"
for="radix-:r2d:" for="radix-:r2d:"
> >
Add custom theme Add custom theme
@ -219,7 +219,7 @@ exports[`<ThemeChoicePanel /> custom theme should display custom theme 1`] = `
/> />
</div> </div>
<span <span
class="_message_dgy0u_98 _help-message_dgy0u_104" class="_message_ssths_93 _help-message_ssths_99"
id="radix-:r2e:" id="radix-:r2e:"
> >
Enter the URL of a custom theme you want to apply. Enter the URL of a custom theme you want to apply.
@ -296,13 +296,13 @@ exports[`<ThemeChoicePanel /> custom theme should render the custom theme sectio
class="mx_SettingsSubsection_content mx_SettingsSubsection_content_newUi" class="mx_SettingsSubsection_content mx_SettingsSubsection_content_newUi"
> >
<form <form
class="_root_dgy0u_24" class="_root_ssths_24"
> >
<div <div
class="_inline-field_dgy0u_40" class="_inline-field_ssths_40"
> >
<div <div
class="_inline-field-control_dgy0u_52" class="_inline-field-control_ssths_52"
> >
<div <div
class="_container_qnvru_18" class="_container_qnvru_18"
@ -320,10 +320,10 @@ exports[`<ThemeChoicePanel /> custom theme should render the custom theme sectio
</div> </div>
</div> </div>
<div <div
class="_inline-field-body_dgy0u_46" class="_inline-field-body_ssths_46"
> >
<label <label
class="_label_dgy0u_67" class="_label_ssths_67"
for="radix-:r10:" for="radix-:r10:"
> >
Match system theme Match system theme
@ -332,13 +332,13 @@ exports[`<ThemeChoicePanel /> custom theme should render the custom theme sectio
</div> </div>
</form> </form>
<form <form
class="_root_dgy0u_24 mx_ThemeChoicePanel_ThemeSelectors" class="_root_ssths_24 mx_ThemeChoicePanel_ThemeSelectors"
> >
<div <div
class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_enabled cpd-theme-light" class="_inline-field_ssths_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_enabled cpd-theme-light"
> >
<div <div
class="_inline-field-control_dgy0u_52" class="_inline-field-control_ssths_52"
> >
<div <div
class="_container_1vw5h_18" class="_container_1vw5h_18"
@ -358,10 +358,10 @@ exports[`<ThemeChoicePanel /> custom theme should render the custom theme sectio
</div> </div>
</div> </div>
<div <div
class="_inline-field-body_dgy0u_46" class="_inline-field-body_ssths_46"
> >
<label <label
class="_label_dgy0u_67 mx_ThemeChoicePanel_themeSelector_Label" class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label"
for="radix-:r11:" for="radix-:r11:"
> >
Light Light
@ -369,10 +369,10 @@ exports[`<ThemeChoicePanel /> custom theme should render the custom theme sectio
</div> </div>
</div> </div>
<div <div
class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark" class="_inline-field_ssths_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark"
> >
<div <div
class="_inline-field-control_dgy0u_52" class="_inline-field-control_ssths_52"
> >
<div <div
class="_container_1vw5h_18" class="_container_1vw5h_18"
@ -391,10 +391,10 @@ exports[`<ThemeChoicePanel /> custom theme should render the custom theme sectio
</div> </div>
</div> </div>
<div <div
class="_inline-field-body_dgy0u_46" class="_inline-field-body_ssths_46"
> >
<label <label
class="_label_dgy0u_67 mx_ThemeChoicePanel_themeSelector_Label" class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label"
for="radix-:r12:" for="radix-:r12:"
> >
Dark Dark
@ -402,10 +402,10 @@ exports[`<ThemeChoicePanel /> custom theme should render the custom theme sectio
</div> </div>
</div> </div>
<div <div
class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector cpd-theme-light" class="_inline-field_ssths_40 mx_ThemeChoicePanel_themeSelector cpd-theme-light"
> >
<div <div
class="_inline-field-control_dgy0u_52" class="_inline-field-control_ssths_52"
> >
<div <div
class="_container_1vw5h_18" class="_container_1vw5h_18"
@ -424,10 +424,10 @@ exports[`<ThemeChoicePanel /> custom theme should render the custom theme sectio
</div> </div>
</div> </div>
<div <div
class="_inline-field-body_dgy0u_46" class="_inline-field-body_ssths_46"
> >
<label <label
class="_label_dgy0u_67 mx_ThemeChoicePanel_themeSelector_Label" class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label"
for="radix-:r13:" for="radix-:r13:"
> >
High contrast High contrast
@ -435,10 +435,10 @@ exports[`<ThemeChoicePanel /> custom theme should render the custom theme sectio
</div> </div>
</div> </div>
<div <div
class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark" class="_inline-field_ssths_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark"
> >
<div <div
class="_inline-field-control_dgy0u_52" class="_inline-field-control_ssths_52"
> >
<div <div
class="_container_1vw5h_18" class="_container_1vw5h_18"
@ -457,10 +457,10 @@ exports[`<ThemeChoicePanel /> custom theme should render the custom theme sectio
</div> </div>
</div> </div>
<div <div
class="_inline-field-body_dgy0u_46" class="_inline-field-body_ssths_46"
> >
<label <label
class="_label_dgy0u_67 mx_ThemeChoicePanel_themeSelector_Label" class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label"
for="radix-:r14:" for="radix-:r14:"
> >
Alice theme Alice theme
@ -472,13 +472,13 @@ exports[`<ThemeChoicePanel /> custom theme should render the custom theme sectio
class="mx_ThemeChoicePanel_CustomTheme" class="mx_ThemeChoicePanel_CustomTheme"
> >
<form <form
class="_root_dgy0u_24 mx_ThemeChoicePanel_CustomTheme_EditInPlace" class="_root_ssths_24 mx_ThemeChoicePanel_CustomTheme_EditInPlace"
> >
<div <div
class="_field_dgy0u_34" class="_field_ssths_34"
> >
<label <label
class="_label_dgy0u_67" class="_label_ssths_67"
for="radix-:r15:" for="radix-:r15:"
> >
Add custom theme Add custom theme
@ -496,7 +496,7 @@ exports[`<ThemeChoicePanel /> custom theme should render the custom theme sectio
/> />
</div> </div>
<span <span
class="_message_dgy0u_98 _help-message_dgy0u_104" class="_message_ssths_93 _help-message_ssths_99"
id="radix-:r16:" id="radix-:r16:"
> >
Enter the URL of a custom theme you want to apply. Enter the URL of a custom theme you want to apply.
@ -573,13 +573,13 @@ exports[`<ThemeChoicePanel /> renders the theme choice UI 1`] = `
class="mx_SettingsSubsection_content mx_SettingsSubsection_content_newUi" class="mx_SettingsSubsection_content mx_SettingsSubsection_content_newUi"
> >
<form <form
class="_root_dgy0u_24" class="_root_ssths_24"
> >
<div <div
class="_inline-field_dgy0u_40" class="_inline-field_ssths_40"
> >
<div <div
class="_inline-field-control_dgy0u_52" class="_inline-field-control_ssths_52"
> >
<div <div
class="_container_qnvru_18" class="_container_qnvru_18"
@ -597,10 +597,10 @@ exports[`<ThemeChoicePanel /> renders the theme choice UI 1`] = `
</div> </div>
</div> </div>
<div <div
class="_inline-field-body_dgy0u_46" class="_inline-field-body_ssths_46"
> >
<label <label
class="_label_dgy0u_67" class="_label_ssths_67"
for="radix-:r0:" for="radix-:r0:"
> >
Match system theme Match system theme
@ -609,13 +609,13 @@ exports[`<ThemeChoicePanel /> renders the theme choice UI 1`] = `
</div> </div>
</form> </form>
<form <form
class="_root_dgy0u_24 mx_ThemeChoicePanel_ThemeSelectors" class="_root_ssths_24 mx_ThemeChoicePanel_ThemeSelectors"
> >
<div <div
class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_enabled cpd-theme-light" class="_inline-field_ssths_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_enabled cpd-theme-light"
> >
<div <div
class="_inline-field-control_dgy0u_52" class="_inline-field-control_ssths_52"
> >
<div <div
class="_container_1vw5h_18" class="_container_1vw5h_18"
@ -635,10 +635,10 @@ exports[`<ThemeChoicePanel /> renders the theme choice UI 1`] = `
</div> </div>
</div> </div>
<div <div
class="_inline-field-body_dgy0u_46" class="_inline-field-body_ssths_46"
> >
<label <label
class="_label_dgy0u_67 mx_ThemeChoicePanel_themeSelector_Label" class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label"
for="radix-:r1:" for="radix-:r1:"
> >
Light Light
@ -646,10 +646,10 @@ exports[`<ThemeChoicePanel /> renders the theme choice UI 1`] = `
</div> </div>
</div> </div>
<div <div
class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark" class="_inline-field_ssths_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark"
> >
<div <div
class="_inline-field-control_dgy0u_52" class="_inline-field-control_ssths_52"
> >
<div <div
class="_container_1vw5h_18" class="_container_1vw5h_18"
@ -668,10 +668,10 @@ exports[`<ThemeChoicePanel /> renders the theme choice UI 1`] = `
</div> </div>
</div> </div>
<div <div
class="_inline-field-body_dgy0u_46" class="_inline-field-body_ssths_46"
> >
<label <label
class="_label_dgy0u_67 mx_ThemeChoicePanel_themeSelector_Label" class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label"
for="radix-:r2:" for="radix-:r2:"
> >
Dark Dark
@ -679,10 +679,10 @@ exports[`<ThemeChoicePanel /> renders the theme choice UI 1`] = `
</div> </div>
</div> </div>
<div <div
class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector cpd-theme-light" class="_inline-field_ssths_40 mx_ThemeChoicePanel_themeSelector cpd-theme-light"
> >
<div <div
class="_inline-field-control_dgy0u_52" class="_inline-field-control_ssths_52"
> >
<div <div
class="_container_1vw5h_18" class="_container_1vw5h_18"
@ -701,10 +701,10 @@ exports[`<ThemeChoicePanel /> renders the theme choice UI 1`] = `
</div> </div>
</div> </div>
<div <div
class="_inline-field-body_dgy0u_46" class="_inline-field-body_ssths_46"
> >
<label <label
class="_label_dgy0u_67 mx_ThemeChoicePanel_themeSelector_Label" class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label"
for="radix-:r3:" for="radix-:r3:"
> >
High contrast High contrast

View file

@ -32,13 +32,13 @@ exports[`AppearanceUserSettingsTab should render 1`] = `
class="mx_SettingsSubsection_content mx_SettingsSubsection_content_newUi" class="mx_SettingsSubsection_content mx_SettingsSubsection_content_newUi"
> >
<form <form
class="_root_dgy0u_24 mx_ThemeChoicePanel_ThemeSelectors" class="_root_ssths_24 mx_ThemeChoicePanel_ThemeSelectors"
> >
<div <div
class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_disabled cpd-theme-light" class="_inline-field_ssths_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_disabled cpd-theme-light"
> >
<div <div
class="_inline-field-control_dgy0u_52" class="_inline-field-control_ssths_52"
> >
<div <div
class="_container_1vw5h_18" class="_container_1vw5h_18"
@ -58,10 +58,10 @@ exports[`AppearanceUserSettingsTab should render 1`] = `
</div> </div>
</div> </div>
<div <div
class="_inline-field-body_dgy0u_46" class="_inline-field-body_ssths_46"
> >
<label <label
class="_label_dgy0u_67 mx_ThemeChoicePanel_themeSelector_Label" class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label"
for="radix-:r0:" for="radix-:r0:"
> >
Light Light
@ -69,10 +69,10 @@ exports[`AppearanceUserSettingsTab should render 1`] = `
</div> </div>
</div> </div>
<div <div
class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_disabled cpd-theme-dark" class="_inline-field_ssths_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_disabled cpd-theme-dark"
> >
<div <div
class="_inline-field-control_dgy0u_52" class="_inline-field-control_ssths_52"
> >
<div <div
class="_container_1vw5h_18" class="_container_1vw5h_18"
@ -92,10 +92,10 @@ exports[`AppearanceUserSettingsTab should render 1`] = `
</div> </div>
</div> </div>
<div <div
class="_inline-field-body_dgy0u_46" class="_inline-field-body_ssths_46"
> >
<label <label
class="_label_dgy0u_67 mx_ThemeChoicePanel_themeSelector_Label" class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label"
for="radix-:r1:" for="radix-:r1:"
> >
Dark Dark
@ -103,10 +103,10 @@ exports[`AppearanceUserSettingsTab should render 1`] = `
</div> </div>
</div> </div>
<div <div
class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_disabled cpd-theme-light" class="_inline-field_ssths_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_disabled cpd-theme-light"
> >
<div <div
class="_inline-field-control_dgy0u_52" class="_inline-field-control_ssths_52"
> >
<div <div
class="_container_1vw5h_18" class="_container_1vw5h_18"
@ -126,10 +126,10 @@ exports[`AppearanceUserSettingsTab should render 1`] = `
</div> </div>
</div> </div>
<div <div
class="_inline-field-body_dgy0u_46" class="_inline-field-body_ssths_46"
> >
<label <label
class="_label_dgy0u_67 mx_ThemeChoicePanel_themeSelector_Label" class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label"
for="radix-:r2:" for="radix-:r2:"
> >
High contrast High contrast
@ -162,14 +162,14 @@ exports[`AppearanceUserSettingsTab should render 1`] = `
class="mx_SettingsSubsection_content mx_SettingsSubsection_content_newUi" class="mx_SettingsSubsection_content mx_SettingsSubsection_content_newUi"
> >
<form <form
class="_root_dgy0u_24 mx_LayoutSwitcher_LayoutSelector" class="_root_ssths_24 mx_LayoutSwitcher_LayoutSelector"
> >
<div <div
class="_field_dgy0u_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio" class="_field_ssths_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio"
> >
<label <label
aria-label="Modern" aria-label="Modern"
class="_label_dgy0u_67" class="_label_ssths_67"
for="radix-:r3:" for="radix-:r3:"
> >
<div <div
@ -292,11 +292,11 @@ exports[`AppearanceUserSettingsTab should render 1`] = `
</label> </label>
</div> </div>
<div <div
class="_field_dgy0u_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio" class="_field_ssths_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio"
> >
<label <label
aria-label="Message bubbles" aria-label="Message bubbles"
class="_label_dgy0u_67" class="_label_ssths_67"
for="radix-:rc:" for="radix-:rc:"
> >
<div <div
@ -418,11 +418,11 @@ exports[`AppearanceUserSettingsTab should render 1`] = `
</label> </label>
</div> </div>
<div <div
class="_field_dgy0u_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio" class="_field_ssths_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio"
> >
<label <label
aria-label="IRC (experimental)" aria-label="IRC (experimental)"
class="_label_dgy0u_67" class="_label_ssths_67"
for="radix-:rl:" for="radix-:rl:"
> >
<div <div
@ -545,13 +545,13 @@ exports[`AppearanceUserSettingsTab should render 1`] = `
</div> </div>
</form> </form>
<form <form
class="_root_dgy0u_24" class="_root_ssths_24"
> >
<div <div
class="_inline-field_dgy0u_40" class="_inline-field_ssths_40"
> >
<div <div
class="_inline-field-control_dgy0u_52" class="_inline-field-control_ssths_52"
> >
<div <div
class="_container_qnvru_18" class="_container_qnvru_18"
@ -570,16 +570,16 @@ exports[`AppearanceUserSettingsTab should render 1`] = `
</div> </div>
</div> </div>
<div <div
class="_inline-field-body_dgy0u_46" class="_inline-field-body_ssths_46"
> >
<label <label
class="_label_dgy0u_67" class="_label_ssths_67"
for="radix-:ru:" for="radix-:ru:"
> >
Show compact text and messages Show compact text and messages
</label> </label>
<span <span
class="_message_dgy0u_98 _help-message_dgy0u_104" class="_message_ssths_93 _help-message_ssths_99"
id="radix-:rv:" id="radix-:rv:"
> >
Modern layout must be selected to use this feature. Modern layout must be selected to use this feature.

View file

@ -8,6 +8,7 @@ exports[`<SpacePanel /> should show all activated MetaSpaces in the correct orde
> >
<div <div
class="mx_UserMenu" class="mx_UserMenu"
data-floating-ui-inert=""
> >
<div <div
aria-expanded="false" aria-expanded="false"
@ -42,6 +43,7 @@ exports[`<SpacePanel /> should show all activated MetaSpaces in the correct orde
<ul <ul
aria-label="Spaces" aria-label="Spaces"
class="mx_AutoHideScrollbar mx_SpaceTreeLevel" class="mx_AutoHideScrollbar mx_SpaceTreeLevel"
data-floating-ui-inert=""
data-rbd-droppable-context-id="0" data-rbd-droppable-context-id="0"
data-rbd-droppable-id="top-level-spaces" data-rbd-droppable-id="top-level-spaces"
role="tree" role="tree"
@ -236,6 +238,7 @@ exports[`<SpacePanel /> should show all activated MetaSpaces in the correct orde
aria-label="Threads" aria-label="Threads"
aria-labelledby=":r14:" aria-labelledby=":r14:"
class="_icon-button_bh2qc_17 mx_ThreadsActivityCentreButton" class="_icon-button_bh2qc_17 mx_ThreadsActivityCentreButton"
data-floating-ui-inert=""
role="button" role="button"
style="--cpd-icon-button-size: 32px;" style="--cpd-icon-button-size: 32px;"
tabindex="0" tabindex="0"
@ -260,6 +263,7 @@ exports[`<SpacePanel /> should show all activated MetaSpaces in the correct orde
</button> </button>
<span <span
aria-hidden="true" aria-hidden="true"
data-floating-ui-inert=""
style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: fixed; white-space: nowrap; width: 1px; top: 0px; left: 0px;" style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: fixed; white-space: nowrap; width: 1px; top: 0px; left: 0px;"
tabindex="-1" tabindex="-1"
/> />
@ -272,6 +276,7 @@ exports[`<SpacePanel /> should show all activated MetaSpaces in the correct orde
/> />
<span <span
aria-owns=":r19:" aria-owns=":r19:"
data-floating-ui-inert=""
style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: fixed; white-space: nowrap; width: 1px; top: 0px; left: 0px;" style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: fixed; white-space: nowrap; width: 1px; top: 0px; left: 0px;"
/> />
<span <span
@ -286,6 +291,7 @@ exports[`<SpacePanel /> should show all activated MetaSpaces in the correct orde
aria-expanded="false" aria-expanded="false"
aria-label="Quick settings" aria-label="Quick settings"
class="mx_AccessibleButton mx_QuickSettingsButton" class="mx_AccessibleButton mx_QuickSettingsButton"
data-floating-ui-inert=""
role="button" role="button"
tabindex="0" tabindex="0"
/> />

View file

@ -477,9 +477,7 @@ exports[`ThreadsActivityCentre should order the room with the same notification
exports[`ThreadsActivityCentre should render the release announcement 1`] = ` exports[`ThreadsActivityCentre should render the release announcement 1`] = `
<body> <body>
<div <div>
data-floating-ui-inert=""
>
<div <div
class="mx_ThreadsActivityCentre_container" class="mx_ThreadsActivityCentre_container"
> >
@ -491,6 +489,7 @@ exports[`ThreadsActivityCentre should render the release announcement 1`] = `
aria-label="Threads" aria-label="Threads"
aria-labelledby=":rc:" aria-labelledby=":rc:"
class="_icon-button_bh2qc_17 mx_ThreadsActivityCentreButton" class="_icon-button_bh2qc_17 mx_ThreadsActivityCentreButton"
data-floating-ui-inert=""
role="button" role="button"
style="--cpd-icon-button-size: 32px;" style="--cpd-icon-button-size: 32px;"
tabindex="0" tabindex="0"
@ -515,6 +514,7 @@ exports[`ThreadsActivityCentre should render the release announcement 1`] = `
</button> </button>
<span <span
aria-hidden="true" aria-hidden="true"
data-floating-ui-inert=""
style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: fixed; white-space: nowrap; width: 1px; top: 0px; left: 0px;" style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: fixed; white-space: nowrap; width: 1px; top: 0px; left: 0px;"
tabindex="-1" tabindex="-1"
/> />
@ -527,6 +527,7 @@ exports[`ThreadsActivityCentre should render the release announcement 1`] = `
/> />
<span <span
aria-owns=":rh:" aria-owns=":rh:"
data-floating-ui-inert=""
style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: fixed; white-space: nowrap; width: 1px; top: 0px; left: 0px;" style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: fixed; white-space: nowrap; width: 1px; top: 0px; left: 0px;"
/> />
<span <span
@ -585,7 +586,6 @@ exports[`ThreadsActivityCentre should render the release announcement 1`] = `
> >
<span <span
data-floating-ui-focus-guard="" data-floating-ui-focus-guard=""
data-floating-ui-inert=""
data-type="inside" data-type="inside"
role="button" role="button"
style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: fixed; white-space: nowrap; width: 1px; top: 0px; left: 0px;" style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: fixed; white-space: nowrap; width: 1px; top: 0px; left: 0px;"
@ -648,7 +648,6 @@ exports[`ThreadsActivityCentre should render the release announcement 1`] = `
</div> </div>
<span <span
data-floating-ui-focus-guard="" data-floating-ui-focus-guard=""
data-floating-ui-inert=""
data-type="inside" data-type="inside"
role="button" role="button"
style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: fixed; white-space: nowrap; width: 1px; top: 0px; left: 0px;" style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: fixed; white-space: nowrap; width: 1px; top: 0px; left: 0px;"

View file

@ -0,0 +1,85 @@
/*
Copyright 2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import { mocked } from "jest-mock";
import { MatrixClient } from "matrix-js-sdk/src/matrix";
import { waitFor } from "jest-matrix-react";
import { createCrossSigning } from "../../../src/CreateCrossSigning";
import { InitialCryptoSetupStore } from "../../../src/stores/InitialCryptoSetupStore";
import { SdkContextClass } from "../../../src/contexts/SDKContext";
import { createTestClient } from "../../test-utils";
import { AccountPasswordStore } from "../../../src/stores/AccountPasswordStore";
jest.mock("../../../src/CreateCrossSigning", () => ({
createCrossSigning: jest.fn(),
}));
describe("InitialCryptoSetupStore", () => {
let testStore: InitialCryptoSetupStore;
let client: MatrixClient;
let stores: SdkContextClass;
let createCrossSigningResolve: () => void;
let createCrossSigningReject: (e: Error) => void;
beforeEach(() => {
testStore = new InitialCryptoSetupStore();
client = createTestClient();
stores = {
accountPasswordStore: {
getPassword: jest.fn(),
} as unknown as AccountPasswordStore,
} as unknown as SdkContextClass;
mocked(createCrossSigning).mockImplementation(() => {
return new Promise<void>((resolve, reject) => {
createCrossSigningResolve = resolve;
createCrossSigningReject = reject;
});
});
});
it("should call createCrossSigning when startInitialCryptoSetup is called", async () => {
testStore.startInitialCryptoSetup(client, false, stores, jest.fn());
await waitFor(() => expect(createCrossSigning).toHaveBeenCalled());
});
it("emits an update event when createCrossSigning resolves", async () => {
const updateSpy = jest.fn();
testStore.on("update", updateSpy);
testStore.startInitialCryptoSetup(client, false, stores, jest.fn());
createCrossSigningResolve();
await waitFor(() => expect(updateSpy).toHaveBeenCalled());
expect(testStore.getStatus()).toBe("complete");
});
it("emits an update event when createCrossSigning rejects", async () => {
const updateSpy = jest.fn();
testStore.on("update", updateSpy);
testStore.startInitialCryptoSetup(client, false, stores, jest.fn());
createCrossSigningReject(new Error("Test error"));
await waitFor(() => expect(updateSpy).toHaveBeenCalled());
expect(testStore.getStatus()).toBe("error");
});
it("should ignore failures if tokenLogin is true", async () => {
const updateSpy = jest.fn();
testStore.on("update", updateSpy);
testStore.startInitialCryptoSetup(client, true, stores, jest.fn());
createCrossSigningReject(new Error("Test error"));
await waitFor(() => expect(updateSpy).toHaveBeenCalled());
expect(testStore.getStatus()).toBe("complete");
});
});

View file

@ -178,22 +178,26 @@ describe("formatDate", () => {
it("should return time string if date is within same day", () => { it("should return time string if date is within same day", () => {
const date = new Date(REPEATABLE_DATE.getTime() + 2 * HOUR_MS + 12 * MINUTE_MS); const date = new Date(REPEATABLE_DATE.getTime() + 2 * HOUR_MS + 12 * MINUTE_MS);
expect(formatDate(date, false, "en-GB")).toMatchInlineSnapshot(`"19:10"`); // We use en-US for these tests because there was a change in Node 22.12 which removed
// the comma after the weekday for en-GB which makes the test output different things
// on different node versions. I'm not sure what a better fix would be, so let's just use
// a locale that happens to have a more stable formatting right now.
expect(formatDate(date, false, "en-US")).toMatchInlineSnapshot(`"19:10"`);
}); });
it("should return time string with weekday if date is within last 6 days", () => { it("should return time string with weekday if date is within last 6 days", () => {
const date = new Date(REPEATABLE_DATE.getTime() - 6 * DAY_MS + 2 * HOUR_MS + 12 * MINUTE_MS); const date = new Date(REPEATABLE_DATE.getTime() - 6 * DAY_MS + 2 * HOUR_MS + 12 * MINUTE_MS);
expect(formatDate(date, false, "en-GB")).toMatchInlineSnapshot(`"Fri 19:10"`); expect(formatDate(date, false, "en-US")).toMatchInlineSnapshot(`"Fri 19:10"`);
}); });
it("should return time & date string without year if it is within the same year", () => { it("should return time & date string without year if it is within the same year", () => {
const date = new Date(REPEATABLE_DATE.getTime() - 66 * DAY_MS + 2 * HOUR_MS + 12 * MINUTE_MS); const date = new Date(REPEATABLE_DATE.getTime() - 66 * DAY_MS + 2 * HOUR_MS + 12 * MINUTE_MS);
expect(formatDate(date, false, "en-GB")).toMatchInlineSnapshot(`"Mon, 12 Sept, 19:10"`); expect(formatDate(date, false, "en-US")).toMatchInlineSnapshot(`"Mon, Sep 12, 19:10"`);
}); });
it("should return full time & date string otherwise", () => { it("should return full time & date string otherwise", () => {
const date = new Date(REPEATABLE_DATE.getTime() - 666 * DAY_MS + 2 * HOUR_MS + 12 * MINUTE_MS); const date = new Date(REPEATABLE_DATE.getTime() - 666 * DAY_MS + 2 * HOUR_MS + 12 * MINUTE_MS);
expect(formatDate(date, false, "en-GB")).toMatchInlineSnapshot(`"Wed, 20 Jan 2021, 19:10"`); expect(formatDate(date, false, "en-US")).toMatchInlineSnapshot(`"Wed, Jan 20, 2021, 19:10"`);
}); });
}); });

View file

@ -120,10 +120,6 @@ module.exports = (env, argv) => {
return { return {
...development, ...development,
experiments: {
asyncWebAssembly: true,
},
bail: true, bail: true,
entry: { entry: {
@ -226,16 +222,6 @@ module.exports = (env, argv) => {
// Polyfill needed by sentry // Polyfill needed by sentry
"process/browser": require.resolve("process/browser"), "process/browser": require.resolve("process/browser"),
}, },
// Enable the custom "wasm-esm" export condition [1] to indicate to
// matrix-sdk-crypto-wasm that we support the ES Module Integration
// Proposal for WebAssembly [2]. The "..." magic value means "the
// default conditions" [3].
//
// [1]: https://nodejs.org/api/packages.html#conditional-exports
// [2]: https://github.com/webassembly/esm-integration
// [3]: https://github.com/webpack/webpack/issues/17692#issuecomment-1866272674.
conditionNames: ["matrix-org:wasm-esm", "..."],
}, },
// Some of our deps have broken source maps, so we have to ignore warnings or exclude them one-by-one // Some of our deps have broken source maps, so we have to ignore warnings or exclude them one-by-one
@ -695,21 +681,13 @@ module.exports = (env, argv) => {
output: { output: {
path: path.join(__dirname, "webapp"), path: path.join(__dirname, "webapp"),
// There are a lot of assets that need to be kept in sync with each other // The generated JS (and CSS, from the extraction plugin) are put in a
// (once a user loads one version of the app, they need to keep being served // unique subdirectory for the build. There will only be one such
// assets for that version). // 'bundle' directory in the generated tarball; however, hosting
// // servers can collect 'bundles' from multiple versions into one
// To deal with this, we try to put as many as possible of the referenced assets // directory and symlink it into place - this allows users who loaded
// into a build-specific subdirectory. This includes generated javascript, as well // an older version of the application to continue to access webpack
// as CSS extracted by the MiniCssExtractPlugin (see config above) and WASM modules // chunks even after the app is redeployed.
// referenced via `import` statements.
//
// Hosting servers can then collect 'bundles' from multiple versions
// into one directory, and continue to serve them even after a new version is deployed.
// This allows users who loaded an older version of the application to continue to
// access assets even after the app is redeployed.
//
// See `scripts/deploy.py` for a script which manages the deployment in this way.
filename: "bundles/[fullhash]/[name].js", filename: "bundles/[fullhash]/[name].js",
chunkFilename: "bundles/[fullhash]/[name].js", chunkFilename: "bundles/[fullhash]/[name].js",
webassemblyModuleFilename: "bundles/[fullhash]/[modulehash].wasm", webassemblyModuleFilename: "bundles/[fullhash]/[modulehash].wasm",

View file

@ -1065,9 +1065,9 @@
esutils "^2.0.2" esutils "^2.0.2"
"@babel/preset-react@^7.12.10", "@babel/preset-react@^7.18.6": "@babel/preset-react@^7.12.10", "@babel/preset-react@^7.18.6":
version "7.25.9" version "7.26.3"
resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.25.9.tgz#5f473035dc2094bcfdbc7392d0766bd42dce173e" resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.26.3.tgz#7c5e028d623b4683c1f83a0bd4713b9100560caa"
integrity sha512-D3to0uSPiWE7rBrdIICCd0tJSIGpLaaGptna2+w7Pft5xMqLpA1sz99DK5TZ1TjGbdQ/VI1eCSZ06dv3lT4JOw== integrity sha512-Nl03d6T9ky516DGK2YMxrTqvnpUW63TnJMOMonj+Zae0JiPC5BC9xPMSL6L8fiSpA5vP88qfygavVQvnLp+6Cw==
dependencies: dependencies:
"@babel/helper-plugin-utils" "^7.25.9" "@babel/helper-plugin-utils" "^7.25.9"
"@babel/helper-validator-option" "^7.25.9" "@babel/helper-validator-option" "^7.25.9"
@ -1584,10 +1584,10 @@
dependencies: dependencies:
"@floating-ui/dom" "^1.0.0" "@floating-ui/dom" "^1.0.0"
"@floating-ui/react@^0.26.24": "@floating-ui/react@^0.27.0":
version "0.26.25" version "0.27.0"
resolved "https://registry.yarnpkg.com/@floating-ui/react/-/react-0.26.25.tgz#cf4c8a2b89fab1a71712d15e6551df3bfbd2ea1d" resolved "https://registry.yarnpkg.com/@floating-ui/react/-/react-0.27.0.tgz#e0931fd09374ab4b8ce1a1af5cb44d1ccd1bb95a"
integrity sha512-hZOmgN0NTOzOuZxI1oIrDu3Gcl8WViIkvPMpB4xdd4QD6xAMtwgwr3VPoiyH/bLtRcS1cDnhxLSD1NsMJmwh/A== integrity sha512-WLEksq7fJapXSJbmfiyq9pAW0a7ZFMEJToFE4oTDESxGjoa+nZu3YMjmZE2KvoUtQhqOK2yMMfWQFZyeWD0wGQ==
dependencies: dependencies:
"@floating-ui/react-dom" "^2.1.2" "@floating-ui/react-dom" "^2.1.2"
"@floating-ui/utils" "^0.2.8" "@floating-ui/utils" "^0.2.8"
@ -1608,36 +1608,37 @@
resolved "https://registry.yarnpkg.com/@fontsource/inter/-/inter-5.1.0.tgz#ab629b2c662457022d2d6a29854b8dc8ba538c47" resolved "https://registry.yarnpkg.com/@fontsource/inter/-/inter-5.1.0.tgz#ab629b2c662457022d2d6a29854b8dc8ba538c47"
integrity sha512-zKZR3kf1G0noIes1frLfOHP5EXVVm0M7sV/l9f/AaYf+M/DId35FO4LkigWjqWYjTJZGgplhdv4cB+ssvCqr5A== integrity sha512-zKZR3kf1G0noIes1frLfOHP5EXVVm0M7sV/l9f/AaYf+M/DId35FO4LkigWjqWYjTJZGgplhdv4cB+ssvCqr5A==
"@formatjs/ecma402-abstract@2.2.4": "@formatjs/ecma402-abstract@2.3.1":
version "2.2.4" version "2.3.1"
resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-2.2.4.tgz#355e42d375678229d46dc8ad7a7139520dd03e7b" resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.1.tgz#cdeb3ffe1aeea9c4284b85b7e37e8e8615314c39"
integrity sha512-lFyiQDVvSbQOpU+WFd//ILolGj4UgA/qXrKeZxdV14uKiAUiPAtX6XAn7WBCRi7Mx6I7EybM9E5yYn4BIpZWYg== integrity sha512-Ip9uV+/MpLXWRk03U/GzeJMuPeOXpJBSB5V1tjA6kJhvqssye5J5LoYLc7Z5IAHb7nR62sRoguzrFiVCP/hnzw==
dependencies: dependencies:
"@formatjs/fast-memoize" "2.2.3" "@formatjs/fast-memoize" "2.2.5"
"@formatjs/intl-localematcher" "0.5.8" "@formatjs/intl-localematcher" "0.5.9"
decimal.js "10"
tslib "2" tslib "2"
"@formatjs/fast-memoize@2.2.3": "@formatjs/fast-memoize@2.2.5":
version "2.2.3" version "2.2.5"
resolved "https://registry.yarnpkg.com/@formatjs/fast-memoize/-/fast-memoize-2.2.3.tgz#74e64109279d5244f9fc281f3ae90c407cece823" resolved "https://registry.yarnpkg.com/@formatjs/fast-memoize/-/fast-memoize-2.2.5.tgz#54a4a1793d773b72c372d3dcab3595149aee7880"
integrity sha512-3jeJ+HyOfu8osl3GNSL4vVHUuWFXR03Iz9jjgI7RwjG6ysu/Ymdr0JRCPHfF5yGbTE6JCrd63EpvX1/WybYRbA== integrity sha512-6PoewUMrrcqxSoBXAOJDiW1m+AmkrAj0RiXnOMD59GRaswjXhm3MDhgepXPBgonc09oSirAJTsAggzAGQf6A6g==
dependencies: dependencies:
tslib "2" tslib "2"
"@formatjs/intl-localematcher@0.5.8": "@formatjs/intl-localematcher@0.5.9":
version "0.5.8" version "0.5.9"
resolved "https://registry.yarnpkg.com/@formatjs/intl-localematcher/-/intl-localematcher-0.5.8.tgz#b11bbd04bd3551f7cadcb1ef1e231822d0e3c97e" resolved "https://registry.yarnpkg.com/@formatjs/intl-localematcher/-/intl-localematcher-0.5.9.tgz#43c6ee22be85b83340bcb09bdfed53657a2720db"
integrity sha512-I+WDNWWJFZie+jkfkiK5Mp4hEDyRSEvmyfYadflOno/mmKJKcB17fEpEH0oJu/OWhhCJ8kJBDz2YMd/6cDl7Mg== integrity sha512-8zkGu/sv5euxbjfZ/xmklqLyDGQSxsLqg8XOq88JW3cmJtzhCP8EtSJXlaKZnVO4beEaoiT9wj4eIoCQ9smwxA==
dependencies: dependencies:
tslib "2" tslib "2"
"@formatjs/intl-segmenter@^11.5.7": "@formatjs/intl-segmenter@^11.5.7":
version "11.7.4" version "11.7.7"
resolved "https://registry.yarnpkg.com/@formatjs/intl-segmenter/-/intl-segmenter-11.7.4.tgz#f99d87ee3f98515069285438a4913681fc243252" resolved "https://registry.yarnpkg.com/@formatjs/intl-segmenter/-/intl-segmenter-11.7.7.tgz#8a5aaa316e11ca2d31b99222e6fcf1ab539b085e"
integrity sha512-pyHgFO86/CReKl20oK9jgaTMzSaG/nIMteMW8YuwUcS22EoMI1qbGTZ65oQ38KMT05SiHiMee2CP3WZvCi8YSQ== integrity sha512-610J5xz5DxtEpa16zNR89CrvA9qWHxQFkUB3FKiGao0Nwn7i8cl+oyBhuH9SvtXF9j2LUOM9VMdVCMzJkVANNw==
dependencies: dependencies:
"@formatjs/ecma402-abstract" "2.2.4" "@formatjs/ecma402-abstract" "2.3.1"
"@formatjs/intl-localematcher" "0.5.8" "@formatjs/intl-localematcher" "0.5.9"
tslib "2" tslib "2"
"@humanwhocodes/config-array@^0.13.0": "@humanwhocodes/config-array@^0.13.0":
@ -1880,9 +1881,9 @@
chalk "^4.0.0" chalk "^4.0.0"
"@jridgewell/gen-mapping@^0.3.5": "@jridgewell/gen-mapping@^0.3.5":
version "0.3.5" version "0.3.8"
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142"
integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==
dependencies: dependencies:
"@jridgewell/set-array" "^1.2.1" "@jridgewell/set-array" "^1.2.1"
"@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/sourcemap-codec" "^1.4.10"
@ -3047,9 +3048,9 @@
"@types/node" "*" "@types/node" "*"
"@types/jsrsasign@^10.5.4": "@types/jsrsasign@^10.5.4":
version "10.5.14" version "10.5.15"
resolved "https://registry.yarnpkg.com/@types/jsrsasign/-/jsrsasign-10.5.14.tgz#61d1dbd791ecd11db556c1ca5d82453fc7207338" resolved "https://registry.yarnpkg.com/@types/jsrsasign/-/jsrsasign-10.5.15.tgz#5cf1ee506b2fa2435b6e1786a873285c7110eb82"
integrity sha512-lppSlfK6etu+cuKs40K4rg8As79PH6hzIB+v55zSqImbSH3SE6Fm8MBHCiI91cWlAP3Z4igtJK1VL3fSN09blQ== integrity sha512-3stUTaSRtN09PPzVWR6aySD9gNnuymz+WviNHoTb85dKu+BjaV4uBbWWGykBBJkfwPtcNZVfTn2lbX00U+yhpQ==
"@types/katex@^0.16.0": "@types/katex@^0.16.0":
version "0.16.7" version "0.16.7"
@ -3113,9 +3114,9 @@
undici-types "~6.20.0" undici-types "~6.20.0"
"@types/node@18": "@types/node@18":
version "18.19.66" version "18.19.68"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.66.tgz#0937a47904ceba5994eedf5cf4b6d503d8d6136c" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.68.tgz#f4f10d9927a7eaf3568c46a6d739cc0967ccb701"
integrity sha512-14HmtUdGxFUalGRfLLn9Gc1oNWvWh5zNbsyOLo5JV6WARSeN1QcEBKRnZm9QqNfrutgsl/hY4eJW63aZ44aBCg== integrity sha512-QGtpFH1vB99ZmTa63K4/FU8twThj4fuVSBkGddTp7uIL/cuoLWIUSL2RcOaigBhfR+hg5pgGkBnkoOxrTVBMKw==
dependencies: dependencies:
undici-types "~5.26.4" undici-types "~5.26.4"
@ -3400,12 +3401,12 @@
resolved "https://registry.yarnpkg.com/@vector-im/compound-design-tokens/-/compound-design-tokens-2.1.1.tgz#d6175a99fe4b97688464126f255386990f3048d6" resolved "https://registry.yarnpkg.com/@vector-im/compound-design-tokens/-/compound-design-tokens-2.1.1.tgz#d6175a99fe4b97688464126f255386990f3048d6"
integrity sha512-QnUi2K14D9KTXxcLQKUU3V75cforZLMwhaaJDNftT8F5mG86950hAM+qhgDNEpEU+pkTffQj0/g/5859YmqWzQ== integrity sha512-QnUi2K14D9KTXxcLQKUU3V75cforZLMwhaaJDNftT8F5mG86950hAM+qhgDNEpEU+pkTffQj0/g/5859YmqWzQ==
"@vector-im/compound-web@^7.4.0": "@vector-im/compound-web@^7.5.0":
version "7.4.0" version "7.5.0"
resolved "https://registry.yarnpkg.com/@vector-im/compound-web/-/compound-web-7.4.0.tgz#a5af8af6346f8ff6c14c70f5d4eb2eab7357a7cc" resolved "https://registry.yarnpkg.com/@vector-im/compound-web/-/compound-web-7.5.0.tgz#1547af5f0ee27b94f79ab11eee006059f3d09707"
integrity sha512-ZRBUeEGNmj/fTkIRa8zGnyVN7ytowpfOtHChqNm+m/+OTJN3o/lOMuQHDV8jeSEW2YwPJqGvPuG/dRr89IcQkA== integrity sha512-Xhef8H5WrRmPuanzRBs8rnl+hwbcQnC7nKSCupUczAQ5hjlieBx4vcQYQ/nMkrs4rMGjgfFtR3E18wT5LlML/A==
dependencies: dependencies:
"@floating-ui/react" "^0.26.24" "@floating-ui/react" "^0.27.0"
"@radix-ui/react-context-menu" "^2.2.1" "@radix-ui/react-context-menu" "^2.2.1"
"@radix-ui/react-dropdown-menu" "^2.1.1" "@radix-ui/react-dropdown-menu" "^2.1.1"
"@radix-ui/react-form" "^0.1.0" "@radix-ui/react-form" "^0.1.0"
@ -5027,7 +5028,7 @@ decamelize@^1.2.0:
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==
decimal.js@^10.4.2: decimal.js@10, decimal.js@^10.4.2:
version "10.4.3" version "10.4.3"
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23"
integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==
@ -7828,16 +7829,21 @@ jsdom@^20.0.0:
ws "^8.11.0" ws "^8.11.0"
xml-name-validator "^4.0.0" xml-name-validator "^4.0.0"
jsesc@^3.0.2, jsesc@~3.0.2: jsesc@^3.0.2:
version "3.0.2" version "3.1.0"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d"
integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g== integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==
jsesc@~0.5.0: jsesc@~0.5.0:
version "0.5.0" version "0.5.0"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==
jsesc@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e"
integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==
json-buffer@3.0.1: json-buffer@3.0.1:
version "3.0.1" version "3.0.1"
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"