Compare commits

..

10 commits

Author SHA1 Message Date
Michael Telatynski
6946b90b11
Iterate
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-05 13:27:34 +00:00
Michael Telatynski
c3f3c9364f
Fix MapMock
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-05 13:14:13 +00:00
Michael Telatynski
72f155640d
Make React compiler happy about some frankly non-issues
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-05 12:59:53 +00:00
Michael Telatynski
b597abf567
Fix mutation of external values in hooks
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-05 12:59:35 +00:00
Michael Telatynski
9443426edb
Fix usage of useRef as memoization
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-05 12:57:39 +00:00
Michael Telatynski
6c2334c029
Merge branch 't3chguy/useId' of https://github.com/vector-im/element-web into t3chguy/react-compiler-eslint 2024-12-05 12:37:04 +00:00
Michael Telatynski
eb1a09a912
Disable in tests
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-05 12:32:36 +00:00
Michael Telatynski
e730074e1b
Fix an easy one
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-05 12:31:33 +00:00
Michael Telatynski
c8c5ef5e6e
Enable react-compiler eslint
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-05 12:31:18 +00:00
Michael Telatynski
b61a2225b7
Switch to React18 useId
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-05 12:28:27 +00:00
97 changed files with 790 additions and 1068 deletions

View file

@ -1,5 +1,5 @@
module.exports = { module.exports = {
plugins: ["matrix-org"], plugins: ["matrix-org", "eslint-plugin-react-compiler"],
extends: ["plugin:matrix-org/babel", "plugin:matrix-org/react", "plugin:matrix-org/a11y"], extends: ["plugin:matrix-org/babel", "plugin:matrix-org/react", "plugin:matrix-org/a11y"],
parserOptions: { parserOptions: {
project: ["./tsconfig.json"], project: ["./tsconfig.json"],
@ -170,6 +170,8 @@ module.exports = {
"jsx-a11y/role-supports-aria-props": "off", "jsx-a11y/role-supports-aria-props": "off",
"matrix-org/require-copyright-header": "error", "matrix-org/require-copyright-header": "error",
"react-compiler/react-compiler": "error",
}, },
overrides: [ overrides: [
{ {
@ -262,6 +264,7 @@ module.exports = {
// These are fine in tests // These are fine in tests
"no-restricted-globals": "off", "no-restricted-globals": "off",
"react-compiler/react-compiler": "off",
}, },
}, },
{ {

1
.github/CODEOWNERS vendored
View file

@ -13,7 +13,6 @@
# Ignore translations as those will be updated by GHA for Localazy download # Ignore translations as those will be updated by GHA for Localazy download
/src/i18n/strings /src/i18n/strings
/src/i18n/strings/en_EN.json @element-hq/element-web-reviewers
# Ignore the synapse plugin as this is updated by GHA for docker image updating # Ignore the synapse plugin as this is updated by GHA for docker image updating
/playwright/plugins/homeserver/synapse/index.ts /playwright/plugins/homeserver/synapse/index.ts

View file

@ -130,12 +130,8 @@ jobs:
if: steps.playwright-cache.outputs.cache-hit != 'true' if: steps.playwright-cache.outputs.cache-hit != 'true'
run: yarn playwright install --with-deps --no-shell chromium run: yarn playwright install --with-deps --no-shell chromium
# We skip tests tagged with @mergequeue when running on PRs, but run them in MQ and everywhere else
- name: Run Playwright tests - name: Run Playwright tests
run: | run: yarn playwright test --shard ${{ matrix.runner }}/${{ strategy.job-total }}
yarn playwright test \
--shard "${{ matrix.runner }}/${{ strategy.job-total }}" \
${{ github.event_name == 'pull_request' && '--grep-invert @mergequeue' || '' }}
- name: Upload blob report to GitHub Actions Artifacts - name: Upload blob report to GitHub Actions Artifacts
if: always() if: always()

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@d469d49426f5a7b8a1fbcac20ad274d3e4892321 uses: guibranco/github-status-action-v2@66088c44e212a906c32a047529a213d81809ec1c
with: with:
authToken: ${{ secrets.GITHUB_TOKEN }} authToken: ${{ secrets.GITHUB_TOKEN }}
state: success state: success

View file

@ -17,6 +17,7 @@ class MockMap extends EventEmitter {
setCenter = jest.fn(); setCenter = jest.fn();
setStyle = jest.fn(); setStyle = jest.fn();
fitBounds = jest.fn(); fitBounds = jest.fn();
remove = jest.fn();
} }
const MockMapInstance = new MockMap(); const MockMapInstance = new MockMap();

View file

@ -217,10 +217,3 @@ instead of the native `toHaveScreenshot`.
If you are running Linux and are unfortunate that the screenshots are not rendering identically, If you are running Linux and are unfortunate that the screenshots are not rendering identically,
you may wish to specify `--ignore-snapshots` and rely on Docker to render them for you. you may wish to specify `--ignore-snapshots` and rely on Docker to render them for you.
## Test Tags
We use test tags to categorise tests for running subsets more efficiently.
- `@mergequeue`: Tests that are slow or flaky and cover areas of the app we update seldom, should not be run on every PR commit but will be run in the Merge Queue.
- `@screenshot`: Tests that use `toMatchScreenshot` to speed up a run of `test:playwright:screenshots`. A test with this tag must not also have the `@mergequeue` tag as this would cause false positives in the stale screenshot detection.

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.5.0", "@vector-im/compound-web": "^7.4.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",
@ -116,10 +116,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.2.0", "linkify-element": "4.1.4",
"linkify-react": "4.2.0", "linkify-react": "4.1.4",
"linkify-string": "4.2.0", "linkify-string": "4.1.4",
"linkifyjs": "4.2.0", "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",
@ -233,6 +233,7 @@
"eslint-plugin-jsx-a11y": "^6.5.1", "eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-matrix-org": "^2.0.2", "eslint-plugin-matrix-org": "^2.0.2",
"eslint-plugin-react": "^7.28.0", "eslint-plugin-react": "^7.28.0",
"eslint-plugin-react-compiler": "^19.0.0-beta-df7b47d-20241124",
"eslint-plugin-react-hooks": "^5.0.0", "eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-unicorn": "^56.0.0", "eslint-plugin-unicorn": "^56.0.0",
"express": "^4.18.2", "express": "^4.18.2",
@ -269,7 +270,7 @@
"postcss-preset-env": "^10.0.0", "postcss-preset-env": "^10.0.0",
"postcss-scss": "^4.0.4", "postcss-scss": "^4.0.4",
"postcss-simple-vars": "^7.0.1", "postcss-simple-vars": "^7.0.1",
"prettier": "3.4.2", "prettier": "3.4.1",
"process": "^0.11.10", "process": "^0.11.10",
"raw-loader": "^4.0.2", "raw-loader": "^4.0.2",
"rimraf": "^6.0.0", "rimraf": "^6.0.0",

View file

@ -8,7 +8,6 @@ Please see LICENSE files in the repository root for full details.
import { type Preset, type Visibility } from "matrix-js-sdk/src/matrix"; import { type Preset, type Visibility } from "matrix-js-sdk/src/matrix";
import type { Page } from "@playwright/test";
import { test, expect } from "../../element-web-test"; import { test, expect } from "../../element-web-test";
import { doTwoWaySasVerification, awaitVerifier } from "./utils"; import { doTwoWaySasVerification, awaitVerifier } from "./utils";
import { Client } from "../../pages/client"; import { Client } from "../../pages/client";
@ -39,8 +38,6 @@ test.describe("User verification", () => {
toasts, toasts,
room: { roomId: dmRoomId }, room: { roomId: dmRoomId },
}) => { }) => {
await waitForDeviceKeys(page);
// once Alice has joined, Bob starts the verification // once Alice has joined, Bob starts the verification
const bobVerificationRequest = await bob.evaluateHandle( const bobVerificationRequest = await bob.evaluateHandle(
async (client, { dmRoomId, aliceCredentials }) => { async (client, { dmRoomId, aliceCredentials }) => {
@ -90,8 +87,6 @@ test.describe("User verification", () => {
toasts, toasts,
room: { roomId: dmRoomId }, room: { roomId: dmRoomId },
}) => { }) => {
await waitForDeviceKeys(page);
// once Alice has joined, Bob starts the verification // once Alice has joined, Bob starts the verification
const bobVerificationRequest = await bob.evaluateHandle( const bobVerificationRequest = await bob.evaluateHandle(
async (client, { dmRoomId, aliceCredentials }) => { async (client, { dmRoomId, aliceCredentials }) => {
@ -154,15 +149,3 @@ async function createDMRoom(client: Client, userId: string): Promise<string> {
], ],
}); });
} }
/**
* Wait until we get the other user's device keys.
* In newer rust-crypto versions, the verification request will be ignored if we
* don't have the sender's device keys.
*/
async function waitForDeviceKeys(page: Page): Promise<void> {
await expect(page.getByRole("button", { name: "Avatar" })).toBeVisible();
const avatar = await page.getByRole("button", { name: "Avatar" });
await avatar.click();
await expect(page.getByText("1 session")).toBeVisible();
}

View file

@ -129,7 +129,6 @@ export class Helpers {
const timelineMessage = this.page.locator(".mx_MTextBody", { hasText: message }); const timelineMessage = this.page.locator(".mx_MTextBody", { hasText: message });
await timelineMessage.click({ button: "right" }); await timelineMessage.click({ button: "right" });
await this.page.getByRole("menuitem", { name: "Pin", exact: true }).click(); await this.page.getByRole("menuitem", { name: "Pin", exact: true }).click();
await this.assertMessageInBanner(message);
} }
/** /**

View file

@ -10,7 +10,7 @@ Please see LICENSE files in the repository root for full details.
import { test } from "."; import { test } from ".";
test.describe("Read receipts", { tag: "@mergequeue" }, () => { test.describe("Read receipts", () => {
test.describe("editing messages", () => { test.describe("editing messages", () => {
test.describe("in threads", () => { test.describe("in threads", () => {
test("An edit of a threaded message makes the room unread", async ({ test("An edit of a threaded message makes the room unread", async ({

View file

@ -10,7 +10,7 @@ Please see LICENSE files in the repository root for full details.
import { test } from "."; import { test } from ".";
test.describe("Read receipts", { tag: "@mergequeue" }, () => { test.describe("Read receipts", () => {
test.describe("editing messages", () => { test.describe("editing messages", () => {
test.describe("in the main timeline", () => { test.describe("in the main timeline", () => {
test("Editing a message leaves a room read", async ({ roomAlpha: room1, roomBeta: room2, util, msg }) => { test("Editing a message leaves a room read", async ({ roomAlpha: room1, roomBeta: room2, util, msg }) => {

View file

@ -10,7 +10,7 @@ Please see LICENSE files in the repository root for full details.
import { test } from "."; import { test } from ".";
test.describe("Read receipts", { tag: "@mergequeue" }, () => { test.describe("Read receipts", () => {
test.describe("editing messages", () => { test.describe("editing messages", () => {
test.describe("thread roots", () => { test.describe("thread roots", () => {
test("An edit of a thread root leaves the room read", async ({ test("An edit of a thread root leaves the room read", async ({

View file

@ -10,7 +10,7 @@ Please see LICENSE files in the repository root for full details.
import { customEvent, many, test } from "."; import { customEvent, many, test } from ".";
test.describe("Read receipts", { tag: "@mergequeue" }, () => { test.describe("Read receipts", () => {
test.describe("Ignored events", () => { test.describe("Ignored events", () => {
test("If all events after receipt are unimportant, the room is read", async ({ test("If all events after receipt are unimportant, the room is read", async ({
roomAlpha: room1, roomAlpha: room1,

View file

@ -10,7 +10,7 @@ Please see LICENSE files in the repository root for full details.
import { test } from "."; import { test } from ".";
test.describe("Read receipts", { tag: "@mergequeue" }, () => { test.describe("Read receipts", () => {
test.describe("Message ordering", () => { test.describe("Message ordering", () => {
test.describe("in the main timeline", () => { test.describe("in the main timeline", () => {
test.fixme( test.fixme(

View file

@ -10,7 +10,7 @@ Please see LICENSE files in the repository root for full details.
import { test } from "."; import { test } from ".";
test.describe("Read receipts", { tag: "@mergequeue" }, () => { test.describe("Read receipts", () => {
test.describe("messages with missing referents", () => { test.describe("messages with missing referents", () => {
test.fixme( test.fixme(
"A message in an unknown thread is not visible and the room is read", "A message in an unknown thread is not visible and the room is read",

View file

@ -10,7 +10,7 @@ Please see LICENSE files in the repository root for full details.
import { many, test } from "."; import { many, test } from ".";
test.describe("Read receipts", { tag: "@mergequeue" }, () => { test.describe("Read receipts", () => {
test.describe("new messages", () => { test.describe("new messages", () => {
test.describe("in threads", () => { test.describe("in threads", () => {
test("Receiving a message makes a room unread", async ({ test("Receiving a message makes a room unread", async ({

View file

@ -10,7 +10,7 @@ Please see LICENSE files in the repository root for full details.
import { many, test } from "."; import { many, test } from ".";
test.describe("Read receipts", { tag: "@mergequeue" }, () => { test.describe("Read receipts", () => {
test.describe("new messages", () => { test.describe("new messages", () => {
test.describe("in the main timeline", () => { test.describe("in the main timeline", () => {
test("Receiving a message makes a room unread", async ({ test("Receiving a message makes a room unread", async ({

View file

@ -10,7 +10,7 @@ Please see LICENSE files in the repository root for full details.
import { many, test } from "."; import { many, test } from ".";
test.describe("Read receipts", { tag: "@mergequeue" }, () => { test.describe("Read receipts", () => {
test.describe("new messages", () => { test.describe("new messages", () => {
test.describe("thread roots", () => { test.describe("thread roots", () => {
test("Reading a thread root does not mark the thread as read", async ({ test("Reading a thread root does not mark the thread as read", async ({

View file

@ -10,7 +10,7 @@ Please see LICENSE files in the repository root for full details.
import { test } from "."; import { test } from ".";
test.describe("Read receipts", { tag: "@mergequeue" }, () => { test.describe("Read receipts", () => {
test.describe("Notifications", () => { test.describe("Notifications", () => {
test.describe("in the main timeline", () => { test.describe("in the main timeline", () => {
test.fixme("A new message that mentions me shows a notification", () => {}); test.fixme("A new message that mentions me shows a notification", () => {});

View file

@ -10,7 +10,7 @@ Please see LICENSE files in the repository root for full details.
import { test, expect } from "."; import { test, expect } from ".";
test.describe("Read receipts", { tag: "@mergequeue" }, () => { test.describe("Read receipts", () => {
test.describe("reactions", () => { test.describe("reactions", () => {
test.describe("in threads", () => { test.describe("in threads", () => {
test("A reaction to a threaded message does not make the room unread", async ({ test("A reaction to a threaded message does not make the room unread", async ({

View file

@ -10,7 +10,7 @@ Please see LICENSE files in the repository root for full details.
import { test } from "."; import { test } from ".";
test.describe("Read receipts", { tag: "@mergequeue" }, () => { test.describe("Read receipts", () => {
test.describe("reactions", () => { test.describe("reactions", () => {
test.describe("in the main timeline", () => { test.describe("in the main timeline", () => {
test("Receiving a reaction to a message does not make a room unread", async ({ test("Receiving a reaction to a message does not make a room unread", async ({

View file

@ -10,7 +10,7 @@ Please see LICENSE files in the repository root for full details.
import { test } from "."; import { test } from ".";
test.describe("Read receipts", { tag: "@mergequeue" }, () => { test.describe("Read receipts", () => {
test.describe("reactions", () => { test.describe("reactions", () => {
test.describe("thread roots", () => { test.describe("thread roots", () => {
test("A reaction to a thread root does not make the room unread", async ({ test("A reaction to a thread root does not make the room unread", async ({

View file

@ -13,7 +13,7 @@ import { ElementAppPage } from "../../pages/ElementAppPage";
import { Bot } from "../../pages/bot"; import { Bot } from "../../pages/bot";
import { test } from "."; import { test } from ".";
test.describe("Read receipts", { tag: "@mergequeue" }, () => { test.describe("Read receipts", () => {
test.use({ test.use({
displayName: "Mae", displayName: "Mae",
botCreateOpts: { displayName: "Other User" }, botCreateOpts: { displayName: "Other User" },

View file

@ -10,7 +10,7 @@ Please see LICENSE files in the repository root for full details.
import { test } from "."; import { test } from ".";
test.describe("Read receipts", { tag: "@mergequeue" }, () => { test.describe("Read receipts", () => {
test.describe("redactions", () => { test.describe("redactions", () => {
test.describe("in threads", () => { test.describe("in threads", () => {
test("Redacting the threaded message pointed to by my receipt leaves the room read", async ({ test("Redacting the threaded message pointed to by my receipt leaves the room read", async ({

View file

@ -10,7 +10,7 @@ Please see LICENSE files in the repository root for full details.
import { test } from "."; import { test } from ".";
test.describe("Read receipts", { tag: "@mergequeue" }, () => { test.describe("Read receipts", () => {
test.describe("redactions", () => { test.describe("redactions", () => {
test.describe("in the main timeline", () => { test.describe("in the main timeline", () => {
test("Redacting the message pointed to by my receipt leaves the room read", async ({ test("Redacting the message pointed to by my receipt leaves the room read", async ({

View file

@ -10,7 +10,7 @@ Please see LICENSE files in the repository root for full details.
import { test } from "."; import { test } from ".";
test.describe("Read receipts", { tag: "@mergequeue" }, () => { test.describe("Read receipts", () => {
test.describe("redactions", () => { test.describe("redactions", () => {
test.describe("thread roots", () => { test.describe("thread roots", () => {
test("Redacting a thread root after it was read leaves the room read", async ({ test("Redacting a thread root after it was read leaves the room read", async ({

View file

@ -10,7 +10,7 @@ Please see LICENSE files in the repository root for full details.
import { test } from "."; import { test } from ".";
test.describe("Read receipts", { tag: "@mergequeue" }, () => { test.describe("Read receipts", () => {
test.describe("Room list order", () => { test.describe("Room list order", () => {
test("Rooms with unread messages appear at the top of room list if 'unread first' is selected", async ({ test("Rooms with unread messages appear at the top of room list if 'unread first' is selected", async ({
roomAlpha: room1, roomAlpha: room1,

View file

@ -20,7 +20,7 @@ import { randB64Bytes } from "../../utils/rand";
// Docker tag to use for synapse docker image. // Docker tag to use for synapse docker image.
// We target a specific digest as every now and then a Synapse update will break our CI. // We target a specific digest as every now and then a Synapse update will break our CI.
// This digest is updated by the playwright-image-updates.yaml workflow periodically. // This digest is updated by the playwright-image-updates.yaml workflow periodically.
const DOCKER_TAG = "develop@sha256:6b82dba715fa7ae641010b4cc5e71edaeb9cc05a50ac5b9e4ff09afa9cd2a80d"; const DOCKER_TAG = "develop@sha256:48308e18c5b3ad20bc0d090119618f45b6be4ba727522e37fbf7827d1a109531";
async function cfgDirFromTemplate(opts: StartHomeserverOpts): Promise<Omit<HomeserverConfig, "dockerUrl">> { async function cfgDirFromTemplate(opts: StartHomeserverOpts): Promise<Omit<HomeserverConfig, "dockerUrl">> {
const templateDir = path.join(__dirname, "templates", opts.template); const templateDir = path.join(__dirname, "templates", opts.template);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 66 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: 17 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.5 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.5 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

Before After
Before After

View file

@ -44,7 +44,6 @@ 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 */
@ -118,7 +117,6 @@ 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

@ -8,24 +8,25 @@ 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, { forwardRef, Ref } from "react"; import React, { ComponentProps, forwardRef, Ref } from "react";
import AccessibleButton, { ButtonProps } from "../../components/views/elements/AccessibleButton"; import AccessibleButton from "../../components/views/elements/AccessibleButton";
type Props<T extends keyof HTMLElementTagNameMap> = ButtonProps<T> & { type Props<T extends keyof JSX.IntrinsicElements> = ComponentProps<typeof AccessibleButton<T>> & {
label?: string; label?: string;
// whether the context menu is currently open // whether the context menu is currently open
isExpanded: boolean; isExpanded: boolean;
}; };
// Semantic component for representing the AccessibleButton which launches a <ContextMenu /> // Semantic component for representing the AccessibleButton which launches a <ContextMenu />
export const ContextMenuButton = forwardRef(function <T extends keyof HTMLElementTagNameMap>( export const ContextMenuButton = forwardRef(function <T extends keyof JSX.IntrinsicElements>(
{ label, isExpanded, children, onClick, onContextMenu, ...props }: Props<T>, { label, isExpanded, children, onClick, onContextMenu, element, ...props }: Props<T>,
ref: Ref<HTMLElementTagNameMap[T]>, ref: Ref<HTMLElement>,
) { ) {
return ( return (
<AccessibleButton <AccessibleButton
{...props} {...props}
element={element as keyof JSX.IntrinsicElements}
onClick={onClick} onClick={onClick}
onContextMenu={onContextMenu ?? onClick ?? undefined} onContextMenu={onContextMenu ?? onClick ?? undefined}
aria-label={label} aria-label={label}

View file

@ -8,23 +8,24 @@ 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, { forwardRef, Ref } from "react"; import React, { ComponentProps, forwardRef, Ref } from "react";
import AccessibleButton, { ButtonProps } from "../../components/views/elements/AccessibleButton"; import AccessibleButton from "../../components/views/elements/AccessibleButton";
type Props<T extends keyof HTMLElementTagNameMap> = ButtonProps<T> & { type Props<T extends keyof JSX.IntrinsicElements> = ComponentProps<typeof AccessibleButton<T>> & {
// whether the context menu is currently open // whether the context menu is currently open
isExpanded: boolean; isExpanded: boolean;
}; };
// Semantic component for representing the AccessibleButton which launches a <ContextMenu /> // Semantic component for representing the AccessibleButton which launches a <ContextMenu />
export const ContextMenuTooltipButton = forwardRef(function <T extends keyof HTMLElementTagNameMap>( export const ContextMenuTooltipButton = forwardRef(function <T extends keyof JSX.IntrinsicElements>(
{ isExpanded, children, onClick, onContextMenu, ...props }: Props<T>, { isExpanded, children, onClick, onContextMenu, element, ...props }: Props<T>,
ref: Ref<HTMLElementTagNameMap[T]>, ref: Ref<HTMLElement>,
) { ) {
return ( return (
<AccessibleButton <AccessibleButton
{...props} {...props}
element={element as keyof JSX.IntrinsicElements}
onClick={onClick} onClick={onClick}
onContextMenu={onContextMenu ?? onClick ?? undefined} onContextMenu={onContextMenu ?? onClick ?? undefined}
aria-haspopup={true} aria-haspopup={true}

View file

@ -6,33 +6,39 @@ 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, { RefObject } from "react"; import React, { ComponentProps } from "react";
import AccessibleButton, { ButtonProps } from "../../components/views/elements/AccessibleButton"; import AccessibleButton from "../../components/views/elements/AccessibleButton";
import { useRovingTabIndex } from "../RovingTabIndex"; import { useRovingTabIndex } from "../RovingTabIndex";
import { Ref } from "./types";
type Props<T extends keyof HTMLElementTagNameMap> = Omit<ButtonProps<T>, "tabIndex"> & { type Props<T extends keyof JSX.IntrinsicElements> = Omit<
inputRef?: RefObject<HTMLElementTagNameMap[T]>; ComponentProps<typeof AccessibleButton<T>>,
"inputRef" | "tabIndex"
> & {
inputRef?: Ref;
focusOnMouseOver?: boolean; focusOnMouseOver?: boolean;
}; };
// Wrapper to allow use of useRovingTabIndex for simple AccessibleButtons outside of React Functional Components. // Wrapper to allow use of useRovingTabIndex for simple AccessibleButtons outside of React Functional Components.
export const RovingAccessibleButton = <T extends keyof HTMLElementTagNameMap>({ export const RovingAccessibleButton = <T extends keyof JSX.IntrinsicElements>({
inputRef, inputRef,
onFocus, onFocus,
onMouseOver, onMouseOver,
focusOnMouseOver, focusOnMouseOver,
element,
...props ...props
}: Props<T>): JSX.Element => { }: Props<T>): JSX.Element => {
const [onFocusInternal, isActive, ref] = useRovingTabIndex<HTMLElementTagNameMap[T]>(inputRef); const [onFocusInternal, isActive, ref] = useRovingTabIndex(inputRef);
return ( return (
<AccessibleButton <AccessibleButton
{...props} {...props}
onFocus={(event: React.FocusEvent<never, never>) => { element={element as keyof JSX.IntrinsicElements}
onFocus={(event: React.FocusEvent) => {
onFocusInternal(); onFocusInternal();
onFocus?.(event); onFocus?.(event);
}} }}
onMouseOver={(event: React.MouseEvent<never, never>) => { onMouseOver={(event: React.MouseEvent) => {
if (focusOnMouseOver) onFocusInternal(); if (focusOnMouseOver) onFocusInternal();
onMouseOver?.(event); onMouseOver?.(event);
}} }}

View file

@ -132,7 +132,6 @@ 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";
@ -429,12 +428,6 @@ 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();
@ -2080,7 +2073,14 @@ 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 = <E2eSetup onFinished={this.onCompleteSecurityE2eSetupFinished} />; view = (
<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

@ -6,7 +6,7 @@ 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, { forwardRef, useCallback, useContext, useEffect, useRef, useState } from "react"; import React, { forwardRef, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
import { import {
ISearchResults, ISearchResults,
IThreadBundledRelationship, IThreadBundledRelationship,
@ -58,7 +58,7 @@ export const RoomSearchView = forwardRef<ScrollPanel, Props>(
const [results, setResults] = useState<ISearchResults | null>(null); const [results, setResults] = useState<ISearchResults | null>(null);
const aborted = useRef(false); const aborted = useRef(false);
// A map from room ID to permalink creator // A map from room ID to permalink creator
const permalinkCreators = useRef(new Map<string, RoomPermalinkCreator>()).current; const permalinkCreators = useMemo(() => new Map<string, RoomPermalinkCreator>(), []);
const innerRef = useRef<ScrollPanel | null>(); const innerRef = useRef<ScrollPanel | null>();
useEffect(() => { useEffect(() => {

View file

@ -2372,11 +2372,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
); );
const pinnedMessageBanner = ( const pinnedMessageBanner = (
<PinnedMessageBanner <PinnedMessageBanner room={this.state.room} permalinkCreator={this.permalinkCreator} />
room={this.state.room}
permalinkCreator={this.permalinkCreator}
resizeNotifier={this.props.resizeNotifier}
/>
); );
let messageComposer; let messageComposer;

View file

@ -7,13 +7,17 @@ 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 CreateCrossSigningDialog from "../../views/dialogs/security/CreateCrossSigningDialog";
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> {
@ -21,7 +25,12 @@ export default class E2eSetup extends React.Component<IProps> {
return ( return (
<AuthPage> <AuthPage>
<CompleteSecurityBody> <CompleteSecurityBody>
<InitialCryptoSetupDialog onFinished={this.props.onFinished} /> <CreateCrossSigningDialog
matrixClient={this.props.matrixClient}
onFinished={this.props.onFinished}
accountPassword={this.props.accountPassword}
tokenLogin={this.props.tokenLogin}
/>
</CompleteSecurityBody> </CompleteSecurityBody>
</AuthPage> </AuthPage>
); );

View file

@ -235,7 +235,12 @@ export default class SoftLogout extends React.Component<IProps, IState> {
value={this.state.password} value={this.state.password}
disabled={this.state.busy} disabled={this.state.busy}
/> />
<AccessibleButton onClick={this.onPasswordLogin} kind="primary" disabled={this.state.busy}> <AccessibleButton
onClick={this.onPasswordLogin}
kind="primary"
type="submit"
disabled={this.state.busy}
>
{_t("action|sign_in")} {_t("action|sign_in")}
</AccessibleButton> </AccessibleButton>
<AccessibleButton onClick={this.onForgotPassword} kind="link"> <AccessibleButton onClick={this.onForgotPassword} kind="link">

View file

@ -910,7 +910,7 @@ export class SSOAuthEntry extends React.Component<ISSOAuthEntryProps, ISSOAuthEn
export class FallbackAuthEntry<T = {}> extends React.Component<IAuthEntryProps & T> { export class FallbackAuthEntry<T = {}> extends React.Component<IAuthEntryProps & T> {
protected popupWindow: Window | null; protected popupWindow: Window | null;
protected fallbackButton = createRef<HTMLDivElement>(); protected fallbackButton = createRef<HTMLButtonElement>();
public constructor(props: IAuthEntryProps & T) { public constructor(props: IAuthEntryProps & T) {
super(props); super(props);

View file

@ -298,7 +298,7 @@ const SettingsList: React.FC<ISettingsListProps> = ({ onBack, onView, onEdit })
<code>{i}</code> <code>{i}</code>
</AccessibleButton> </AccessibleButton>
<AccessibleButton <AccessibleButton
title={_t("devtools|edit_setting")} alt={_t("devtools|edit_setting")}
onClick={() => onEdit(i)} onClick={() => onEdit(i)}
className="mx_DevTools_SettingsExplorer_edit" className="mx_DevTools_SettingsExplorer_edit"
> >

View file

@ -0,0 +1,99 @@
/*
Copyright 2024 New Vector Ltd.
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
Copyright 2018, 2019 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 React, { useCallback, useEffect, useState } from "react";
import { logger } from "matrix-js-sdk/src/logger";
import { MatrixClient } from "matrix-js-sdk/src/matrix";
import { _t } from "../../../../languageHandler";
import DialogButtons from "../../elements/DialogButtons";
import BaseDialog from "../BaseDialog";
import Spinner from "../../elements/Spinner";
import { createCrossSigning } from "../../../../CreateCrossSigning";
interface Props {
matrixClient: MatrixClient;
accountPassword?: string;
tokenLogin: boolean;
onFinished: (success?: boolean) => void;
}
/*
* Walks the user through the process of creating a cross-signing keys. 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.
*/
const CreateCrossSigningDialog: React.FC<Props> = ({ matrixClient, accountPassword, tokenLogin, onFinished }) => {
const [error, setError] = useState(false);
const bootstrapCrossSigning = useCallback(async () => {
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]);
useEffect(() => {
bootstrapCrossSigning();
}, [bootstrapCrossSigning]);
let content;
if (error) {
content = (
<div>
<p>{_t("encryption|unable_to_setup_keys_error")}</p>
<div className="mx_Dialog_buttons">
<DialogButtons
primaryButton={_t("action|retry")}
onPrimaryButtonClick={bootstrapCrossSigning}
onCancel={onCancel}
/>
</div>
</div>
);
} else {
content = (
<div>
<Spinner />
</div>
);
}
return (
<BaseDialog
className="mx_CreateCrossSigningDialog"
onFinished={onFinished}
title={_t("encryption|bootstrap_title")}
hasCancel={false}
fixedWidth={false}
>
<div>{content}</div>
</BaseDialog>
);
};
export default CreateCrossSigningDialog;

View file

@ -1,71 +0,0 @@
/*
Copyright 2024 New Vector Ltd.
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
Copyright 2018, 2019 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 React, { useCallback } from "react";
import { _t } from "../../../../languageHandler";
import DialogButtons from "../../elements/DialogButtons";
import BaseDialog from "../BaseDialog";
import Spinner from "../../elements/Spinner";
import { InitialCryptoSetupStore, useInitialCryptoSetupStatus } from "../../../../stores/InitialCryptoSetupStore";
interface Props {
onFinished: (success?: boolean) => void;
}
/*
* Walks the user through the process of creating a cross-signing keys.
* 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.
*/
export const InitialCryptoSetupDialog: React.FC<Props> = ({ onFinished }) => {
const onRetryClick = useCallback(() => {
InitialCryptoSetupStore.sharedInstance().retry();
}, []);
const onCancelClick = useCallback(() => {
onFinished(false);
}, [onFinished]);
const status = useInitialCryptoSetupStatus(InitialCryptoSetupStore.sharedInstance());
let content;
if (status === "error") {
content = (
<div>
<p>{_t("encryption|unable_to_setup_keys_error")}</p>
<div className="mx_Dialog_buttons">
<DialogButtons
primaryButton={_t("action|retry")}
onPrimaryButtonClick={onRetryClick}
onCancel={onCancelClick}
/>
</div>
</div>
);
} else {
content = (
<div>
<Spinner />
</div>
);
}
return (
<BaseDialog
className="mx_CreateCrossSigningDialog"
onFinished={onFinished}
title={_t("encryption|bootstrap_title")}
hasCancel={false}
fixedWidth={false}
>
<div>{content}</div>
</BaseDialog>
);
};

View file

@ -1253,7 +1253,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
<span>{filterToLabel(filter)}</span> <span>{filterToLabel(filter)}</span>
<AccessibleButton <AccessibleButton
tabIndex={-1} tabIndex={-1}
title={_t("spotlight_dialog|remove_filter", { alt={_t("spotlight_dialog|remove_filter", {
filter: filterToLabel(filter), filter: filterToLabel(filter),
})} })}
className="mx_SpotlightDialog_filter--close" className="mx_SpotlightDialog_filter--close"

View file

@ -13,15 +13,15 @@ import { useRovingTabIndex } from "../../../../accessibility/RovingTabIndex";
import AccessibleButton, { ButtonProps } from "../../elements/AccessibleButton"; import AccessibleButton, { ButtonProps } from "../../elements/AccessibleButton";
import { Ref } from "../../../../accessibility/roving/types"; import { Ref } from "../../../../accessibility/roving/types";
type TooltipOptionProps<T extends keyof HTMLElementTagNameMap> = ButtonProps<T> & { type TooltipOptionProps<T extends keyof JSX.IntrinsicElements> = ButtonProps<T> & {
className?: string;
endAdornment?: ReactNode; endAdornment?: ReactNode;
inputRef?: Ref; inputRef?: Ref;
}; };
export const TooltipOption = <T extends keyof HTMLElementTagNameMap>({ export const TooltipOption = <T extends keyof JSX.IntrinsicElements>({
inputRef, inputRef,
className, className,
element,
...props ...props
}: TooltipOptionProps<T>): JSX.Element => { }: TooltipOptionProps<T>): JSX.Element => {
const [onFocus, isActive, ref] = useRovingTabIndex(inputRef); const [onFocus, isActive, ref] = useRovingTabIndex(inputRef);
@ -34,6 +34,7 @@ export const TooltipOption = <T extends keyof HTMLElementTagNameMap>({
tabIndex={-1} tabIndex={-1}
aria-selected={isActive} aria-selected={isActive}
role="option" role="option"
element={element as keyof JSX.IntrinsicElements}
/> />
); );
}; };

View file

@ -168,7 +168,7 @@ export const NetworkDropdown: React.FC<IProps> = ({ protocols, config, setConfig
adornment: ( adornment: (
<AccessibleButton <AccessibleButton
className="mx_NetworkDropdown_removeServer" className="mx_NetworkDropdown_removeServer"
title={_t("spotlight|public_rooms|network_dropdown_remove_server_adornment", { roomServer })} alt={_t("spotlight|public_rooms|network_dropdown_remove_server_adornment", { roomServer })}
onClick={() => setUserDefinedServers(without(userDefinedServers, roomServer))} onClick={() => setUserDefinedServers(without(userDefinedServers, roomServer))}
/> />
), ),

View file

@ -6,15 +6,7 @@ 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, { import React, { ComponentProps, forwardRef, FunctionComponent, HTMLAttributes, InputHTMLAttributes, Ref } from "react";
ComponentProps,
ComponentPropsWithoutRef,
forwardRef,
FunctionComponent,
ReactElement,
KeyboardEvent,
Ref,
} from "react";
import classnames from "classnames"; import classnames from "classnames";
import { Tooltip } from "@vector-im/compound-web"; import { Tooltip } from "@vector-im/compound-web";
@ -46,8 +38,20 @@ export type AccessibleButtonKind =
| "icon_primary" | "icon_primary"
| "icon_primary_outline"; | "icon_primary_outline";
type ElementType = keyof HTMLElementTagNameMap; /**
const defaultElement = "div"; * This type construct allows us to specifically pass those props down to the element were creating that the element
* actually supports.
*
* e.g., if element is set to "a", well support href and target, if its set to "input", we support type.
*
* To remain compatible with existing code, well continue to support InputHTMLAttributes<Element>
*/
type DynamicHtmlElementProps<T extends keyof JSX.IntrinsicElements> =
JSX.IntrinsicElements[T] extends HTMLAttributes<{}> ? DynamicElementProps<T> : DynamicElementProps<"div">;
type DynamicElementProps<T extends keyof JSX.IntrinsicElements> = Partial<
Omit<JSX.IntrinsicElements[T], "ref" | "onClick" | "onMouseDown" | "onKeyUp" | "onKeyDown">
> &
Omit<InputHTMLAttributes<Element>, "onClick">;
type TooltipProps = ComponentProps<typeof Tooltip>; type TooltipProps = ComponentProps<typeof Tooltip>;
@ -56,7 +60,7 @@ type TooltipProps = ComponentProps<typeof Tooltip>;
* *
* Extends props accepted by the underlying element specified using the `element` prop. * Extends props accepted by the underlying element specified using the `element` prop.
*/ */
type Props<T extends ElementType = "div"> = { type Props<T extends keyof JSX.IntrinsicElements> = DynamicHtmlElementProps<T> & {
/** /**
* The base element type. "div" by default. * The base element type. "div" by default.
*/ */
@ -101,12 +105,14 @@ type Props<T extends ElementType = "div"> = {
disableTooltip?: TooltipProps["disabled"]; disableTooltip?: TooltipProps["disabled"];
}; };
export type ButtonProps<T extends ElementType> = Props<T> & Omit<ComponentPropsWithoutRef<T>, keyof Props<T>>; export type ButtonProps<T extends keyof JSX.IntrinsicElements> = Props<T>;
/** /**
* Type of the props passed to the element that is rendered by AccessibleButton. * Type of the props passed to the element that is rendered by AccessibleButton.
*/ */
type RenderedElementProps<T extends ElementType> = React.InputHTMLAttributes<Element> & RefProp<T>; interface RenderedElementProps extends React.InputHTMLAttributes<Element> {
ref?: React.Ref<Element>;
}
/** /**
* AccessibleButton is a generic wrapper for any element that should be treated * AccessibleButton is a generic wrapper for any element that should be treated
@ -118,9 +124,9 @@ type RenderedElementProps<T extends ElementType> = React.InputHTMLAttributes<Ele
* @param {Object} props react element properties * @param {Object} props react element properties
* @returns {Object} rendered react * @returns {Object} rendered react
*/ */
const AccessibleButton = forwardRef(function <T extends ElementType = typeof defaultElement>( const AccessibleButton = forwardRef(function <T extends keyof JSX.IntrinsicElements>(
{ {
element, element = "div" as T,
onClick, onClick,
children, children,
kind, kind,
@ -135,10 +141,10 @@ const AccessibleButton = forwardRef(function <T extends ElementType = typeof def
onTooltipOpenChange, onTooltipOpenChange,
disableTooltip, disableTooltip,
...restProps ...restProps
}: ButtonProps<T>, }: Props<T>,
ref: Ref<HTMLElementTagNameMap[T]>, ref: Ref<HTMLElement>,
): JSX.Element { ): JSX.Element {
const newProps = restProps as RenderedElementProps<T>; const newProps: RenderedElementProps = restProps;
newProps["aria-label"] = newProps["aria-label"] ?? title; newProps["aria-label"] = newProps["aria-label"] ?? title;
if (disabled) { if (disabled) {
newProps["aria-disabled"] = true; newProps["aria-disabled"] = true;
@ -156,7 +162,7 @@ const AccessibleButton = forwardRef(function <T extends ElementType = typeof def
// And divs which we report as role button to assistive technologies. // And divs which we report as role button to assistive technologies.
// Browsers handle space and enter key presses differently and we are only adjusting to the // Browsers handle space and enter key presses differently and we are only adjusting to the
// inconsistencies here // inconsistencies here
newProps.onKeyDown = (e: KeyboardEvent<never>) => { newProps.onKeyDown = (e) => {
const action = getKeyBindingsManager().getAccessibilityAction(e); const action = getKeyBindingsManager().getAccessibilityAction(e);
switch (action) { switch (action) {
@ -172,7 +178,7 @@ const AccessibleButton = forwardRef(function <T extends ElementType = typeof def
onKeyDown?.(e); onKeyDown?.(e);
} }
}; };
newProps.onKeyUp = (e: KeyboardEvent<never>) => { newProps.onKeyUp = (e) => {
const action = getKeyBindingsManager().getAccessibilityAction(e); const action = getKeyBindingsManager().getAccessibilityAction(e);
switch (action) { switch (action) {
@ -201,7 +207,7 @@ const AccessibleButton = forwardRef(function <T extends ElementType = typeof def
}); });
// React.createElement expects InputHTMLAttributes // React.createElement expects InputHTMLAttributes
const button = React.createElement(element ?? defaultElement, newProps, children); const button = React.createElement(element, newProps, children);
if (title) { if (title) {
return ( return (
@ -227,15 +233,4 @@ const AccessibleButton = forwardRef(function <T extends ElementType = typeof def
}; };
(AccessibleButton as FunctionComponent).displayName = "AccessibleButton"; (AccessibleButton as FunctionComponent).displayName = "AccessibleButton";
interface RefProp<T extends ElementType> { export default AccessibleButton;
ref?: Ref<HTMLElementTagNameMap[T]>;
}
interface ButtonComponent {
// With the explicit `element` prop
<C extends ElementType>(props: { element?: C } & ButtonProps<C> & RefProp<C>): ReactElement;
// Without the explicit `element` prop
(props: ButtonProps<"div"> & RefProp<"div">): ReactElement;
}
export default AccessibleButton as ButtonComponent;

View file

@ -133,7 +133,12 @@ export default class EditableItemList<P = {}> extends React.PureComponent<IProps
onChange={this.onNewItemChanged} onChange={this.onNewItemChanged}
list={this.props.suggestionsListId} list={this.props.suggestionsListId}
/> />
<AccessibleButton onClick={this.onItemAdded} kind="primary" disabled={!this.props.newItem}> <AccessibleButton
onClick={this.onItemAdded}
kind="primary"
type="submit"
disabled={!this.props.newItem}
>
{_t("action|add")} {_t("action|add")}
</AccessibleButton> </AccessibleButton>
</form> </form>

View file

@ -58,11 +58,10 @@ const EffectsOverlay: FunctionComponent<IProps> = ({ roomWidth }) => {
if (canvas) canvas.height = UIStore.instance.windowHeight; if (canvas) canvas.height = UIStore.instance.windowHeight;
UIStore.instance.on(UI_EVENTS.Resize, resize); UIStore.instance.on(UI_EVENTS.Resize, resize);
const currentEffects = effectsRef.current; // this is not a react node ref, warning can be safely ignored
return () => { return () => {
dis.unregister(dispatcherRef); dis.unregister(dispatcherRef);
UIStore.instance.off(UI_EVENTS.Resize, resize); UIStore.instance.off(UI_EVENTS.Resize, resize);
// eslint-disable-next-line react-hooks/exhaustive-deps
const currentEffects = effectsRef.current; // this is not a react node ref, warning can be safely ignored
for (const effect in currentEffects) { for (const effect in currentEffects) {
const effectModule: ICanvasEffect = currentEffects.get(effect)!; const effectModule: ICanvasEffect = currentEffects.get(effect)!;
if (effectModule && effectModule.isRunning) { if (effectModule && effectModule.isRunning) {

View file

@ -31,7 +31,7 @@ class Emoji extends React.PureComponent<IProps> {
return ( return (
<RovingAccessibleButton <RovingAccessibleButton
id={this.props.id} id={this.props.id}
onClick={(ev: ButtonEvent) => onClick(ev, emoji)} onClick={(ev) => onClick(ev, emoji)}
onMouseEnter={() => onMouseEnter(emoji)} onMouseEnter={() => onMouseEnter(emoji)}
onMouseLeave={() => onMouseLeave(emoji)} onMouseLeave={() => onMouseLeave(emoji)}
className="mx_EmojiPicker_item_wrapper" className="mx_EmojiPicker_item_wrapper"

View file

@ -90,7 +90,7 @@ export const MPollEndBody = React.forwardRef<any, IBodyProps>(({ mxEvent, ...pro
const { pollStartEvent, isLoadingPollStartEvent } = usePollStartEvent(mxEvent); const { pollStartEvent, isLoadingPollStartEvent } = usePollStartEvent(mxEvent);
if (!pollStartEvent) { if (!pollStartEvent) {
const pollEndFallbackMessage = M_TEXT.findIn<string>(mxEvent.getContent()) || textForEvent(mxEvent, cli); const pollEndFallbackMessage = M_TEXT.findIn(mxEvent.getContent()) || textForEvent(mxEvent, cli);
return ( return (
<> <>
<PollIcon className="mx_MPollEndBody_icon" /> <PollIcon className="mx_MPollEndBody_icon" />

View file

@ -435,7 +435,7 @@ export default class MessageActionBar extends React.PureComponent<IMessageAction
<RovingAccessibleButton <RovingAccessibleButton
className="mx_MessageActionBar_iconButton" className="mx_MessageActionBar_iconButton"
title={isPinned ? _t("action|unpin") : _t("action|pin")} title={isPinned ? _t("action|unpin") : _t("action|pin")}
onClick={(e: ButtonEvent) => this.onPinClick(e, isPinned)} onClick={(e) => this.onPinClick(e, isPinned)}
onContextMenu={(e: ButtonEvent) => this.onPinClick(e, isPinned)} onContextMenu={(e: ButtonEvent) => this.onPinClick(e, isPinned)}
key="pin" key="pin"
placement="left" placement="left"

View file

@ -6,10 +6,10 @@
* Please see LICENSE files in the repository root for full details. * Please see LICENSE files in the repository root for full details.
*/ */
import React, { JSX, useEffect, useRef, useState } from "react"; import React, { JSX, useEffect, useState } from "react";
import PinIcon from "@vector-im/compound-design-tokens/assets/web/icons/pin-solid"; import PinIcon from "@vector-im/compound-design-tokens/assets/web/icons/pin-solid";
import { Button } from "@vector-im/compound-web"; import { Button } from "@vector-im/compound-web";
import { MatrixEvent, Room } from "matrix-js-sdk/src/matrix"; import { Room } from "matrix-js-sdk/src/matrix";
import classNames from "classnames"; import classNames from "classnames";
import { usePinnedEvents, useSortedFetchedPinnedEvents } from "../../../hooks/usePinnedEvents"; import { usePinnedEvents, useSortedFetchedPinnedEvents } from "../../../hooks/usePinnedEvents";
@ -25,7 +25,6 @@ import { Action } from "../../../dispatcher/actions";
import MessageEvent from "../messages/MessageEvent"; import MessageEvent from "../messages/MessageEvent";
import PosthogTrackers from "../../../PosthogTrackers.ts"; import PosthogTrackers from "../../../PosthogTrackers.ts";
import { EventPreview } from "./EventPreview.tsx"; import { EventPreview } from "./EventPreview.tsx";
import ResizeNotifier from "../../../utils/ResizeNotifier";
/** /**
* The props for the {@link PinnedMessageBanner} component. * The props for the {@link PinnedMessageBanner} component.
@ -39,20 +38,12 @@ interface PinnedMessageBannerProps {
* The room where the banner is displayed * The room where the banner is displayed
*/ */
room: Room; room: Room;
/**
* The resize notifier to notify the timeline to resize itself when the banner is displayed or hidden.
*/
resizeNotifier: ResizeNotifier;
} }
/** /**
* A banner that displays the pinned messages in a room. * A banner that displays the pinned messages in a room.
*/ */
export function PinnedMessageBanner({ export function PinnedMessageBanner({ room, permalinkCreator }: PinnedMessageBannerProps): JSX.Element | null {
room,
permalinkCreator,
resizeNotifier,
}: PinnedMessageBannerProps): JSX.Element | null {
const pinnedEventIds = usePinnedEvents(room); const pinnedEventIds = usePinnedEvents(room);
const pinnedEvents = useSortedFetchedPinnedEvents(room, pinnedEventIds); const pinnedEvents = useSortedFetchedPinnedEvents(room, pinnedEventIds);
const eventCount = pinnedEvents.length; const eventCount = pinnedEvents.length;
@ -65,8 +56,6 @@ export function PinnedMessageBanner({
}, [eventCount]); }, [eventCount]);
const pinnedEvent = pinnedEvents[currentEventIndex]; const pinnedEvent = pinnedEvents[currentEventIndex];
useNotifyTimeline(pinnedEvent, resizeNotifier);
if (!pinnedEvent) return null; if (!pinnedEvent) return null;
const shouldUseMessageEvent = pinnedEvent.isRedacted() || pinnedEvent.isDecryptionFailure(); const shouldUseMessageEvent = pinnedEvent.isRedacted() || pinnedEvent.isDecryptionFailure();
@ -139,23 +128,6 @@ export function PinnedMessageBanner({
); );
} }
/**
* When the banner is displayed or hidden, we want to notify the timeline to resize itself.
* @param pinnedEvent
* @param resizeNotifier
*/
function useNotifyTimeline(pinnedEvent: MatrixEvent | null, resizeNotifier: ResizeNotifier): void {
const previousEvent = useRef<MatrixEvent | null>(null);
useEffect(() => {
// If we switch from a pinned message to no pinned message or the opposite, we want to resize the timeline
if ((previousEvent.current && !pinnedEvent) || (!previousEvent.current && pinnedEvent)) {
resizeNotifier.notifyTimelineHeightChanged();
}
previousEvent.current = pinnedEvent;
}, [pinnedEvent, resizeNotifier]);
}
const MAX_INDICATORS = 3; const MAX_INDICATORS = 3;
/** /**

View file

@ -6,7 +6,7 @@ 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, { ForwardedRef, forwardRef, MutableRefObject, useRef } from "react"; import React, { ForwardedRef, forwardRef, MutableRefObject, useMemo } from "react";
import classNames from "classnames"; import classNames from "classnames";
import EditorStateTransfer from "../../../../utils/EditorStateTransfer"; import EditorStateTransfer from "../../../../utils/EditorStateTransfer";
@ -44,7 +44,7 @@ export default function EditWysiwygComposer({
className, className,
...props ...props
}: EditWysiwygComposerProps): JSX.Element { }: EditWysiwygComposerProps): JSX.Element {
const defaultContextValue = useRef(getDefaultContextValue({ editorStateTransfer })); const defaultContextValue = useMemo(() => getDefaultContextValue({ editorStateTransfer }), []);
const initialContent = useInitialContent(editorStateTransfer); const initialContent = useInitialContent(editorStateTransfer);
const isReady = !editorStateTransfer || initialContent !== undefined; const isReady = !editorStateTransfer || initialContent !== undefined;
@ -55,7 +55,7 @@ export default function EditWysiwygComposer({
} }
return ( return (
<ComposerContext.Provider value={defaultContextValue.current}> <ComposerContext.Provider value={defaultContextValue}>
<WysiwygComposer <WysiwygComposer
className={classNames("mx_EditWysiwygComposer", className)} className={classNames("mx_EditWysiwygComposer", className)}
initialContent={initialContent} initialContent={initialContent}

View file

@ -6,7 +6,7 @@ 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, { ForwardedRef, forwardRef, MutableRefObject, useRef } from "react"; import React, { ForwardedRef, forwardRef, MutableRefObject, useMemo } from "react";
import { IEventRelation } from "matrix-js-sdk/src/matrix"; import { IEventRelation } from "matrix-js-sdk/src/matrix";
import { useWysiwygSendActionHandler } from "./hooks/useWysiwygSendActionHandler"; import { useWysiwygSendActionHandler } from "./hooks/useWysiwygSendActionHandler";
@ -52,10 +52,10 @@ export default function SendWysiwygComposer({
...props ...props
}: SendWysiwygComposerProps): JSX.Element { }: SendWysiwygComposerProps): JSX.Element {
const Composer = isRichTextEnabled ? WysiwygComposer : PlainTextComposer; const Composer = isRichTextEnabled ? WysiwygComposer : PlainTextComposer;
const defaultContextValue = useRef(getDefaultContextValue({ eventRelation: props.eventRelation })); const defaultContextValue = useMemo(() => getDefaultContextValue({ eventRelation: props.eventRelation }), []);
return ( return (
<ComposerContext.Provider value={defaultContextValue.current}> <ComposerContext.Provider value={defaultContextValue}>
<Composer <Composer
className="mx_SendWysiwygComposer" className="mx_SendWysiwygComposer"
leftComponent={e2eStatus && <E2EIcon status={e2eStatus} />} leftComponent={e2eStatus && <E2EIcon status={e2eStatus} />}

View file

@ -407,6 +407,7 @@ export default class SetIdServer extends React.Component<IProps, IState> {
forceValidity={this.state.error ? false : undefined} forceValidity={this.state.error ? false : undefined}
/> />
<AccessibleButton <AccessibleButton
type="submit"
kind="primary_sm" kind="primary_sm"
onClick={this.checkIdServer} onClick={this.checkIdServer}
disabled={!this.idServerChangeEnabled()} disabled={!this.idServerChangeEnabled()}

View file

@ -6,7 +6,7 @@
* 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, JSX, useCallback, useMemo, useRef, useState } from "react"; import React, { ChangeEvent, JSX, useCallback, useMemo, useState } from "react";
import { import {
InlineField, InlineField,
ToggleControl, ToggleControl,
@ -39,12 +39,12 @@ import { useSettingValue } from "../../../hooks/useSettings";
*/ */
export function ThemeChoicePanel(): JSX.Element { export function ThemeChoicePanel(): JSX.Element {
const themeState = useTheme(); const themeState = useTheme();
const themeWatcher = useRef(new ThemeWatcher()); const themeWatcher = useMemo(() => new ThemeWatcher(), []);
const customThemeEnabled = useSettingValue<boolean>("feature_custom_themes"); const customThemeEnabled = useSettingValue<boolean>("feature_custom_themes");
return ( return (
<SettingsSubsection heading={_t("common|theme")} legacy={false} data-testid="themePanel"> <SettingsSubsection heading={_t("common|theme")} legacy={false} data-testid="themePanel">
{themeWatcher.current.isSystemThemeSupported() && ( {themeWatcher.isSystemThemeSupported() && (
<SystemTheme systemThemeActivated={themeState.systemThemeActivated} /> <SystemTheme systemThemeActivated={themeState.systemThemeActivated} />
)} )}
<ThemeSelectors theme={themeState.theme} disabled={themeState.systemThemeActivated} /> <ThemeSelectors theme={themeState.theme} disabled={themeState.systemThemeActivated} />

View file

@ -7,21 +7,23 @@ Please see LICENSE files in the repository root for full details.
*/ */
import classNames from "classnames"; import classNames from "classnames";
import React from "react"; import React, { ComponentProps } from "react";
import { ChevronDownIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; import { ChevronDownIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
import { _t } from "../../../../languageHandler"; import { _t } from "../../../../languageHandler";
import AccessibleButton, { ButtonProps } from "../../elements/AccessibleButton"; import AccessibleButton from "../../elements/AccessibleButton";
type Props<T extends keyof HTMLElementTagNameMap> = Omit< type Props<T extends keyof JSX.IntrinsicElements> = Omit<
ButtonProps<T>, ComponentProps<typeof AccessibleButton<T>>,
"aria-label" | "title" | "kind" | "className" | "element" "aria-label" | "title" | "kind" | "className" | "onClick" | "element"
> & { > & {
isExpanded: boolean; isExpanded: boolean;
onClick: () => void;
}; };
export const DeviceExpandDetailsButton = <T extends keyof HTMLElementTagNameMap>({ export const DeviceExpandDetailsButton = <T extends keyof JSX.IntrinsicElements>({
isExpanded, isExpanded,
onClick,
...rest ...rest
}: Props<T>): JSX.Element => { }: Props<T>): JSX.Element => {
const label = isExpanded ? _t("settings|sessions|hide_details") : _t("settings|sessions|show_details"); const label = isExpanded ? _t("settings|sessions|hide_details") : _t("settings|sessions|show_details");
@ -34,6 +36,7 @@ export const DeviceExpandDetailsButton = <T extends keyof HTMLElementTagNameMap>
className={classNames("mx_DeviceExpandDetailsButton", { className={classNames("mx_DeviceExpandDetailsButton", {
mx_DeviceExpandDetailsButton_expanded: isExpanded, mx_DeviceExpandDetailsButton_expanded: isExpanded,
})} })}
onClick={onClick}
> >
<ChevronDownIcon className="mx_DeviceExpandDetailsButton_icon" /> <ChevronDownIcon className="mx_DeviceExpandDetailsButton_icon" />
</AccessibleButton> </AccessibleButton>

View file

@ -8,6 +8,7 @@ Please see LICENSE files in the repository root for full details.
import React, { useCallback, useMemo, useState } from "react"; import React, { useCallback, useMemo, useState } from "react";
import { JoinRule, EventType, RoomState, Room } from "matrix-js-sdk/src/matrix"; import { JoinRule, EventType, RoomState, Room } from "matrix-js-sdk/src/matrix";
import { RoomPowerLevelsEventContent } from "matrix-js-sdk/src/types";
import { _t } from "../../../../../languageHandler"; import { _t } from "../../../../../languageHandler";
import LabelledToggleSwitch from "../../../elements/LabelledToggleSwitch"; import LabelledToggleSwitch from "../../../elements/LabelledToggleSwitch";
@ -24,48 +25,49 @@ interface ElementCallSwitchProps {
const ElementCallSwitch: React.FC<ElementCallSwitchProps> = ({ room }) => { const ElementCallSwitch: React.FC<ElementCallSwitchProps> = ({ room }) => {
const isPublic = useMemo(() => room.getJoinRule() === JoinRule.Public, [room]); const isPublic = useMemo(() => room.getJoinRule() === JoinRule.Public, [room]);
const [content, events, maySend] = useRoomState( const [content, maySend] = useRoomState(
room, room,
useCallback( useCallback(
(state: RoomState) => { (state: RoomState) => {
const content = state?.getStateEvents(EventType.RoomPowerLevels, "")?.getContent(); const content = state
?.getStateEvents(EventType.RoomPowerLevels, "")
?.getContent<RoomPowerLevelsEventContent>();
return [ return [
content ?? {}, content ?? {},
content?.["events"] ?? {},
state?.maySendStateEvent(EventType.RoomPowerLevels, room.client.getSafeUserId()), state?.maySendStateEvent(EventType.RoomPowerLevels, room.client.getSafeUserId()),
]; ] as const;
}, },
[room.client], [room.client],
), ),
); );
const [elementCallEnabled, setElementCallEnabled] = useState<boolean>(() => { const [elementCallEnabled, setElementCallEnabled] = useState<boolean>(() => {
return events[ElementCall.MEMBER_EVENT_TYPE.name] === 0; return content.events?.[ElementCall.MEMBER_EVENT_TYPE.name] === 0;
}); });
const onChange = useCallback( const onChange = useCallback(
(enabled: boolean): void => { (enabled: boolean): void => {
setElementCallEnabled(enabled); setElementCallEnabled(enabled);
// Take a copy to avoid mutating the original
const newContent = { events: {}, ...content };
if (enabled) { if (enabled) {
const userLevel = events[EventType.RoomMessage] ?? content.users_default ?? 0; const userLevel = newContent.events[EventType.RoomMessage] ?? content.users_default ?? 0;
const moderatorLevel = content.kick ?? 50; const moderatorLevel = content.kick ?? 50;
events[ElementCall.CALL_EVENT_TYPE.name] = isPublic ? moderatorLevel : userLevel; newContent.events[ElementCall.CALL_EVENT_TYPE.name] = isPublic ? moderatorLevel : userLevel;
events[ElementCall.MEMBER_EVENT_TYPE.name] = userLevel; newContent.events[ElementCall.MEMBER_EVENT_TYPE.name] = userLevel;
} else { } else {
const adminLevel = events[EventType.RoomPowerLevels] ?? content.state_default ?? 100; const adminLevel = newContent.events[EventType.RoomPowerLevels] ?? content.state_default ?? 100;
events[ElementCall.CALL_EVENT_TYPE.name] = adminLevel; newContent.events[ElementCall.CALL_EVENT_TYPE.name] = adminLevel;
events[ElementCall.MEMBER_EVENT_TYPE.name] = adminLevel; newContent.events[ElementCall.MEMBER_EVENT_TYPE.name] = adminLevel;
} }
room.client.sendStateEvent(room.roomId, EventType.RoomPowerLevels, { room.client.sendStateEvent(room.roomId, EventType.RoomPowerLevels, newContent);
events: events,
...content,
});
}, },
[room.client, room.roomId, content, events, isPublic], [room.client, room.roomId, content, isPublic],
); );
const brand = SdkConfig.get("element_call").brand ?? DEFAULTS.element_call.brand; const brand = SdkConfig.get("element_call").brand ?? DEFAULTS.element_call.brand;

View file

@ -268,6 +268,7 @@ export default class MjolnirUserSettingsTab extends React.Component<{}, IState>
onChange={this.onPersonalRuleChanged} onChange={this.onPersonalRuleChanged}
/> />
<AccessibleButton <AccessibleButton
type="submit"
kind="primary" kind="primary"
onClick={this.onAddPersonalRule} onClick={this.onAddPersonalRule}
disabled={this.state.busy} disabled={this.state.busy}
@ -294,7 +295,12 @@ export default class MjolnirUserSettingsTab extends React.Component<{}, IState>
value={this.state.newList} value={this.state.newList}
onChange={this.onNewListChanged} onChange={this.onNewListChanged}
/> />
<AccessibleButton kind="primary" onClick={this.onSubscribeList} disabled={this.state.busy}> <AccessibleButton
type="submit"
kind="primary"
onClick={this.onSubscribeList}
disabled={this.state.busy}
>
{_t("action|subscribe")} {_t("action|subscribe")}
</AccessibleButton> </AccessibleButton>
</form> </form>

View file

@ -27,7 +27,7 @@ type Props = {
const MATCH_SYSTEM_THEME_ID = "MATCH_SYSTEM_THEME_ID"; const MATCH_SYSTEM_THEME_ID = "MATCH_SYSTEM_THEME_ID";
const QuickThemeSwitcher: React.FC<Props> = ({ requestClose }) => { const QuickThemeSwitcher: React.FC<Props> = ({ requestClose }) => {
const orderedThemes = useMemo(getOrderedThemes, []); const orderedThemes = useMemo(() => getOrderedThemes(), []);
const themeState = useTheme(); const themeState = useTheme();
const nonHighContrast = findNonHighContrastTheme(themeState.theme); const nonHighContrast = findNonHighContrastTheme(themeState.theme);

View file

@ -71,6 +71,7 @@ export const SpaceAvatar: React.FC<Pick<IProps, "avatarUrl" | "avatarDisabled" |
<AccessibleButton <AccessibleButton
className="mx_SpaceBasicSettings_avatar" className="mx_SpaceBasicSettings_avatar"
onClick={() => avatarUploadRef.current?.click()} onClick={() => avatarUploadRef.current?.click()}
alt=""
/> />
<AccessibleButton <AccessibleButton
onClick={() => avatarUploadRef.current?.click()} onClick={() => avatarUploadRef.current?.click()}

View file

@ -221,7 +221,7 @@ const CreateSpaceButton: React.FC<Pick<IInnerSpacePanelProps, "isPanelCollapsed"
isPanelCollapsed, isPanelCollapsed,
setPanelCollapsed, setPanelCollapsed,
}) => { }) => {
const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu<HTMLDivElement>(); const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu<HTMLElement>();
useEffect(() => { useEffect(() => {
if (!isPanelCollapsed && menuDisplayed) { if (!isPanelCollapsed && menuDisplayed) {

View file

@ -30,7 +30,7 @@ import defaultDispatcher from "../../../dispatcher/dispatcher";
import { Action } from "../../../dispatcher/actions"; import { Action } from "../../../dispatcher/actions";
import { ContextMenuTooltipButton } from "../../../accessibility/context_menu/ContextMenuTooltipButton"; import { ContextMenuTooltipButton } from "../../../accessibility/context_menu/ContextMenuTooltipButton";
import { toRightOf, useContextMenu } from "../../structures/ContextMenu"; import { toRightOf, useContextMenu } from "../../structures/ContextMenu";
import AccessibleButton, { ButtonEvent, ButtonProps as AccessibleButtonProps } from "../elements/AccessibleButton"; import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton";
import { StaticNotificationState } from "../../../stores/notifications/StaticNotificationState"; import { StaticNotificationState } from "../../../stores/notifications/StaticNotificationState";
import { NotificationLevel } from "../../../stores/notifications/NotificationLevel"; import { NotificationLevel } from "../../../stores/notifications/NotificationLevel";
import { getKeyBindingsManager } from "../../../KeyBindingsManager"; import { getKeyBindingsManager } from "../../../KeyBindingsManager";
@ -39,8 +39,8 @@ import SpaceContextMenu from "../context_menus/SpaceContextMenu";
import { useRovingTabIndex } from "../../../accessibility/RovingTabIndex"; import { useRovingTabIndex } from "../../../accessibility/RovingTabIndex";
import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts"; import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts";
type ButtonProps<T extends keyof HTMLElementTagNameMap> = Omit< type ButtonProps<T extends keyof JSX.IntrinsicElements> = Omit<
AccessibleButtonProps<T>, ComponentProps<typeof AccessibleButton<T>>,
"title" | "onClick" | "size" | "element" "title" | "onClick" | "size" | "element"
> & { > & {
space?: Room; space?: Room;
@ -52,12 +52,12 @@ type ButtonProps<T extends keyof HTMLElementTagNameMap> = Omit<
notificationState?: NotificationState; notificationState?: NotificationState;
isNarrow?: boolean; isNarrow?: boolean;
size: string; size: string;
innerRef?: RefObject<HTMLDivElement>; innerRef?: RefObject<HTMLElement>;
ContextMenuComponent?: ComponentType<ComponentProps<typeof SpaceContextMenu>>; ContextMenuComponent?: ComponentType<ComponentProps<typeof SpaceContextMenu>>;
onClick?(ev?: ButtonEvent): void; onClick?(ev?: ButtonEvent): void;
}; };
export const SpaceButton = <T extends keyof HTMLElementTagNameMap>({ export const SpaceButton = <T extends keyof JSX.IntrinsicElements>({
space, space,
spaceKey: _spaceKey, spaceKey: _spaceKey,
className, className,
@ -72,8 +72,8 @@ export const SpaceButton = <T extends keyof HTMLElementTagNameMap>({
ContextMenuComponent, ContextMenuComponent,
...props ...props
}: ButtonProps<T>): JSX.Element => { }: ButtonProps<T>): JSX.Element => {
const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu<HTMLDivElement>(innerRef); const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu<HTMLElement>(innerRef);
const [onFocus, isActive, ref] = useRovingTabIndex<HTMLDivElement>(handle); const [onFocus, isActive, ref] = useRovingTabIndex(handle);
const tabIndex = isActive ? 0 : -1; const tabIndex = isActive ? 0 : -1;
const spaceKey = _spaceKey ?? space?.roomId; const spaceKey = _spaceKey ?? space?.roomId;

View file

@ -6,7 +6,7 @@
* Please see LICENSE files in the repository root for full details. * Please see LICENSE files in the repository root for full details.
*/ */
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useMemo, useState } from "react";
import { ClientEvent, MatrixClient, MatrixEventEvent, Room } from "matrix-js-sdk/src/matrix"; import { ClientEvent, MatrixClient, MatrixEventEvent, Room } from "matrix-js-sdk/src/matrix";
import { throttle } from "lodash"; import { throttle } from "lodash";
@ -42,10 +42,8 @@ export function useUnreadThreadRooms(forceComputation: boolean): Result {
setResult(computeUnreadThreadRooms(mxClient, msc3946ProcessDynamicPredecessor, settingTACOnlyNotifs)); setResult(computeUnreadThreadRooms(mxClient, msc3946ProcessDynamicPredecessor, settingTACOnlyNotifs));
}, [mxClient, msc3946ProcessDynamicPredecessor, settingTACOnlyNotifs]); }, [mxClient, msc3946ProcessDynamicPredecessor, settingTACOnlyNotifs]);
// The exhautive deps lint rule can't compute dependencies here since it's not a plain inline func. const scheduleUpdate = useMemo(
// We make this as simple as possible so its only dep is doUpdate itself. () =>
// eslint-disable-next-line react-hooks/exhaustive-deps
const scheduleUpdate = useCallback(
throttle(doUpdate, MIN_UPDATE_INTERVAL_MS, { throttle(doUpdate, MIN_UPDATE_INTERVAL_MS, {
leading: false, leading: false,
trailing: true, trailing: true,

View file

@ -69,7 +69,7 @@ interface IDropdownButtonProps extends ButtonProps {
} }
const LegacyCallViewDropdownButton: React.FC<IDropdownButtonProps> = ({ state, deviceKinds, ...props }) => { const LegacyCallViewDropdownButton: React.FC<IDropdownButtonProps> = ({ state, deviceKinds, ...props }) => {
const [menuDisplayed, buttonRef, openMenu, closeMenu] = useContextMenu<HTMLDivElement>(); const [menuDisplayed, buttonRef, openMenu, closeMenu] = useContextMenu();
const [hoveringDropdown, setHoveringDropdown] = useState(false); const [hoveringDropdown, setHoveringDropdown] = useState(false);
const classes = classNames("mx_LegacyCallViewButtons_button", "mx_LegacyCallViewButtons_dropdownButton", { const classes = classNames("mx_LegacyCallViewButtons_button", "mx_LegacyCallViewButtons_dropdownButton", {

View file

@ -6,7 +6,7 @@ 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 { ReactNode, createContext, useCallback, useContext, useEffect, useRef, useState } from "react"; import { ReactNode, createContext, useCallback, useContext, useEffect, useState, useMemo } from "react";
/** /**
* A ToastContext helps components display any kind of toast message and can be provided * A ToastContext helps components display any kind of toast message and can be provided
@ -33,19 +33,19 @@ export function useToastContext(): ToastRack {
* the ToastRack object that should be provided to the context * the ToastRack object that should be provided to the context
*/ */
export function useActiveToast(): [ReactNode | undefined, ToastRack] { export function useActiveToast(): [ReactNode | undefined, ToastRack] {
const toastRack = useRef(new ToastRack()); const toastRack = useMemo(() => new ToastRack(), []);
const [activeToast, setActiveToast] = useState<ReactNode | undefined>(toastRack.current.getActiveToast()); const [activeToast, setActiveToast] = useState<ReactNode | undefined>(toastRack.getActiveToast());
const updateCallback = useCallback(() => { const updateCallback = useCallback(() => {
setActiveToast(toastRack.current.getActiveToast()); setActiveToast(toastRack.getActiveToast());
}, [setActiveToast, toastRack]); }, [setActiveToast, toastRack]);
useEffect(() => { useEffect(() => {
toastRack.current.setCallback(updateCallback); toastRack.setCallback(updateCallback);
}, [toastRack, updateCallback]); }, [toastRack, updateCallback]);
return [activeToast, toastRack.current]; return [activeToast, toastRack];
} }
interface DisplayedToast { interface DisplayedToast {

View file

@ -2936,7 +2936,6 @@
"warning": "<w>WARNUNG:</w> <description/>" "warning": "<w>WARNUNG:</w> <description/>"
}, },
"share": { "share": {
"link_copied": "Link kopiert",
"permalink_message": "Link zur ausgewählten Nachricht", "permalink_message": "Link zur ausgewählten Nachricht",
"permalink_most_recent": "Link zur aktuellsten Nachricht", "permalink_most_recent": "Link zur aktuellsten Nachricht",
"share_call": "Konferenzeinladungslink", "share_call": "Konferenzeinladungslink",

View file

@ -398,7 +398,7 @@
}, },
"bug_reporting": { "bug_reporting": {
"additional_context": "If there is additional context that would help in analysing the issue, such as what you were doing at the time, room IDs, user IDs, etc., please include those things here.", "additional_context": "If there is additional context that would help in analysing the issue, such as what you were doing at the time, room IDs, user IDs, etc., please include those things here.",
"before_submitting": "We recommend <a>creating a GitHub issue</a> to ensure that your report is reviewed.", "before_submitting": "Before submitting logs, you must <a>create a GitHub issue</a> to describe your problem.",
"collecting_information": "Collecting app version information", "collecting_information": "Collecting app version information",
"collecting_logs": "Collecting logs", "collecting_logs": "Collecting logs",
"create_new_issue": "Please <newIssueLink>create a new issue</newIssueLink> on GitHub so that we can investigate this bug.", "create_new_issue": "Please <newIssueLink>create a new issue</newIssueLink> on GitHub so that we can investigate this bug.",

View file

@ -340,7 +340,7 @@
"set_email_prompt": "Czy chcesz ustawić adres e-mail?", "set_email_prompt": "Czy chcesz ustawić adres e-mail?",
"sign_in_description": "Użyj swojego konta, aby kontynuować.", "sign_in_description": "Użyj swojego konta, aby kontynuować.",
"sign_in_instead": "Zamiast tego zaloguj się", "sign_in_instead": "Zamiast tego zaloguj się",
"sign_in_instead_prompt": "Masz już konto? <a>Zaloguj się tutaj</a>", "sign_in_instead_prompt": "Zamiast tego zaloguj się",
"sign_in_or_register": "Zaloguj się lub utwórz konto", "sign_in_or_register": "Zaloguj się lub utwórz konto",
"sign_in_or_register_description": "Użyj konta lub utwórz nowe, aby kontynuować.", "sign_in_or_register_description": "Użyj konta lub utwórz nowe, aby kontynuować.",
"sign_in_prompt": "Posiadasz już konto? <a>Zaloguj się</a>", "sign_in_prompt": "Posiadasz już konto? <a>Zaloguj się</a>",
@ -505,7 +505,6 @@
"matrix": "Matrix", "matrix": "Matrix",
"message": "Wiadomość", "message": "Wiadomość",
"message_layout": "Wygląd wiadomości", "message_layout": "Wygląd wiadomości",
"message_timestamp_invalid": "Nieprawidłowy znacznik czasu",
"microphone": "Mikrofon", "microphone": "Mikrofon",
"model": "Model", "model": "Model",
"modern": "Współczesny", "modern": "Współczesny",
@ -909,8 +908,6 @@
"warning": "Jeżeli nie ustawiłeś nowej metody odzyskiwania, atakujący może uzyskać dostęp do Twojego konta. Zmień hasło konta i natychmiast ustaw nową metodę odzyskiwania w Ustawieniach." "warning": "Jeżeli nie ustawiłeś nowej metody odzyskiwania, atakujący może uzyskać dostęp do Twojego konta. Zmień hasło konta i natychmiast ustaw nową metodę odzyskiwania w Ustawieniach."
}, },
"not_supported": "<niewspierany>", "not_supported": "<niewspierany>",
"pinned_identity_changed": "Tożsamość użytkownika %(displayName)s (<b>%(userId)s</b>) uległa zmianie. <a>Dowiedz się więcej</a>",
"pinned_identity_changed_no_displayname": "Tożsamość użytkownika <b>%(userId)s</b> uległa zmianie <a>Dowiedz się więcej</a>",
"recovery_method_removed": { "recovery_method_removed": {
"description_1": "Ta sesja wykryła, że Twoja fraza bezpieczeństwa i klucz dla bezpiecznych wiadomości zostały usunięte.", "description_1": "Ta sesja wykryła, że Twoja fraza bezpieczeństwa i klucz dla bezpiecznych wiadomości zostały usunięte.",
"description_2": "Jeśli zrobiłeś to przez pomyłkę, możesz ustawić bezpieczne wiadomości w tej sesji, co zaszyfruje ponownie historię wiadomości za pomocą nowej metody odzyskiwania.", "description_2": "Jeśli zrobiłeś to przez pomyłkę, możesz ustawić bezpieczne wiadomości w tej sesji, co zaszyfruje ponownie historię wiadomości za pomocą nowej metody odzyskiwania.",
@ -999,7 +996,7 @@
"unverified_sessions_toast_description": "Sprawdź, by upewnić się że Twoje konto jest bezpieczne", "unverified_sessions_toast_description": "Sprawdź, by upewnić się że Twoje konto jest bezpieczne",
"unverified_sessions_toast_reject": "Później", "unverified_sessions_toast_reject": "Później",
"unverified_sessions_toast_title": "Masz niezweryfikowane sesje", "unverified_sessions_toast_title": "Masz niezweryfikowane sesje",
"verification_description": "Zweryfikuj swoją tożsamość, aby uzyskać dostęp do wiadomości szyfrowanych i potwierdzić swoją tożsamość innym. Jeśli korzystasz z urządzenia mobilnego, otwórz na niej aplikację.", "verification_description": "Zweryfikuj swoją tożsamość, aby uzyskać dostęp do wiadomości szyfrowanych i potwierdzić swoją tożsamość innym.",
"verification_dialog_title_device": "Zweryfikuj drugie urządzenie", "verification_dialog_title_device": "Zweryfikuj drugie urządzenie",
"verification_dialog_title_user": "Żądanie weryfikacji", "verification_dialog_title_user": "Żądanie weryfikacji",
"verification_skip_warning": "Bez weryfikacji, nie będziesz posiadać dostępu do wszystkich swoich wiadomości, a inni będą Cię widzieć jako niezaufanego.", "verification_skip_warning": "Bez weryfikacji, nie będziesz posiadać dostępu do wszystkich swoich wiadomości, a inni będą Cię widzieć jako niezaufanego.",
@ -1105,15 +1102,7 @@
"you": "Dodano reakcję %(reaction)s do %(message)s" "you": "Dodano reakcję %(reaction)s do %(message)s"
}, },
"m.sticker": "%(senderName)s: %(stickerName)s", "m.sticker": "%(senderName)s: %(stickerName)s",
"m.text": "%(senderName)s: %(message)s", "m.text": "%(senderName)s: %(message)s"
"prefix": {
"audio": "Audio",
"file": "Plik",
"image": "Obraz",
"poll": "Ankieta",
"video": "Wideo"
},
"preview": "<bold>%(prefix)s:</bold> %(preview)s"
}, },
"export_chat": { "export_chat": {
"cancelled": "Eksport został anulowany", "cancelled": "Eksport został anulowany",
@ -2950,7 +2939,6 @@
"warning": "<w>OSTRZEŻENIE:</w> <description/>" "warning": "<w>OSTRZEŻENIE:</w> <description/>"
}, },
"share": { "share": {
"link_copied": "Skopiowano link",
"permalink_message": "Link do zaznaczonej wiadomości", "permalink_message": "Link do zaznaczonej wiadomości",
"permalink_most_recent": "Link do najnowszej wiadomości", "permalink_most_recent": "Link do najnowszej wiadomości",
"share_call": "Link zaproszenia do konferencji", "share_call": "Link zaproszenia do konferencji",
@ -3259,8 +3247,8 @@
"historical_event_no_key_backup": "Historia wiadomości nie jest dostępna na tym urządzeniu", "historical_event_no_key_backup": "Historia wiadomości nie jest dostępna na tym urządzeniu",
"historical_event_unverified_device": "Musisz zweryfikować to urządzenie, aby wyświetlić historię wiadomości", "historical_event_unverified_device": "Musisz zweryfikować to urządzenie, aby wyświetlić historię wiadomości",
"historical_event_user_not_joined": "Nie masz dostępu do tej wiadomości", "historical_event_user_not_joined": "Nie masz dostępu do tej wiadomości",
"sender_identity_previously_verified": "Zweryfikowana tożsamość nadawcy uległa zmianie", "sender_identity_previously_verified": "Zweryfikowana tożsamość uległa zmianie",
"sender_unsigned_device": "Wysłano z niezabezpieczonego urządzenia.", "sender_unsigned_device": "Zaszyfrowano przez urządzenie niezweryfikowane przez właściciela.",
"unable_to_decrypt": "Nie można rozszyfrować wiadomości" "unable_to_decrypt": "Nie można rozszyfrować wiadomości"
}, },
"disambiguated_profile": "%(displayName)s (%(matrixId)s)", "disambiguated_profile": "%(displayName)s (%(matrixId)s)",
@ -3732,7 +3720,6 @@
"error_files_too_large": "Te pliki są <b>zbyt duże</b> do wysłania. Ograniczenie wielkości plików to %(limit)s.", "error_files_too_large": "Te pliki są <b>zbyt duże</b> do wysłania. Ograniczenie wielkości plików to %(limit)s.",
"error_some_files_too_large": "Niektóre pliki są <b>zbyt duże</b> do wysłania. Ograniczenie wielkości plików to %(limit)s.", "error_some_files_too_large": "Niektóre pliki są <b>zbyt duże</b> do wysłania. Ograniczenie wielkości plików to %(limit)s.",
"error_title": "Błąd wysyłania", "error_title": "Błąd wysyłania",
"not_image": "Wybrany plik nie jest prawidłowym plikiem obrazu.",
"title": "Prześlij pliki", "title": "Prześlij pliki",
"title_progress": "Prześlij pliki (%(current)s z %(total)s)", "title_progress": "Prześlij pliki (%(current)s z %(total)s)",
"upload_all_button": "Prześlij wszystko", "upload_all_button": "Prześlij wszystko",

View file

@ -1,140 +0,0 @@
/*
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,11 +33,6 @@ 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

@ -194,7 +194,6 @@ export class StopGapWidgetDriver extends WidgetDriver {
EventType.CallSDPStreamMetadataChanged, EventType.CallSDPStreamMetadataChanged,
EventType.CallSDPStreamMetadataChangedPrefix, EventType.CallSDPStreamMetadataChangedPrefix,
EventType.CallReplaces, EventType.CallReplaces,
EventType.CallEncryptionKeysPrefix,
]; ];
for (const eventType of sendRecvToDevice) { for (const eventType of sendRecvToDevice) {
this.allowedCapabilities.add( this.allowedCapabilities.add(

View file

@ -6,7 +6,7 @@ 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 { useEffect, useState } from "react"; import { useEffect, useMemo } from "react";
import type { Map as MapLibreMap } from "maplibre-gl"; import type { Map as MapLibreMap } from "maplibre-gl";
import { createMap } from "./map"; import { createMap } from "./map";
@ -26,29 +26,25 @@ interface UseMapProps {
*/ */
export const useMap = ({ interactive, bodyId, onError }: UseMapProps): MapLibreMap | undefined => { export const useMap = ({ interactive, bodyId, onError }: UseMapProps): MapLibreMap | undefined => {
const cli = useMatrixClientContext(); const cli = useMatrixClientContext();
const [map, setMap] = useState<MapLibreMap>();
useEffect( const map = useMemo(() => {
() => {
try { try {
setMap(createMap(cli, !!interactive, bodyId, onError)); return createMap(cli, !!interactive, bodyId, onError);
} catch (error) { } catch (error) {
console.error("Error encountered in useMap", error); console.error("Error encountered in useMap", error);
if (error instanceof Error) { if (error instanceof Error) {
onError?.(error); onError?.(error);
} }
} }
}, [bodyId, cli, interactive, onError]);
// cleanup
useEffect(() => {
if (!map) return;
return () => { return () => {
if (map) {
map.remove(); map.remove();
setMap(undefined);
}
}; };
}, }, [map]);
// map is excluded as a dependency
// eslint-disable-next-line react-hooks/exhaustive-deps
[interactive, bodyId, onError],
);
return map; return map;
}; };

View file

@ -0,0 +1,131 @@
/*
Copyright 2024 New Vector Ltd.
Copyright 2018-2022 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React from "react";
import { render, screen, waitFor } from "jest-matrix-react";
import { mocked } from "jest-mock";
import { MatrixClient } from "matrix-js-sdk/src/matrix";
import { createCrossSigning } from "../../../../../src/CreateCrossSigning";
import CreateCrossSigningDialog from "../../../../../src/components/views/dialogs/security/CreateCrossSigningDialog";
import { createTestClient } from "../../../../test-utils";
jest.mock("../../../../../src/CreateCrossSigning", () => ({
createCrossSigning: jest.fn(),
}));
describe("CreateCrossSigningDialog", () => {
let client: MatrixClient;
let createCrossSigningResolve: () => void;
let createCrossSigningReject: (e: Error) => void;
beforeEach(() => {
client = createTestClient();
mocked(createCrossSigning).mockImplementation(() => {
return new Promise((resolve, reject) => {
createCrossSigningResolve = resolve;
createCrossSigningReject = reject;
});
});
});
afterEach(() => {
jest.resetAllMocks();
jest.restoreAllMocks();
});
it("should call createCrossSigning and show a spinner while it runs", async () => {
const onFinished = jest.fn();
render(
<CreateCrossSigningDialog
matrixClient={client}
accountPassword="hunter2"
tokenLogin={false}
onFinished={onFinished}
/>,
);
expect(createCrossSigning).toHaveBeenCalledWith(client, false, "hunter2");
expect(screen.getByTestId("spinner")).toBeInTheDocument();
createCrossSigningResolve!();
await waitFor(() => expect(onFinished).toHaveBeenCalledWith(true));
});
it("should display an error if createCrossSigning fails", async () => {
render(
<CreateCrossSigningDialog
matrixClient={client}
accountPassword="hunter2"
tokenLogin={false}
onFinished={jest.fn()}
/>,
);
createCrossSigningReject!(new Error("generic error message"));
await expect(await screen.findByRole("button", { name: "Retry" })).toBeInTheDocument();
});
it("ignores failures when tokenLogin is true", async () => {
const onFinished = jest.fn();
render(
<CreateCrossSigningDialog
matrixClient={client}
accountPassword="hunter2"
tokenLogin={true}
onFinished={onFinished}
/>,
);
createCrossSigningReject!(new Error("generic error message"));
await waitFor(() => expect(onFinished).toHaveBeenCalledWith(false));
});
it("cancels the dialog when the cancel button is clicked", async () => {
const onFinished = jest.fn();
render(
<CreateCrossSigningDialog
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(
<CreateCrossSigningDialog
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

@ -1,61 +0,0 @@
/*
Copyright 2024 New Vector Ltd.
Copyright 2018-2022 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React from "react";
import { render, screen } from "jest-matrix-react";
import userEvent from "@testing-library/user-event";
import { InitialCryptoSetupDialog } from "../../../../../src/components/views/dialogs/security/InitialCryptoSetupDialog";
import { InitialCryptoSetupStore } from "../../../../../src/stores/InitialCryptoSetupStore";
describe("InitialCryptoSetupDialog", () => {
const storeMock = {
getStatus: jest.fn(),
retry: jest.fn(),
on: jest.fn(),
off: jest.fn(),
};
beforeEach(() => {
jest.spyOn(InitialCryptoSetupStore, "sharedInstance").mockReturnValue(storeMock as any);
});
afterEach(() => {
jest.resetAllMocks();
jest.restoreAllMocks();
});
it("should show a spinner while the setup is in progress", async () => {
const onFinished = jest.fn();
storeMock.getStatus.mockReturnValue("in_progress");
render(<InitialCryptoSetupDialog onFinished={onFinished} />);
expect(screen.getByTestId("spinner")).toBeInTheDocument();
});
it("should display an error if setup has failed", async () => {
storeMock.getStatus.mockReturnValue("error");
render(<InitialCryptoSetupDialog onFinished={jest.fn()} />);
await expect(await screen.findByRole("button", { name: "Retry" })).toBeInTheDocument();
});
it("calls retry when retry button pressed", async () => {
const onFinished = jest.fn();
storeMock.getStatus.mockReturnValue("error");
render(<InitialCryptoSetupDialog onFinished={onFinished} />);
await userEvent.click(await screen.findByRole("button", { name: "Retry" }));
expect(storeMock.retry).toHaveBeenCalled();
});
});

View file

@ -314,6 +314,7 @@ exports[`<MatrixChat /> with a soft-logged-out session should show the soft-logo
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary" class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
role="button" role="button"
tabindex="0" tabindex="0"
type="submit"
> >
Sign in Sign in
</div> </div>

View file

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

View file

@ -20,7 +20,6 @@ import RightPanelStore from "../../../../../src/stores/right-panel/RightPanelSto
import { RightPanelPhases } from "../../../../../src/stores/right-panel/RightPanelStorePhases"; import { RightPanelPhases } from "../../../../../src/stores/right-panel/RightPanelStorePhases";
import { UPDATE_EVENT } from "../../../../../src/stores/AsyncStore"; import { UPDATE_EVENT } from "../../../../../src/stores/AsyncStore";
import { Action } from "../../../../../src/dispatcher/actions"; import { Action } from "../../../../../src/dispatcher/actions";
import ResizeNotifier from "../../../../../src/utils/ResizeNotifier.ts";
describe("<PinnedMessageBanner />", () => { describe("<PinnedMessageBanner />", () => {
const userId = "@alice:server.org"; const userId = "@alice:server.org";
@ -29,12 +28,10 @@ describe("<PinnedMessageBanner />", () => {
let mockClient: MatrixClient; let mockClient: MatrixClient;
let room: Room; let room: Room;
let permalinkCreator: RoomPermalinkCreator; let permalinkCreator: RoomPermalinkCreator;
let resizeNotifier: ResizeNotifier;
beforeEach(() => { beforeEach(() => {
mockClient = stubClient(); mockClient = stubClient();
room = new Room(roomId, mockClient, userId); room = new Room(roomId, mockClient, userId);
permalinkCreator = new RoomPermalinkCreator(room); permalinkCreator = new RoomPermalinkCreator(room);
resizeNotifier = new ResizeNotifier();
jest.spyOn(dis, "dispatch").mockReturnValue(undefined); jest.spyOn(dis, "dispatch").mockReturnValue(undefined);
}); });
@ -80,7 +77,7 @@ describe("<PinnedMessageBanner />", () => {
*/ */
function renderBanner() { function renderBanner() {
return render( return render(
<PinnedMessageBanner permalinkCreator={permalinkCreator} room={room} resizeNotifier={resizeNotifier} />, <PinnedMessageBanner permalinkCreator={permalinkCreator} room={room} />,
withClientContextRenderOptions(mockClient), withClientContextRenderOptions(mockClient),
); );
} }
@ -148,9 +145,7 @@ describe("<PinnedMessageBanner />", () => {
event3.getId()!, event3.getId()!,
]); ]);
jest.spyOn(pinnedEventHooks, "useSortedFetchedPinnedEvents").mockReturnValue([event1, event2, event3]); jest.spyOn(pinnedEventHooks, "useSortedFetchedPinnedEvents").mockReturnValue([event1, event2, event3]);
rerender( rerender(<PinnedMessageBanner permalinkCreator={permalinkCreator} room={room} />);
<PinnedMessageBanner permalinkCreator={permalinkCreator} room={room} resizeNotifier={resizeNotifier} />,
);
await expect(screen.findByText("Third pinned message")).resolves.toBeVisible(); await expect(screen.findByText("Third pinned message")).resolves.toBeVisible();
expect(asFragment()).toMatchSnapshot(); expect(asFragment()).toMatchSnapshot();
}); });
@ -211,42 +206,6 @@ describe("<PinnedMessageBanner />", () => {
expect(asFragment()).toMatchSnapshot(); expect(asFragment()).toMatchSnapshot();
}); });
describe("Notify the timeline to resize", () => {
beforeEach(() => {
jest.spyOn(resizeNotifier, "notifyTimelineHeightChanged");
jest.spyOn(pinnedEventHooks, "usePinnedEvents").mockReturnValue([event1.getId()!, event2.getId()!]);
jest.spyOn(pinnedEventHooks, "useSortedFetchedPinnedEvents").mockReturnValue([event1, event2]);
});
it("should notify the timeline to resize when we display the banner", async () => {
renderBanner();
await expect(screen.findByText("Second pinned message")).resolves.toBeVisible();
// The banner is displayed, so we need to resize the timeline
expect(resizeNotifier.notifyTimelineHeightChanged).toHaveBeenCalledTimes(1);
await userEvent.click(screen.getByRole("button", { name: "View the pinned message in the timeline." }));
await expect(screen.findByText("First pinned message")).resolves.toBeVisible();
// The banner is already displayed, so we don't need to resize the timeline
expect(resizeNotifier.notifyTimelineHeightChanged).toHaveBeenCalledTimes(1);
});
it("should notify the timeline to resize when we hide the banner", async () => {
const { rerender } = renderBanner();
await expect(screen.findByText("Second pinned message")).resolves.toBeVisible();
// The banner is displayed, so we need to resize the timeline
expect(resizeNotifier.notifyTimelineHeightChanged).toHaveBeenCalledTimes(1);
// The banner has no event to display and is hidden
jest.spyOn(pinnedEventHooks, "usePinnedEvents").mockReturnValue([]);
jest.spyOn(pinnedEventHooks, "useSortedFetchedPinnedEvents").mockReturnValue([]);
rerender(
<PinnedMessageBanner permalinkCreator={permalinkCreator} room={room} resizeNotifier={resizeNotifier} />,
);
// The timeline should be resized
expect(resizeNotifier.notifyTimelineHeightChanged).toHaveBeenCalledTimes(2);
});
});
describe("Right button", () => { describe("Right button", () => {
beforeEach(() => { beforeEach(() => {
jest.spyOn(pinnedEventHooks, "usePinnedEvents").mockReturnValue([event1.getId()!, event2.getId()!]); jest.spyOn(pinnedEventHooks, "usePinnedEvents").mockReturnValue([event1.getId()!, event2.getId()!]);
@ -258,8 +217,6 @@ describe("<PinnedMessageBanner />", () => {
jest.spyOn(RightPanelStore.instance, "isOpenForRoom").mockReturnValue(false); jest.spyOn(RightPanelStore.instance, "isOpenForRoom").mockReturnValue(false);
renderBanner(); renderBanner();
await expect(screen.findByText("Second pinned message")).resolves.toBeVisible();
expect(screen.getByRole("button", { name: "View all" })).toBeVisible(); expect(screen.getByRole("button", { name: "View all" })).toBeVisible();
}); });
@ -271,8 +228,6 @@ describe("<PinnedMessageBanner />", () => {
}); });
renderBanner(); renderBanner();
await expect(screen.findByText("Second pinned message")).resolves.toBeVisible();
expect(screen.getByRole("button", { name: "View all" })).toBeVisible(); expect(screen.getByRole("button", { name: "View all" })).toBeVisible();
}); });
@ -284,8 +239,6 @@ describe("<PinnedMessageBanner />", () => {
}); });
renderBanner(); renderBanner();
await expect(screen.findByText("Second pinned message")).resolves.toBeVisible();
expect(screen.getByRole("button", { name: "Close list" })).toBeVisible(); expect(screen.getByRole("button", { name: "Close list" })).toBeVisible();
}); });
@ -310,7 +263,6 @@ describe("<PinnedMessageBanner />", () => {
}); });
renderBanner(); renderBanner();
await expect(screen.findByText("Second pinned message")).resolves.toBeVisible();
expect(screen.getByRole("button", { name: "Close list" })).toBeVisible(); expect(screen.getByRole("button", { name: "Close list" })).toBeVisible();
jest.spyOn(RightPanelStore.instance, "isOpenForRoom").mockReturnValue(false); jest.spyOn(RightPanelStore.instance, "isOpenForRoom").mockReturnValue(false);

View file

@ -10,7 +10,6 @@ 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,
@ -21,9 +20,6 @@ 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", () => {
@ -91,10 +87,6 @@ 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"
> >
==MOCK FORMATTED DATE== Wed, 15 May, 0:00
</p> </p>
</div> </div>
</div> </div>

View file

@ -35,7 +35,7 @@ describe("SetIntegrationManager", () => {
deleteThreePid: jest.fn(), deleteThreePid: jest.fn(),
}); });
let stores!: SdkContextClass; let stores: SdkContextClass;
const getComponent = () => ( const getComponent = () => (
<MatrixClientContext.Provider value={mockClient}> <MatrixClientContext.Provider value={mockClient}>

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_ssths_24 mx_LayoutSwitcher_LayoutSelector" class="_root_dgy0u_24 mx_LayoutSwitcher_LayoutSelector"
> >
<div <div
class="_field_ssths_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio" class="_field_dgy0u_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio"
> >
<label <label
aria-label="Modern" aria-label="Modern"
class="_label_ssths_67" class="_label_dgy0u_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_ssths_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio" class="_field_dgy0u_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio"
> >
<label <label
aria-label="Message bubbles" aria-label="Message bubbles"
class="_label_ssths_67" class="_label_dgy0u_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_ssths_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio" class="_field_dgy0u_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio"
> >
<label <label
aria-label="IRC (experimental)" aria-label="IRC (experimental)"
class="_label_ssths_67" class="_label_dgy0u_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_ssths_24" class="_root_dgy0u_24"
> >
<div <div
class="_inline-field_ssths_40" class="_inline-field_dgy0u_40"
> >
<div <div
class="_inline-field-control_ssths_52" class="_inline-field-control_dgy0u_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_ssths_46" class="_inline-field-body_dgy0u_46"
> >
<label <label
class="_label_ssths_67" class="_label_dgy0u_67"
for="radix-:rr:" for="radix-:rr:"
> >
Show compact text and messages Show compact text and messages
</label> </label>
<span <span
class="_message_ssths_93 _help-message_ssths_99" class="_message_dgy0u_98 _help-message_dgy0u_104"
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_ssths_24" class="_root_dgy0u_24"
> >
<div <div
class="_inline-field_ssths_40" class="_inline-field_dgy0u_40"
> >
<div <div
class="_inline-field-control_ssths_52" class="_inline-field-control_dgy0u_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_ssths_46" class="_inline-field-body_dgy0u_46"
> >
<label <label
class="_label_ssths_67" class="_label_dgy0u_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_ssths_24 mx_ThemeChoicePanel_ThemeSelectors" class="_root_dgy0u_24 mx_ThemeChoicePanel_ThemeSelectors"
> >
<div <div
class="_inline-field_ssths_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_enabled cpd-theme-light" class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_enabled cpd-theme-light"
> >
<div <div
class="_inline-field-control_ssths_52" class="_inline-field-control_dgy0u_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_ssths_46" class="_inline-field-body_dgy0u_46"
> >
<label <label
class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label" class="_label_dgy0u_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_ssths_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark" class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark"
> >
<div <div
class="_inline-field-control_ssths_52" class="_inline-field-control_dgy0u_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_ssths_46" class="_inline-field-body_dgy0u_46"
> >
<label <label
class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label" class="_label_dgy0u_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_ssths_40 mx_ThemeChoicePanel_themeSelector cpd-theme-light" class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector cpd-theme-light"
> >
<div <div
class="_inline-field-control_ssths_52" class="_inline-field-control_dgy0u_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_ssths_46" class="_inline-field-body_dgy0u_46"
> >
<label <label
class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label" class="_label_dgy0u_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_ssths_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark" class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark"
> >
<div <div
class="_inline-field-control_ssths_52" class="_inline-field-control_dgy0u_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_ssths_46" class="_inline-field-body_dgy0u_46"
> >
<label <label
class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label" class="_label_dgy0u_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_ssths_24 mx_ThemeChoicePanel_CustomTheme_EditInPlace" class="_root_dgy0u_24 mx_ThemeChoicePanel_CustomTheme_EditInPlace"
> >
<div <div
class="_field_ssths_34" class="_field_dgy0u_34"
> >
<label <label
class="_label_ssths_67" class="_label_dgy0u_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_ssths_93 _help-message_ssths_99" class="_message_dgy0u_98 _help-message_dgy0u_104"
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_ssths_24" class="_root_dgy0u_24"
> >
<div <div
class="_inline-field_ssths_40" class="_inline-field_dgy0u_40"
> >
<div <div
class="_inline-field-control_ssths_52" class="_inline-field-control_dgy0u_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_ssths_46" class="_inline-field-body_dgy0u_46"
> >
<label <label
class="_label_ssths_67" class="_label_dgy0u_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_ssths_24 mx_ThemeChoicePanel_ThemeSelectors" class="_root_dgy0u_24 mx_ThemeChoicePanel_ThemeSelectors"
> >
<div <div
class="_inline-field_ssths_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_enabled cpd-theme-light" class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_enabled cpd-theme-light"
> >
<div <div
class="_inline-field-control_ssths_52" class="_inline-field-control_dgy0u_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_ssths_46" class="_inline-field-body_dgy0u_46"
> >
<label <label
class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label" class="_label_dgy0u_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_ssths_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark" class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark"
> >
<div <div
class="_inline-field-control_ssths_52" class="_inline-field-control_dgy0u_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_ssths_46" class="_inline-field-body_dgy0u_46"
> >
<label <label
class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label" class="_label_dgy0u_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_ssths_40 mx_ThemeChoicePanel_themeSelector cpd-theme-light" class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector cpd-theme-light"
> >
<div <div
class="_inline-field-control_ssths_52" class="_inline-field-control_dgy0u_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_ssths_46" class="_inline-field-body_dgy0u_46"
> >
<label <label
class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label" class="_label_dgy0u_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_ssths_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark" class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark"
> >
<div <div
class="_inline-field-control_ssths_52" class="_inline-field-control_dgy0u_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_ssths_46" class="_inline-field-body_dgy0u_46"
> >
<label <label
class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label" class="_label_dgy0u_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_ssths_24 mx_ThemeChoicePanel_CustomTheme_EditInPlace" class="_root_dgy0u_24 mx_ThemeChoicePanel_CustomTheme_EditInPlace"
> >
<div <div
class="_field_ssths_34" class="_field_dgy0u_34"
> >
<label <label
class="_label_ssths_67" class="_label_dgy0u_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_ssths_93 _help-message_ssths_99" class="_message_dgy0u_98 _help-message_dgy0u_104"
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_ssths_24" class="_root_dgy0u_24"
> >
<div <div
class="_inline-field_ssths_40" class="_inline-field_dgy0u_40"
> >
<div <div
class="_inline-field-control_ssths_52" class="_inline-field-control_dgy0u_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_ssths_46" class="_inline-field-body_dgy0u_46"
> >
<label <label
class="_label_ssths_67" class="_label_dgy0u_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_ssths_24 mx_ThemeChoicePanel_ThemeSelectors" class="_root_dgy0u_24 mx_ThemeChoicePanel_ThemeSelectors"
> >
<div <div
class="_inline-field_ssths_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_enabled cpd-theme-light" class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_enabled cpd-theme-light"
> >
<div <div
class="_inline-field-control_ssths_52" class="_inline-field-control_dgy0u_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_ssths_46" class="_inline-field-body_dgy0u_46"
> >
<label <label
class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label" class="_label_dgy0u_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_ssths_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark" class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark"
> >
<div <div
class="_inline-field-control_ssths_52" class="_inline-field-control_dgy0u_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_ssths_46" class="_inline-field-body_dgy0u_46"
> >
<label <label
class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label" class="_label_dgy0u_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_ssths_40 mx_ThemeChoicePanel_themeSelector cpd-theme-light" class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector cpd-theme-light"
> >
<div <div
class="_inline-field-control_ssths_52" class="_inline-field-control_dgy0u_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_ssths_46" class="_inline-field-body_dgy0u_46"
> >
<label <label
class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label" class="_label_dgy0u_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_ssths_24 mx_ThemeChoicePanel_ThemeSelectors" class="_root_dgy0u_24 mx_ThemeChoicePanel_ThemeSelectors"
> >
<div <div
class="_inline-field_ssths_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_disabled cpd-theme-light" class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_disabled cpd-theme-light"
> >
<div <div
class="_inline-field-control_ssths_52" class="_inline-field-control_dgy0u_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_ssths_46" class="_inline-field-body_dgy0u_46"
> >
<label <label
class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label" class="_label_dgy0u_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_ssths_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_disabled cpd-theme-dark" class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_disabled cpd-theme-dark"
> >
<div <div
class="_inline-field-control_ssths_52" class="_inline-field-control_dgy0u_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_ssths_46" class="_inline-field-body_dgy0u_46"
> >
<label <label
class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label" class="_label_dgy0u_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_ssths_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_disabled cpd-theme-light" class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_disabled cpd-theme-light"
> >
<div <div
class="_inline-field-control_ssths_52" class="_inline-field-control_dgy0u_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_ssths_46" class="_inline-field-body_dgy0u_46"
> >
<label <label
class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label" class="_label_dgy0u_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_ssths_24 mx_LayoutSwitcher_LayoutSelector" class="_root_dgy0u_24 mx_LayoutSwitcher_LayoutSelector"
> >
<div <div
class="_field_ssths_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio" class="_field_dgy0u_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio"
> >
<label <label
aria-label="Modern" aria-label="Modern"
class="_label_ssths_67" class="_label_dgy0u_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_ssths_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio" class="_field_dgy0u_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio"
> >
<label <label
aria-label="Message bubbles" aria-label="Message bubbles"
class="_label_ssths_67" class="_label_dgy0u_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_ssths_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio" class="_field_dgy0u_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio"
> >
<label <label
aria-label="IRC (experimental)" aria-label="IRC (experimental)"
class="_label_ssths_67" class="_label_dgy0u_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_ssths_24" class="_root_dgy0u_24"
> >
<div <div
class="_inline-field_ssths_40" class="_inline-field_dgy0u_40"
> >
<div <div
class="_inline-field-control_ssths_52" class="_inline-field-control_dgy0u_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_ssths_46" class="_inline-field-body_dgy0u_46"
> >
<label <label
class="_label_ssths_67" class="_label_dgy0u_67"
for="radix-:ru:" for="radix-:ru:"
> >
Show compact text and messages Show compact text and messages
</label> </label>
<span <span
class="_message_ssths_93 _help-message_ssths_99" class="_message_dgy0u_98 _help-message_dgy0u_104"
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

@ -85,6 +85,7 @@ exports[`<MjolnirUserSettingsTab /> renders correctly when user has no ignored u
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary" class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
role="button" role="button"
tabindex="0" tabindex="0"
type="submit"
> >
Ignore Ignore
</div> </div>
@ -149,6 +150,7 @@ exports[`<MjolnirUserSettingsTab /> renders correctly when user has no ignored u
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary" class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
role="button" role="button"
tabindex="0" tabindex="0"
type="submit"
> >
Subscribe Subscribe
</div> </div>

View file

@ -8,7 +8,6 @@ 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"
@ -43,7 +42,6 @@ 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"
@ -238,7 +236,6 @@ 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"
@ -263,7 +260,6 @@ 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"
/> />
@ -276,7 +272,6 @@ 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
@ -291,7 +286,6 @@ 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,7 +477,9 @@ 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"
> >
@ -489,7 +491,6 @@ 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"
@ -514,7 +515,6 @@ 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,7 +527,6 @@ 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
@ -586,6 +585,7 @@ 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,6 +648,7 @@ 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

@ -1,85 +0,0 @@
/*
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,26 +178,22 @@ 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);
// We use en-US for these tests because there was a change in Node 22.12 which removed expect(formatDate(date, false, "en-GB")).toMatchInlineSnapshot(`"19:10"`);
// 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-US")).toMatchInlineSnapshot(`"Fri 19:10"`); expect(formatDate(date, false, "en-GB")).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-US")).toMatchInlineSnapshot(`"Mon, Sep 12, 19:10"`); expect(formatDate(date, false, "en-GB")).toMatchInlineSnapshot(`"Mon, 12 Sept, 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-US")).toMatchInlineSnapshot(`"Wed, Jan 20, 2021, 19:10"`); expect(formatDate(date, false, "en-GB")).toMatchInlineSnapshot(`"Wed, 20 Jan 2021, 19:10"`);
}); });
}); });

View file

@ -187,6 +187,18 @@ module.exports = (env, argv) => {
}, },
resolve: { resolve: {
// We define an alternative import path so we can safely use src/ across the react-sdk
// and js-sdk. We already import from src/ where possible to ensure our source maps are
// extremely accurate (and because we're capable of compiling the layers manually rather
// than relying on partially-mangled output from babel), though we do need to fix the
// package level import (stuff like `import {Thing} from "matrix-js-sdk"` for example).
// We can't use the aliasing down below to point at src/ because that'll fail to resolve
// the package.json for the dependency. Instead, we rely on the package.json of each
// layer to have our custom alternate fields to load things in the right order. These are
// the defaults of webpack prepended with `matrix_src_`.
mainFields: ["matrix_src_browser", "matrix_src_main", "browser", "main"],
aliasFields: ["matrix_src_browser", "browser"],
// We need to specify that TS can be resolved without an extension // We need to specify that TS can be resolved without an extension
extensions: [".js", ".json", ".ts", ".tsx"], extensions: [".js", ".json", ".ts", ".tsx"],
alias: { alias: {

426
yarn.lock
View file

@ -34,7 +34,7 @@
dependencies: dependencies:
axe-core "~4.10.2" axe-core "~4.10.2"
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0", "@babel/code-frame@^7.26.2": "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0":
version "7.26.2" version "7.26.2"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85"
integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==
@ -51,17 +51,12 @@
"@babel/highlight" "^7.25.7" "@babel/highlight" "^7.25.7"
picocolors "^1.0.0" picocolors "^1.0.0"
"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.26.0": "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.25.9", "@babel/compat-data@^7.26.0":
version "7.26.2" version "7.26.2"
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.2.tgz#278b6b13664557de95b8f35b90d96785850bb56e" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.2.tgz#278b6b13664557de95b8f35b90d96785850bb56e"
integrity sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg== integrity sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==
"@babel/compat-data@^7.25.9": "@babel/core@^7.0.0", "@babel/core@^7.11.6", "@babel/core@^7.12.10", "@babel/core@^7.12.3", "@babel/core@^7.18.5", "@babel/core@^7.21.3", "@babel/core@^7.23.9", "@babel/core@^7.24.4":
version "7.26.3"
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.3.tgz#99488264a56b2aded63983abd6a417f03b92ed02"
integrity sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==
"@babel/core@^7.0.0", "@babel/core@^7.11.6", "@babel/core@^7.12.10", "@babel/core@^7.12.3", "@babel/core@^7.18.5", "@babel/core@^7.21.3", "@babel/core@^7.23.9":
version "7.26.0" version "7.26.0"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.0.tgz#d78b6023cc8f3114ccf049eb219613f74a747b40" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.0.tgz#d78b6023cc8f3114ccf049eb219613f74a747b40"
integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg== integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==
@ -108,13 +103,13 @@
"@jridgewell/trace-mapping" "^0.3.25" "@jridgewell/trace-mapping" "^0.3.25"
jsesc "^3.0.2" jsesc "^3.0.2"
"@babel/generator@^7.26.0", "@babel/generator@^7.26.3": "@babel/generator@^7.25.9", "@babel/generator@^7.26.0":
version "7.26.3" version "7.26.2"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.3.tgz#ab8d4360544a425c90c248df7059881f4b2ce019" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.2.tgz#87b75813bec87916210e5e01939a4c823d6bb74f"
integrity sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ== integrity sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==
dependencies: dependencies:
"@babel/parser" "^7.26.3" "@babel/parser" "^7.26.2"
"@babel/types" "^7.26.3" "@babel/types" "^7.26.0"
"@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/gen-mapping" "^0.3.5"
"@jridgewell/trace-mapping" "^0.3.25" "@jridgewell/trace-mapping" "^0.3.25"
jsesc "^3.0.2" jsesc "^3.0.2"
@ -308,13 +303,20 @@
dependencies: dependencies:
"@babel/types" "^7.25.8" "@babel/types" "^7.25.8"
"@babel/parser@^7.25.9", "@babel/parser@^7.26.0", "@babel/parser@^7.26.3": "@babel/parser@^7.24.4":
version "7.26.3" version "7.26.3"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.3.tgz#8c51c5db6ddf08134af1ddbacf16aaab48bac234" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.3.tgz#8c51c5db6ddf08134af1ddbacf16aaab48bac234"
integrity sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA== integrity sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==
dependencies: dependencies:
"@babel/types" "^7.26.3" "@babel/types" "^7.26.3"
"@babel/parser@^7.25.9", "@babel/parser@^7.26.0", "@babel/parser@^7.26.2":
version "7.26.2"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.2.tgz#fd7b6f487cfea09889557ef5d4eeb9ff9a5abd11"
integrity sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==
dependencies:
"@babel/types" "^7.26.0"
"@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.25.9": "@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.25.9":
version "7.25.9" version "7.25.9"
resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz#cc2e53ebf0a0340777fff5ed521943e253b4d8fe" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz#cc2e53ebf0a0340777fff5ed521943e253b4d8fe"
@ -1065,9 +1067,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.26.3" version "7.25.9"
resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.26.3.tgz#7c5e028d623b4683c1f83a0bd4713b9100560caa" resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.25.9.tgz#5f473035dc2094bcfdbc7392d0766bd42dce173e"
integrity sha512-Nl03d6T9ky516DGK2YMxrTqvnpUW63TnJMOMonj+Zae0JiPC5BC9xPMSL6L8fiSpA5vP88qfygavVQvnLp+6Cw== integrity sha512-D3to0uSPiWE7rBrdIICCd0tJSIGpLaaGptna2+w7Pft5xMqLpA1sz99DK5TZ1TjGbdQ/VI1eCSZ06dv3lT4JOw==
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"
@ -1126,15 +1128,15 @@
globals "^11.1.0" globals "^11.1.0"
"@babel/traverse@^7.25.9": "@babel/traverse@^7.25.9":
version "7.26.4" version "7.25.9"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.4.tgz#ac3a2a84b908dde6d463c3bfa2c5fdc1653574bd" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.25.9.tgz#a50f8fe49e7f69f53de5bea7e413cd35c5e13c84"
integrity sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w== integrity sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==
dependencies: dependencies:
"@babel/code-frame" "^7.26.2" "@babel/code-frame" "^7.25.9"
"@babel/generator" "^7.26.3" "@babel/generator" "^7.25.9"
"@babel/parser" "^7.26.3" "@babel/parser" "^7.25.9"
"@babel/template" "^7.25.9" "@babel/template" "^7.25.9"
"@babel/types" "^7.26.3" "@babel/types" "^7.25.9"
debug "^4.3.1" debug "^4.3.1"
globals "^11.1.0" globals "^11.1.0"
@ -1147,7 +1149,7 @@
"@babel/helper-validator-identifier" "^7.25.7" "@babel/helper-validator-identifier" "^7.25.7"
to-fast-properties "^2.0.0" to-fast-properties "^2.0.0"
"@babel/types@^7.25.7", "@babel/types@^7.4.4": "@babel/types@^7.25.7", "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.4.4":
version "7.26.0" version "7.26.0"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.0.tgz#deabd08d6b753bc8e0f198f8709fb575e31774ff" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.0.tgz#deabd08d6b753bc8e0f198f8709fb575e31774ff"
integrity sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA== integrity sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==
@ -1155,7 +1157,7 @@
"@babel/helper-string-parser" "^7.25.9" "@babel/helper-string-parser" "^7.25.9"
"@babel/helper-validator-identifier" "^7.25.9" "@babel/helper-validator-identifier" "^7.25.9"
"@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.26.3": "@babel/types@^7.26.3":
version "7.26.3" version "7.26.3"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.3.tgz#37e79830f04c2b5687acc77db97fbc75fb81f3c0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.3.tgz#37e79830f04c2b5687acc77db97fbc75fb81f3c0"
integrity sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA== integrity sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==
@ -1584,10 +1586,10 @@
dependencies: dependencies:
"@floating-ui/dom" "^1.0.0" "@floating-ui/dom" "^1.0.0"
"@floating-ui/react@^0.27.0": "@floating-ui/react@^0.26.24":
version "0.27.0" version "0.26.25"
resolved "https://registry.yarnpkg.com/@floating-ui/react/-/react-0.27.0.tgz#e0931fd09374ab4b8ce1a1af5cb44d1ccd1bb95a" resolved "https://registry.yarnpkg.com/@floating-ui/react/-/react-0.26.25.tgz#cf4c8a2b89fab1a71712d15e6551df3bfbd2ea1d"
integrity sha512-WLEksq7fJapXSJbmfiyq9pAW0a7ZFMEJToFE4oTDESxGjoa+nZu3YMjmZE2KvoUtQhqOK2yMMfWQFZyeWD0wGQ== integrity sha512-hZOmgN0NTOzOuZxI1oIrDu3Gcl8WViIkvPMpB4xdd4QD6xAMtwgwr3VPoiyH/bLtRcS1cDnhxLSD1NsMJmwh/A==
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,37 +1610,36 @@
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.3.1": "@formatjs/ecma402-abstract@2.2.4":
version "2.3.1" version "2.2.4"
resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.1.tgz#cdeb3ffe1aeea9c4284b85b7e37e8e8615314c39" resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-2.2.4.tgz#355e42d375678229d46dc8ad7a7139520dd03e7b"
integrity sha512-Ip9uV+/MpLXWRk03U/GzeJMuPeOXpJBSB5V1tjA6kJhvqssye5J5LoYLc7Z5IAHb7nR62sRoguzrFiVCP/hnzw== integrity sha512-lFyiQDVvSbQOpU+WFd//ILolGj4UgA/qXrKeZxdV14uKiAUiPAtX6XAn7WBCRi7Mx6I7EybM9E5yYn4BIpZWYg==
dependencies: dependencies:
"@formatjs/fast-memoize" "2.2.5" "@formatjs/fast-memoize" "2.2.3"
"@formatjs/intl-localematcher" "0.5.9" "@formatjs/intl-localematcher" "0.5.8"
decimal.js "10"
tslib "2" tslib "2"
"@formatjs/fast-memoize@2.2.5": "@formatjs/fast-memoize@2.2.3":
version "2.2.5" version "2.2.3"
resolved "https://registry.yarnpkg.com/@formatjs/fast-memoize/-/fast-memoize-2.2.5.tgz#54a4a1793d773b72c372d3dcab3595149aee7880" resolved "https://registry.yarnpkg.com/@formatjs/fast-memoize/-/fast-memoize-2.2.3.tgz#74e64109279d5244f9fc281f3ae90c407cece823"
integrity sha512-6PoewUMrrcqxSoBXAOJDiW1m+AmkrAj0RiXnOMD59GRaswjXhm3MDhgepXPBgonc09oSirAJTsAggzAGQf6A6g== integrity sha512-3jeJ+HyOfu8osl3GNSL4vVHUuWFXR03Iz9jjgI7RwjG6ysu/Ymdr0JRCPHfF5yGbTE6JCrd63EpvX1/WybYRbA==
dependencies: dependencies:
tslib "2" tslib "2"
"@formatjs/intl-localematcher@0.5.9": "@formatjs/intl-localematcher@0.5.8":
version "0.5.9" version "0.5.8"
resolved "https://registry.yarnpkg.com/@formatjs/intl-localematcher/-/intl-localematcher-0.5.9.tgz#43c6ee22be85b83340bcb09bdfed53657a2720db" resolved "https://registry.yarnpkg.com/@formatjs/intl-localematcher/-/intl-localematcher-0.5.8.tgz#b11bbd04bd3551f7cadcb1ef1e231822d0e3c97e"
integrity sha512-8zkGu/sv5euxbjfZ/xmklqLyDGQSxsLqg8XOq88JW3cmJtzhCP8EtSJXlaKZnVO4beEaoiT9wj4eIoCQ9smwxA== 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.7" version "11.7.4"
resolved "https://registry.yarnpkg.com/@formatjs/intl-segmenter/-/intl-segmenter-11.7.7.tgz#8a5aaa316e11ca2d31b99222e6fcf1ab539b085e" resolved "https://registry.yarnpkg.com/@formatjs/intl-segmenter/-/intl-segmenter-11.7.4.tgz#f99d87ee3f98515069285438a4913681fc243252"
integrity sha512-610J5xz5DxtEpa16zNR89CrvA9qWHxQFkUB3FKiGao0Nwn7i8cl+oyBhuH9SvtXF9j2LUOM9VMdVCMzJkVANNw== integrity sha512-pyHgFO86/CReKl20oK9jgaTMzSaG/nIMteMW8YuwUcS22EoMI1qbGTZ65oQ38KMT05SiHiMee2CP3WZvCi8YSQ==
dependencies: dependencies:
"@formatjs/ecma402-abstract" "2.3.1" "@formatjs/ecma402-abstract" "2.2.4"
"@formatjs/intl-localematcher" "0.5.9" "@formatjs/intl-localematcher" "0.5.8"
tslib "2" tslib "2"
"@humanwhocodes/config-array@^0.13.0": "@humanwhocodes/config-array@^0.13.0":
@ -1881,9 +1882,9 @@
chalk "^4.0.0" chalk "^4.0.0"
"@jridgewell/gen-mapping@^0.3.5": "@jridgewell/gen-mapping@^0.3.5":
version "0.3.8" version "0.3.5"
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36"
integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA== integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==
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"
@ -2433,10 +2434,10 @@
"@sentry/core" "8.40.0" "@sentry/core" "8.40.0"
"@sentry/types" "8.40.0" "@sentry/types" "8.40.0"
"@sentry/babel-plugin-component-annotate@2.22.7": "@sentry/babel-plugin-component-annotate@2.22.6":
version "2.22.7" version "2.22.6"
resolved "https://registry.yarnpkg.com/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-2.22.7.tgz#604c7e33d48528a13477e7af597c4d5fca51b8bd" resolved "https://registry.yarnpkg.com/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-2.22.6.tgz#829d6caf2c95c1c46108336de4e1049e6521435e"
integrity sha512-aa7XKgZMVl6l04NY+3X7BP7yvQ/s8scn8KzQfTLrGRarziTlMGrsCOBQtCNWXOPEbtxAIHpZ9dsrAn5EJSivOQ== integrity sha512-V2g1Y1I5eSe7dtUVMBvAJr8BaLRr4CLrgNgtPaZyMT4Rnps82SrZ5zqmEkLXPumlXhLUWR6qzoMNN2u+RXVXfQ==
"@sentry/browser@^8.0.0": "@sentry/browser@^8.0.0":
version "8.40.0" version "8.40.0"
@ -2450,14 +2451,14 @@
"@sentry/core" "8.40.0" "@sentry/core" "8.40.0"
"@sentry/types" "8.40.0" "@sentry/types" "8.40.0"
"@sentry/bundler-plugin-core@2.22.7": "@sentry/bundler-plugin-core@2.22.6":
version "2.22.7" version "2.22.6"
resolved "https://registry.yarnpkg.com/@sentry/bundler-plugin-core/-/bundler-plugin-core-2.22.7.tgz#28204a224cd1fef58d157e5beeb2493947a9bc35" resolved "https://registry.yarnpkg.com/@sentry/bundler-plugin-core/-/bundler-plugin-core-2.22.6.tgz#a1ea1fd43700a3ece9e7db016997e79a2782b87d"
integrity sha512-ouQh5sqcB8vsJ8yTTe0rf+iaUkwmeUlGNFi35IkCFUQlWJ22qS6OfvNjOqFI19e6eGUXks0c/2ieFC4+9wJ+1g== integrity sha512-1esQdgSUCww9XAntO4pr7uAM5cfGhLsgTK9MEwAKNfvpMYJi9NUTYa3A7AZmdA8V6107Lo4OD7peIPrDRbaDCg==
dependencies: dependencies:
"@babel/core" "^7.18.5" "@babel/core" "^7.18.5"
"@sentry/babel-plugin-component-annotate" "2.22.7" "@sentry/babel-plugin-component-annotate" "2.22.6"
"@sentry/cli" "2.39.1" "@sentry/cli" "^2.36.1"
dotenv "^16.3.1" dotenv "^16.3.1"
find-up "^5.0.0" find-up "^5.0.0"
glob "^9.3.2" glob "^9.3.2"
@ -2499,7 +2500,7 @@
resolved "https://registry.yarnpkg.com/@sentry/cli-win32-x64/-/cli-win32-x64-2.39.1.tgz#1a874a5570c6d162b35d9d001c96e5389d07d2cb" resolved "https://registry.yarnpkg.com/@sentry/cli-win32-x64/-/cli-win32-x64-2.39.1.tgz#1a874a5570c6d162b35d9d001c96e5389d07d2cb"
integrity sha512-xv0R2CMf/X1Fte3cMWie1NXuHmUyQPDBfCyIt6k6RPFPxAYUgcqgMPznYwVMwWEA1W43PaOkSn3d8ZylsDaETw== integrity sha512-xv0R2CMf/X1Fte3cMWie1NXuHmUyQPDBfCyIt6k6RPFPxAYUgcqgMPznYwVMwWEA1W43PaOkSn3d8ZylsDaETw==
"@sentry/cli@2.39.1": "@sentry/cli@^2.36.1":
version "2.39.1" version "2.39.1"
resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-2.39.1.tgz#916bb5b7567ccf7fdf94ef6cf8a2b9ab78370d29" resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-2.39.1.tgz#916bb5b7567ccf7fdf94ef6cf8a2b9ab78370d29"
integrity sha512-JIb3e9vh0+OmQ0KxmexMXg9oZsR/G7HMwxt5BUIKAXZ9m17Xll4ETXTRnRUBT3sf7EpNGAmlQk1xEmVN9pYZYQ== integrity sha512-JIb3e9vh0+OmQ0KxmexMXg9oZsR/G7HMwxt5BUIKAXZ9m17Xll4ETXTRnRUBT3sf7EpNGAmlQk1xEmVN9pYZYQ==
@ -2531,11 +2532,11 @@
integrity sha512-nuCf3U3deolPM9BjNnwCc33UtFl9ec15/r74ngAkNccn+A2JXdIAsDkGJMO/9mgSFykLe1QyeJ0pQFRisCGOiA== integrity sha512-nuCf3U3deolPM9BjNnwCc33UtFl9ec15/r74ngAkNccn+A2JXdIAsDkGJMO/9mgSFykLe1QyeJ0pQFRisCGOiA==
"@sentry/webpack-plugin@^2.7.1": "@sentry/webpack-plugin@^2.7.1":
version "2.22.7" version "2.22.6"
resolved "https://registry.yarnpkg.com/@sentry/webpack-plugin/-/webpack-plugin-2.22.7.tgz#992c6c782c736f22e72eb318745e28cc24aabad7" resolved "https://registry.yarnpkg.com/@sentry/webpack-plugin/-/webpack-plugin-2.22.6.tgz#8c9d27d5cd89153a5b6e08cc9dcb3048b122ffbc"
integrity sha512-j5h5LZHWDlm/FQCCmEghQ9FzYXwfZdlOf3FE/X6rK6lrtx0JCAkq+uhMSasoyP4XYKL4P4vRS6WFSos4jxf/UA== integrity sha512-BiLhAzQYAz/9kCXKj2LeUKWf/9GBVn2dD0DeYK89s+sjDEaxjbcLBBiLlLrzT7eC9QVj2tUZRKOi6puCfc8ysw==
dependencies: dependencies:
"@sentry/bundler-plugin-core" "2.22.7" "@sentry/bundler-plugin-core" "2.22.6"
unplugin "1.0.1" unplugin "1.0.1"
uuid "^9.0.0" uuid "^9.0.0"
@ -3048,9 +3049,9 @@
"@types/node" "*" "@types/node" "*"
"@types/jsrsasign@^10.5.4": "@types/jsrsasign@^10.5.4":
version "10.5.15" version "10.5.14"
resolved "https://registry.yarnpkg.com/@types/jsrsasign/-/jsrsasign-10.5.15.tgz#5cf1ee506b2fa2435b6e1786a873285c7110eb82" resolved "https://registry.yarnpkg.com/@types/jsrsasign/-/jsrsasign-10.5.14.tgz#61d1dbd791ecd11db556c1ca5d82453fc7207338"
integrity sha512-3stUTaSRtN09PPzVWR6aySD9gNnuymz+WviNHoTb85dKu+BjaV4uBbWWGykBBJkfwPtcNZVfTn2lbX00U+yhpQ== integrity sha512-lppSlfK6etu+cuKs40K4rg8As79PH6hzIB+v55zSqImbSH3SE6Fm8MBHCiI91cWlAP3Z4igtJK1VL3fSN09blQ==
"@types/katex@^0.16.0": "@types/katex@^0.16.0":
version "0.16.7" version "0.16.7"
@ -3114,9 +3115,9 @@
undici-types "~6.20.0" undici-types "~6.20.0"
"@types/node@18": "@types/node@18":
version "18.19.68" version "18.19.66"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.68.tgz#f4f10d9927a7eaf3568c46a6d739cc0967ccb701" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.66.tgz#0937a47904ceba5994eedf5cf4b6d503d8d6136c"
integrity sha512-QGtpFH1vB99ZmTa63K4/FU8twThj4fuVSBkGddTp7uIL/cuoLWIUSL2RcOaigBhfR+hg5pgGkBnkoOxrTVBMKw== integrity sha512-14HmtUdGxFUalGRfLLn9Gc1oNWvWh5zNbsyOLo5JV6WARSeN1QcEBKRnZm9QqNfrutgsl/hY4eJW63aZ44aBCg==
dependencies: dependencies:
undici-types "~5.26.4" undici-types "~5.26.4"
@ -3401,12 +3402,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.5.0": "@vector-im/compound-web@^7.4.0":
version "7.5.0" version "7.4.0"
resolved "https://registry.yarnpkg.com/@vector-im/compound-web/-/compound-web-7.5.0.tgz#1547af5f0ee27b94f79ab11eee006059f3d09707" resolved "https://registry.yarnpkg.com/@vector-im/compound-web/-/compound-web-7.4.0.tgz#a5af8af6346f8ff6c14c70f5d4eb2eab7357a7cc"
integrity sha512-Xhef8H5WrRmPuanzRBs8rnl+hwbcQnC7nKSCupUczAQ5hjlieBx4vcQYQ/nMkrs4rMGjgfFtR3E18wT5LlML/A== integrity sha512-ZRBUeEGNmj/fTkIRa8zGnyVN7ytowpfOtHChqNm+m/+OTJN3o/lOMuQHDV8jeSEW2YwPJqGvPuG/dRr89IcQkA==
dependencies: dependencies:
"@floating-ui/react" "^0.27.0" "@floating-ui/react" "^0.26.24"
"@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"
@ -3424,7 +3425,7 @@
dependencies: dependencies:
eslint-plugin-unicorn "^54.0.0" eslint-plugin-unicorn "^54.0.0"
"@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.14.1": "@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.12.1":
version "1.14.1" version "1.14.1"
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.14.1.tgz#a9f6a07f2b03c95c8d38c4536a1fdfb521ff55b6" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.14.1.tgz#a9f6a07f2b03c95c8d38c4536a1fdfb521ff55b6"
integrity sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ== integrity sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==
@ -3490,7 +3491,7 @@
resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.13.2.tgz#917a20e93f71ad5602966c2d685ae0c6c21f60f1" resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.13.2.tgz#917a20e93f71ad5602966c2d685ae0c6c21f60f1"
integrity sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ== integrity sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==
"@webassemblyjs/wasm-edit@^1.14.1": "@webassemblyjs/wasm-edit@^1.12.1":
version "1.14.1" version "1.14.1"
resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz#ac6689f502219b59198ddec42dcd496b1004d597" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz#ac6689f502219b59198ddec42dcd496b1004d597"
integrity sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ== integrity sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==
@ -3525,7 +3526,7 @@
"@webassemblyjs/wasm-gen" "1.14.1" "@webassemblyjs/wasm-gen" "1.14.1"
"@webassemblyjs/wasm-parser" "1.14.1" "@webassemblyjs/wasm-parser" "1.14.1"
"@webassemblyjs/wasm-parser@1.14.1", "@webassemblyjs/wasm-parser@^1.14.1": "@webassemblyjs/wasm-parser@1.14.1", "@webassemblyjs/wasm-parser@^1.12.1":
version "1.14.1" version "1.14.1"
resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz#b3e13f1893605ca78b52c68e54cf6a865f90b9fb" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz#b3e13f1893605ca78b52c68e54cf6a865f90b9fb"
integrity sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ== integrity sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==
@ -4211,15 +4212,7 @@ bytes@3.1.2:
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
call-bind-apply-helpers@^1.0.0: call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7:
version "1.0.1"
resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz#32e5892e6361b29b0b545ba6f7763378daca2840"
integrity sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==
dependencies:
es-errors "^1.3.0"
function-bind "^1.1.2"
call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6:
version "1.0.7" version "1.0.7"
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9"
integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==
@ -4230,16 +4223,6 @@ call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6:
get-intrinsic "^1.2.4" get-intrinsic "^1.2.4"
set-function-length "^1.2.1" set-function-length "^1.2.1"
call-bind@^1.0.7:
version "1.0.8"
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.8.tgz#0736a9660f537e3388826f440d5ec45f744eaa4c"
integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==
dependencies:
call-bind-apply-helpers "^1.0.0"
es-define-property "^1.0.0"
get-intrinsic "^1.2.4"
set-function-length "^1.2.2"
callsites@^3.0.0: callsites@^3.0.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
@ -4821,11 +4804,11 @@ css-tree@^2.3.1:
source-map-js "^1.0.1" source-map-js "^1.0.1"
css-tree@^3.0.0, css-tree@^3.0.1: css-tree@^3.0.0, css-tree@^3.0.1:
version "3.1.0" version "3.0.1"
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-3.1.0.tgz#7aabc035f4e66b5c86f54570d55e05b1346eb0fd" resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-3.0.1.tgz#bea6deaea60bb5bcf416adfb1ecf607a8d9471f6"
integrity sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w== integrity sha512-8Fxxv+tGhORlshCdCwnNJytvlvq46sOLSYEx2ZIGurahWvMucSRnyjPA3AmrMq4VPRYbHVpWj5VkiVasrM2H4Q==
dependencies: dependencies:
mdn-data "2.12.2" mdn-data "2.12.1"
source-map-js "^1.0.1" source-map-js "^1.0.1"
css-tree@~2.2.0: css-tree@~2.2.0:
@ -5002,10 +4985,10 @@ debug@2.6.9:
dependencies: dependencies:
ms "2.0.0" ms "2.0.0"
debug@4, debug@^4.1.0, debug@^4.3.1: debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.7, debug@~4.3.6:
version "4.4.0" version "4.3.7"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52"
integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==
dependencies: dependencies:
ms "^2.1.3" ms "^2.1.3"
@ -5016,19 +4999,12 @@ debug@^3.2.7:
dependencies: dependencies:
ms "^2.1.1" ms "^2.1.1"
debug@^4.1.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.7, debug@~4.3.6:
version "4.3.7"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52"
integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==
dependencies:
ms "^2.1.3"
decamelize@^1.2.0: decamelize@^1.2.0:
version "1.2.0" version "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, decimal.js@^10.4.2: 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==
@ -5280,18 +5256,9 @@ dot-case@^3.0.4:
tslib "^2.0.3" tslib "^2.0.3"
dotenv@^16.0.2, dotenv@^16.3.1: dotenv@^16.0.2, dotenv@^16.3.1:
version "16.4.7" version "16.4.5"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.7.tgz#0e20c5b82950140aa99be360a8a5f52335f53c26" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f"
integrity sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ== integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==
dunder-proto@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.0.tgz#c2fce098b3c8f8899554905f4377b6d85dabaa80"
integrity sha512-9+Sj30DIu+4KvHqMfLUGLFYL2PkURSYMVXJyXe92nFRvlYq5hBjLEhblKB+vkd/WVlUYMWigiY07T91Fkk0+4A==
dependencies:
call-bind-apply-helpers "^1.0.0"
es-errors "^1.3.0"
gopd "^1.2.0"
duplexer@^0.1.2: duplexer@^0.1.2:
version "0.1.2" version "0.1.2"
@ -5330,9 +5297,9 @@ ejs@^3.1.8:
jake "^10.8.5" jake "^10.8.5"
electron-to-chromium@^1.5.41: electron-to-chromium@^1.5.41:
version "1.5.72" version "1.5.67"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.72.tgz#a732805986d3a5b5fedd438ddf4616c7d78ac2df" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.67.tgz#66ebd2be4a77469ac2760ef5e9e460ba9a43a845"
integrity sha512-ZpSAUOZ2Izby7qnZluSrAlGgGQzucmFbN0n64dYzocYxnxV5ufurpj3VgEe4cUp7ir9LmeLxNYo8bVnlM8bQHw== integrity sha512-nz88NNBsD7kQSAGGJyp8hS6xSPtWwqNogA0mjtc2nUYeEf3nURK9qpV18TuBdDmEDgVWotS8Wkzf+V52dSQ/LQ==
emittery@^0.13.1: emittery@^0.13.1:
version "0.13.1" version "0.13.1"
@ -5481,10 +5448,12 @@ es-abstract@^1.17.5, es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23
unbox-primitive "^1.0.2" unbox-primitive "^1.0.2"
which-typed-array "^1.1.15" which-typed-array "^1.1.15"
es-define-property@^1.0.0, es-define-property@^1.0.1: es-define-property@^1.0.0:
version "1.0.1" version "1.0.0"
resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845"
integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==
dependencies:
get-intrinsic "^1.2.4"
es-errors@^1.2.1, es-errors@^1.3.0: es-errors@^1.2.1, es-errors@^1.3.0:
version "1.3.0" version "1.3.0"
@ -5674,6 +5643,18 @@ eslint-plugin-matrix-org@^2.0.2:
resolved "https://registry.yarnpkg.com/eslint-plugin-matrix-org/-/eslint-plugin-matrix-org-2.0.2.tgz#95b86b0f16704ab19740f7c3c62eae69e20365e6" resolved "https://registry.yarnpkg.com/eslint-plugin-matrix-org/-/eslint-plugin-matrix-org-2.0.2.tgz#95b86b0f16704ab19740f7c3c62eae69e20365e6"
integrity sha512-cQy5Rjeq6uyu1mLXlPZwEJdyM0NmclrnEz68y792FSuuxzMyJNNYLGDQ5CkYW8H+PrD825HUFZ34pNXnjMOzOw== integrity sha512-cQy5Rjeq6uyu1mLXlPZwEJdyM0NmclrnEz68y792FSuuxzMyJNNYLGDQ5CkYW8H+PrD825HUFZ34pNXnjMOzOw==
eslint-plugin-react-compiler@^19.0.0-beta-df7b47d-20241124:
version "19.0.0-beta-df7b47d-20241124"
resolved "https://registry.yarnpkg.com/eslint-plugin-react-compiler/-/eslint-plugin-react-compiler-19.0.0-beta-df7b47d-20241124.tgz#468751d3a8a6781189405ee56b39b80545306df8"
integrity sha512-82PfnllC8jP/68KdLAbpWuYTcfmtGLzkqy2IW85WopKMTr+4rdQpp+lfliQ/QE79wWrv/dRoADrk3Pdhq25nTw==
dependencies:
"@babel/core" "^7.24.4"
"@babel/parser" "^7.24.4"
"@babel/plugin-transform-private-methods" "^7.25.9"
hermes-parser "^0.25.1"
zod "^3.22.4"
zod-validation-error "^3.0.3"
eslint-plugin-react-hooks@^5.0.0: eslint-plugin-react-hooks@^5.0.0:
version "5.0.0" version "5.0.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0.tgz#72e2eefbac4b694f5324154619fee44f5f60f101" resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0.tgz#72e2eefbac4b694f5324154619fee44f5f60f101"
@ -5967,9 +5948,9 @@ expect@^29.0.0, expect@^29.7.0:
jest-util "^29.7.0" jest-util "^29.7.0"
express@^4.18.2, express@^4.19.2: express@^4.18.2, express@^4.19.2:
version "4.21.2" version "4.21.1"
resolved "https://registry.yarnpkg.com/express/-/express-4.21.2.tgz#cf250e48362174ead6cea4a566abef0162c1ec32" resolved "https://registry.yarnpkg.com/express/-/express-4.21.1.tgz#9dae5dda832f16b4eec941a4e44aa89ec481b281"
integrity sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA== integrity sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==
dependencies: dependencies:
accepts "~1.3.8" accepts "~1.3.8"
array-flatten "1.1.1" array-flatten "1.1.1"
@ -5990,7 +5971,7 @@ express@^4.18.2, express@^4.19.2:
methods "~1.1.2" methods "~1.1.2"
on-finished "2.4.1" on-finished "2.4.1"
parseurl "~1.3.3" parseurl "~1.3.3"
path-to-regexp "0.1.12" path-to-regexp "0.1.10"
proxy-addr "~2.0.7" proxy-addr "~2.0.7"
qs "6.13.0" qs "6.13.0"
range-parser "~1.2.1" range-parser "~1.2.1"
@ -6351,7 +6332,7 @@ get-east-asian-width@^1.0.0:
resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz#21b4071ee58ed04ee0db653371b55b4299875389" resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz#21b4071ee58ed04ee0db653371b55b4299875389"
integrity sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ== integrity sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==
get-intrinsic@^1.2.1, get-intrinsic@^1.2.3: get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4:
version "1.2.4" version "1.2.4"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd"
integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==
@ -6362,20 +6343,6 @@ get-intrinsic@^1.2.1, get-intrinsic@^1.2.3:
has-symbols "^1.0.3" has-symbols "^1.0.3"
hasown "^2.0.0" hasown "^2.0.0"
get-intrinsic@^1.2.4:
version "1.2.5"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.5.tgz#dfe7dd1b30761b464fe51bf4bb00ac7c37b681e7"
integrity sha512-Y4+pKa7XeRUPWFNvOOYHkRYrfzW07oraURSvjDmRVOJ748OrVmeXtpE4+GCEHncjCjkTxPNRt8kEbxDhsn6VTg==
dependencies:
call-bind-apply-helpers "^1.0.0"
dunder-proto "^1.0.0"
es-define-property "^1.0.1"
es-errors "^1.3.0"
function-bind "^1.1.2"
gopd "^1.2.0"
has-symbols "^1.1.0"
hasown "^2.0.2"
get-nonce@^1.0.0: get-nonce@^1.0.0:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/get-nonce/-/get-nonce-1.0.1.tgz#fdf3f0278073820d2ce9426c18f07481b1e0cdf3" resolved "https://registry.yarnpkg.com/get-nonce/-/get-nonce-1.0.1.tgz#fdf3f0278073820d2ce9426c18f07481b1e0cdf3"
@ -6552,12 +6519,7 @@ globjoin@^0.1.4:
resolved "https://registry.yarnpkg.com/globjoin/-/globjoin-0.1.4.tgz#2f4494ac8919e3767c5cbb691e9f463324285d43" resolved "https://registry.yarnpkg.com/globjoin/-/globjoin-0.1.4.tgz#2f4494ac8919e3767c5cbb691e9f463324285d43"
integrity sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg== integrity sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==
gopd@^1.0.1, gopd@^1.2.0: gopd@^1.0.1, gopd@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1"
integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
gopd@^1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.1.0.tgz#df8f0839c2d48caefc32a025a49294d39606c912" resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.1.0.tgz#df8f0839c2d48caefc32a025a49294d39606c912"
integrity sha512-FQoVQnqcdk4hVM4JN1eromaun4iuS34oStkdlLENLdpULsuQcTyXj8w7ayhuUfPwEYZ1ZOooOTT6fdA9Vmx/RA== integrity sha512-FQoVQnqcdk4hVM4JN1eromaun4iuS34oStkdlLENLdpULsuQcTyXj8w7ayhuUfPwEYZ1ZOooOTT6fdA9Vmx/RA==
@ -6608,30 +6570,18 @@ has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2:
dependencies: dependencies:
es-define-property "^1.0.0" es-define-property "^1.0.0"
has-proto@^1.0.1: has-proto@^1.0.1, has-proto@^1.0.3:
version "1.2.0"
resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.2.0.tgz#5de5a6eabd95fdffd9818b43055e8065e39fe9d5"
integrity sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==
dependencies:
dunder-proto "^1.0.0"
has-proto@^1.0.3:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.1.0.tgz#deb10494cbbe8809bce168a3b961f42969f5ed43" resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.1.0.tgz#deb10494cbbe8809bce168a3b961f42969f5ed43"
integrity sha512-QLdzI9IIO1Jg7f9GT1gXpPpXArAn6cS31R1eEZqz08Gc+uQ8/XiqHWt17Fiw+2p6oTTIq5GXEpQkAlA88YRl/Q== integrity sha512-QLdzI9IIO1Jg7f9GT1gXpPpXArAn6cS31R1eEZqz08Gc+uQ8/XiqHWt17Fiw+2p6oTTIq5GXEpQkAlA88YRl/Q==
dependencies: dependencies:
call-bind "^1.0.7" call-bind "^1.0.7"
has-symbols@^1.0.2: has-symbols@^1.0.2, has-symbols@^1.0.3:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
has-symbols@^1.0.3, has-symbols@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338"
integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==
has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: has-tostringtag@^1.0.0, has-tostringtag@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc"
@ -6651,6 +6601,18 @@ he@^1.2.0:
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
hermes-estree@0.25.1:
version "0.25.1"
resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.25.1.tgz#6aeec17d1983b4eabf69721f3aa3eb705b17f480"
integrity sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==
hermes-parser@^0.25.1:
version "0.25.1"
resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.25.1.tgz#5be0e487b2090886c62bd8a11724cd766d5f54d1"
integrity sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==
dependencies:
hermes-estree "0.25.1"
highlight.js@^11.3.1: highlight.js@^11.3.1:
version "11.10.0" version "11.10.0"
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.10.0.tgz#6e3600dc4b33d6dc23d5bd94fbf72405f5892b92" resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.10.0.tgz#6e3600dc4b33d6dc23d5bd94fbf72405f5892b92"
@ -7829,21 +7791,16 @@ 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.1.0" version "3.0.2"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e"
integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==
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"
@ -7960,9 +7917,9 @@ kleur@^3.0.3:
integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
knip@^5.36.2: knip@^5.36.2:
version "5.39.2" version "5.38.3"
resolved "https://registry.yarnpkg.com/knip/-/knip-5.39.2.tgz#1faacd8d8ef36b509b2f6e396cce85b645abb04e" resolved "https://registry.yarnpkg.com/knip/-/knip-5.38.3.tgz#a139e0c6215c9958d213ce1a9e3985cf5de2774e"
integrity sha512-BuvuWRllLWV/r2G4m9ggNH+DZ6gouP/dhtJPXVlMbWNF++w9/EfrF6k2g7YBKCwjzCC+PXmYtpH8S2t8RjnY4Q== integrity sha512-pg3CMzWlZy4mnuwxieGoK74oOgzFPvsUR/aE8NSqx2oQr56soXTzmw8GsHR277pU52Fe0h4/pho2PMhVeEvj8g==
dependencies: dependencies:
"@nodelib/fs.walk" "1.2.8" "@nodelib/fs.walk" "1.2.8"
"@snyk/github-codeowners" "1.1.0" "@snyk/github-codeowners" "1.1.0"
@ -8041,10 +7998,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.2.0: linkify-element@4.1.4:
version "4.2.0" version "4.1.4"
resolved "https://registry.yarnpkg.com/linkify-element/-/linkify-element-4.2.0.tgz#fb5c6d47576487a463fd22a0cc889e15833aa943" resolved "https://registry.yarnpkg.com/linkify-element/-/linkify-element-4.1.4.tgz#d4050b41fb47c44871e5eed93bc11865e403cc90"
integrity sha512-LahyRMhXAgWTP9TOid7pTv8UUZFDz+saLkIVAoGNmOvISt+uSeBzdGhk3dsvkdzAh1QMhkO+fVJjmkMEITre5g== integrity sha512-XhSTTF7b7OoX4KIkwVG8MET5DSFEHohT0Gp5pjmsByYp+JCyZq5rSZGsar5dYzeuKUV6TqTSLtsH/NzBBwBxgQ==
linkify-it@^4.0.1: linkify-it@^4.0.1:
version "4.0.1" version "4.0.1"
@ -8053,20 +8010,20 @@ linkify-it@^4.0.1:
dependencies: dependencies:
uc.micro "^1.0.1" uc.micro "^1.0.1"
linkify-react@4.2.0: linkify-react@4.1.4:
version "4.2.0" version "4.1.4"
resolved "https://registry.yarnpkg.com/linkify-react/-/linkify-react-4.2.0.tgz#d143b2af8efa5e3b09517b66ed442624c3e06bcf" resolved "https://registry.yarnpkg.com/linkify-react/-/linkify-react-4.1.4.tgz#6c709f3f96543914874982f4b0b00f9c9270ce93"
integrity sha512-dIcDGo+n4FP2FPIHDcqB7cUE+omkcEgQJpc7sNNP4+XZ9FUhFAkKjGnHMzsZM+B4yF93sK166z9K5cKTe/JpzA== integrity sha512-UI9nqHtFzHYRUvVRrYeua5GIXkc0Jy3RpLsJBWEht7HwqjAa2qSaIksGmNSLqclNpO/5AkwaxUJv71I/pQsk9Q==
linkify-string@4.2.0: linkify-string@4.1.4:
version "4.2.0" version "4.1.4"
resolved "https://registry.yarnpkg.com/linkify-string/-/linkify-string-4.2.0.tgz#e8c5a9d57698e81e7cce7f4915ddbcbde17134c0" resolved "https://registry.yarnpkg.com/linkify-string/-/linkify-string-4.1.4.tgz#89fb814e05c5b22f76d2a2a640bc8b1db4c6694f"
integrity sha512-LqOKk0+RlqibFkxjPAGOL7Mfssqj6/SdG9QWGvzeVDpoWXhaw9OXxseCtFanjIl7C6UyTTZizhyGr4IWLfijiw== integrity sha512-4z2UEzEi4SxnhWMzzZ8Pa8vIOwX/2U0XWxk/0UIA7lI+Dn0ZRKqTE9ildnO6Jl6K5hqVuLKTeMD8p4bdFW6P8g==
linkifyjs@4.2.0: linkifyjs@4.1.4:
version "4.2.0" version "4.1.4"
resolved "https://registry.yarnpkg.com/linkifyjs/-/linkifyjs-4.2.0.tgz#9dd30222b9cbabec9c950e725ec00031c7fa3f08" resolved "https://registry.yarnpkg.com/linkifyjs/-/linkifyjs-4.1.4.tgz#2766605a20078d50c90f35af22275a42dfb7dfc4"
integrity sha512-pCj3PrQyATaoTYKHrgWRF3SJwsm61udVh+vuls/Rl6SptiDhgE7ziUIudAedRY9QEfynmM7/RmLEfPUyw1HPCw== integrity sha512-0/NxkHNpiJ0k9VrYCkAn9OtU1eu8xEr1tCCpDtSsVRm/SF0xAak2Gzv3QimSfgUgqLBCDlfhMbu73XvaEHUTPQ==
lint-staged@^15.0.2: lint-staged@^15.0.2:
version "15.2.10" version "15.2.10"
@ -8368,7 +8325,12 @@ mdn-data@2.0.30:
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc" resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc"
integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA== integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==
mdn-data@2.12.2, mdn-data@^2.12.2: mdn-data@2.12.1:
version "2.12.1"
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.12.1.tgz#10cb462215c13d95c92ff60d0fb3becac1bbb924"
integrity sha512-rsfnCbOHjqrhWxwt5/wtSLzpoKTzW7OXdT5lLOIH1OTYhWu9rRJveGq0sKvDZODABH7RX+uoR+DYcpFnq4Tf6Q==
mdn-data@^2.12.2:
version "2.12.2" version "2.12.2"
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.12.2.tgz#9ae6c41a9e65adf61318b32bff7b64fbfb13f8cf" resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.12.2.tgz#9ae6c41a9e65adf61318b32bff7b64fbfb13f8cf"
integrity sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA== integrity sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==
@ -8652,9 +8614,9 @@ node-int64@^0.4.0:
integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==
node-releases@^2.0.18: node-releases@^2.0.18:
version "2.0.19" version "2.0.18"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f"
integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==
normalize-package-data@^2.5.0: normalize-package-data@^2.5.0:
version "2.5.0" version "2.5.0"
@ -9040,10 +9002,10 @@ path-scurry@^2.0.0:
lru-cache "^11.0.0" lru-cache "^11.0.0"
minipass "^7.1.2" minipass "^7.1.2"
path-to-regexp@0.1.12: path-to-regexp@0.1.10:
version "0.1.12" version "0.1.10"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz#67e9108c5c0551b9e5326064387de4763c4d5f8b"
integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ== integrity sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==
path-to-regexp@^2.2.1: path-to-regexp@^2.2.1:
version "2.4.0" version "2.4.0"
@ -9781,10 +9743,10 @@ prelude-ls@^1.2.1:
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
prettier@3.4.2: prettier@3.4.1:
version "3.4.2" version "3.4.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.4.2.tgz#a5ce1fb522a588bf2b78ca44c6e6fe5aa5a2b13f" resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.4.1.tgz#e211d451d6452db0a291672ca9154bc8c2579f7b"
integrity sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ== integrity sha512-G+YdqtITVZmOJje6QkXQWzl3fSfMxFwm1tjTyo9exhkmWSqC4Yhd1+lug++IlR2mvRVAxEDDWYkQdeSztajqgg==
pretty-error@^4.0.0: pretty-error@^4.0.0:
version "4.0.0" version "4.0.0"
@ -10597,7 +10559,7 @@ set-blocking@^2.0.0:
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==
set-function-length@^1.2.1, set-function-length@^1.2.2: set-function-length@^1.2.1:
version "1.2.2" version "1.2.2"
resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449"
integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==
@ -11257,7 +11219,7 @@ terser-webpack-plugin@^5.3.10, terser-webpack-plugin@^5.3.9:
serialize-javascript "^6.0.1" serialize-javascript "^6.0.1"
terser "^5.26.0" terser "^5.26.0"
terser@^5.10.0: terser@^5.10.0, terser@^5.26.0:
version "5.36.0" version "5.36.0"
resolved "https://registry.yarnpkg.com/terser/-/terser-5.36.0.tgz#8b0dbed459ac40ff7b4c9fd5a3a2029de105180e" resolved "https://registry.yarnpkg.com/terser/-/terser-5.36.0.tgz#8b0dbed459ac40ff7b4c9fd5a3a2029de105180e"
integrity sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w== integrity sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==
@ -11267,16 +11229,6 @@ terser@^5.10.0:
commander "^2.20.0" commander "^2.20.0"
source-map-support "~0.5.20" source-map-support "~0.5.20"
terser@^5.26.0:
version "5.37.0"
resolved "https://registry.yarnpkg.com/terser/-/terser-5.37.0.tgz#38aa66d1cfc43d0638fab54e43ff8a4f72a21ba3"
integrity sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==
dependencies:
"@jridgewell/source-map" "^0.3.3"
acorn "^8.8.2"
commander "^2.20.0"
source-map-support "~0.5.20"
test-exclude@^6.0.0: test-exclude@^6.0.0:
version "6.0.0" version "6.0.0"
resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e"
@ -11972,15 +11924,15 @@ webpack-virtual-modules@^0.5.0:
integrity sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw== integrity sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==
webpack@^5.89.0: webpack@^5.89.0:
version "5.97.1" version "5.96.1"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.97.1.tgz#972a8320a438b56ff0f1d94ade9e82eac155fa58" resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.96.1.tgz#3676d1626d8312b6b10d0c18cc049fba7ac01f0c"
integrity sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg== integrity sha512-l2LlBSvVZGhL4ZrPwyr8+37AunkcYj5qh8o6u2/2rzoPc8gxFJkLj1WxNgooi9pnoc06jh0BjuXnamM4qlujZA==
dependencies: dependencies:
"@types/eslint-scope" "^3.7.7" "@types/eslint-scope" "^3.7.7"
"@types/estree" "^1.0.6" "@types/estree" "^1.0.6"
"@webassemblyjs/ast" "^1.14.1" "@webassemblyjs/ast" "^1.12.1"
"@webassemblyjs/wasm-edit" "^1.14.1" "@webassemblyjs/wasm-edit" "^1.12.1"
"@webassemblyjs/wasm-parser" "^1.14.1" "@webassemblyjs/wasm-parser" "^1.12.1"
acorn "^8.14.0" acorn "^8.14.0"
browserslist "^4.24.0" browserslist "^4.24.0"
chrome-trace-event "^1.0.2" chrome-trace-event "^1.0.2"
@ -12319,6 +12271,6 @@ zod-validation-error@^3.0.3:
integrity sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ== integrity sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ==
zod@^3.22.4: zod@^3.22.4:
version "3.24.0" version "3.23.8"
resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.0.tgz#babb32313f7c5f4a99812feee806d186b4f76bde" resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d"
integrity sha512-Hz+wiY8yD0VLA2k/+nsg2Abez674dDGTai33SwNvMPuf9uIrBC9eFgIMQxBBbHFxVXi8W+5nX9DcAh9YNSQm/w== integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==