Compare commits

...
Sign in to create a new pull request.

23 commits

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

As per comment. Fixes the tests.

* Spell locale right

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

---------

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

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

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

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

* fix test

* test for the store

* Add comment
2024-12-11 13:10:27 +00:00
renovate[bot]
b86bb5cc2f
Update guibranco/github-status-action-v2 digest to d469d49 (#28702)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-11 02:01:31 +00:00
renovate[bot]
e835cab139
Update all non-major dependencies (#28703)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-10 17:20:18 +00:00
Hugh Nimmo-Smith
6b7c94905f
Allow trusted Element Call widget to send and receive media encryption key to-device messages (#28316) 2024-12-10 12:05:30 +00:00
ElementRobot
a4e8bb3f9a
[create-pull-request] automated change (#28696)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2024-12-10 06:17:13 +00:00
Hubert Chathi
2b4000d47f
Add delay in test to allow Alice to fetch Bob's device keys (#28668)
* add delay in test to allow Alice to fetch Bob's device keys

* wait until we see bob's device, rather than hard-coding a timeout

* Fix comment

Co-authored-by: Florian Duros <florianduros@element.io>

* fix lint

---------

Co-authored-by: Florian Duros <florianduros@element.io>
2024-12-09 21:02:29 +00:00
Michael Telatynski
01304439ee
Make tsc faster again (#28678)
* Stash initial work to bring TSC from over 6 mins to under 1 minute

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Stabilise types

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Fix incorrect props to AccessibleButton

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Swap AccessibleButton element types to match the props they provide

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Changed my mind, remove spurious previously ignored props

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update snapshots

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-06 17:49:32 +00:00
David Baker
c659afa8db
Rename CreateCrossSigningDialog to InitialCryptoSetupDialog (#28658)
* Rename CreateCrossSigningDialog to InitialCryptoSetup

because it will soon encompass things other than just creating cross
signing.

* Fix name & tests

* Fix import

* Remove code creating key backup

Because this was split out from my key backup by default PR

* Fix comment

* Convert to named export
2024-12-06 10:26:26 +00:00
ElementRobot
9cc5564d50
[create-pull-request] automated change (#28670)
Co-authored-by: t3chguy <t3chguy@users.noreply.github.com>
2024-12-06 09:38:58 +00:00
Michael Telatynski
549300726f
Update CODEOWNERS 2024-12-06 09:18:33 +00:00
ElementRobot
319dab5920
[create-pull-request] automated change (#28669)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2024-12-06 06:17:19 +00:00
renovate[bot]
5c51d179b9
Update linkify to v4.2.0 (#28665)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-05 19:28:39 +00:00
Florian Duros
dbdb23f6bc
Notify to resize the timeline when the pinned message banner is displayed or hidden (#28654) 2024-12-05 16:11:40 +00:00
Florian Duros
5686666ad2
Fix multiple pinned messages flacky tests by waiting the message to be displayed in the banner when pinned. (#28655) 2024-12-05 15:31:16 +00:00
Michael Telatynski
0c4189f2ed
Remove stale webpack configuration (#28649)
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-05 14:42:26 +00:00
Michael Telatynski
450cb608ec
Add mergequeue tag to read-receipts tests and skip running them on PR commits (#28648)
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-05 14:30:28 +00:00
Michael Telatynski
7e03f38a3b
Switch to React18 useId (#28651)
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-12-05 14:11:03 +00:00
88 changed files with 1012 additions and 713 deletions

1
.github/CODEOWNERS vendored
View file

@ -13,6 +13,7 @@
# Ignore translations as those will be updated by GHA for Localazy download
/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
/playwright/plugins/homeserver/synapse/index.ts

View file

@ -130,8 +130,12 @@ jobs:
if: steps.playwright-cache.outputs.cache-hit != 'true'
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
run: yarn playwright test --shard ${{ matrix.runner }}/${{ strategy.job-total }}
run: |
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
if: always()

View file

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

View file

@ -217,3 +217,10 @@ instead of the native `toHaveScreenshot`.
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.
## 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",
"@sentry/browser": "^8.0.0",
"@vector-im/compound-design-tokens": "^2.0.1",
"@vector-im/compound-web": "^7.4.0",
"@vector-im/compound-web": "^7.5.0",
"@vector-im/matrix-wysiwyg": "2.37.13",
"@zxcvbn-ts/core": "^3.0.4",
"@zxcvbn-ts/language-common": "^3.0.4",
@ -116,10 +116,10 @@
"jsrsasign": "^11.0.0",
"jszip": "^3.7.0",
"katex": "^0.16.0",
"linkify-element": "4.1.4",
"linkify-react": "4.1.4",
"linkify-string": "4.1.4",
"linkifyjs": "4.1.4",
"linkify-element": "4.2.0",
"linkify-react": "4.2.0",
"linkify-string": "4.2.0",
"linkifyjs": "4.2.0",
"lodash": "^4.17.21",
"maplibre-gl": "^4.0.0",
"matrix-encrypt-attachment": "^1.0.3",
@ -269,7 +269,7 @@
"postcss-preset-env": "^10.0.0",
"postcss-scss": "^4.0.4",
"postcss-simple-vars": "^7.0.1",
"prettier": "3.4.1",
"prettier": "3.4.2",
"process": "^0.11.10",
"raw-loader": "^4.0.2",
"rimraf": "^6.0.0",

View file

@ -8,6 +8,7 @@ Please see LICENSE files in the repository root for full details.
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 { doTwoWaySasVerification, awaitVerifier } from "./utils";
import { Client } from "../../pages/client";
@ -38,6 +39,8 @@ test.describe("User verification", () => {
toasts,
room: { roomId: dmRoomId },
}) => {
await waitForDeviceKeys(page);
// once Alice has joined, Bob starts the verification
const bobVerificationRequest = await bob.evaluateHandle(
async (client, { dmRoomId, aliceCredentials }) => {
@ -87,6 +90,8 @@ test.describe("User verification", () => {
toasts,
room: { roomId: dmRoomId },
}) => {
await waitForDeviceKeys(page);
// once Alice has joined, Bob starts the verification
const bobVerificationRequest = await bob.evaluateHandle(
async (client, { dmRoomId, aliceCredentials }) => {
@ -149,3 +154,15 @@ 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,6 +129,7 @@ export class Helpers {
const timelineMessage = this.page.locator(".mx_MTextBody", { hasText: message });
await timelineMessage.click({ button: "right" });
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 ".";
test.describe("Read receipts", () => {
test.describe("Read receipts", { tag: "@mergequeue" }, () => {
test.describe("editing messages", () => {
test.describe("in threads", () => {
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 ".";
test.describe("Read receipts", () => {
test.describe("Read receipts", { tag: "@mergequeue" }, () => {
test.describe("editing messages", () => {
test.describe("in the main timeline", () => {
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 ".";
test.describe("Read receipts", () => {
test.describe("Read receipts", { tag: "@mergequeue" }, () => {
test.describe("editing messages", () => {
test.describe("thread roots", () => {
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 ".";
test.describe("Read receipts", () => {
test.describe("Read receipts", { tag: "@mergequeue" }, () => {
test.describe("Ignored events", () => {
test("If all events after receipt are unimportant, the room is read", async ({
roomAlpha: room1,

View file

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

View file

@ -10,7 +10,7 @@ Please see LICENSE files in the repository root for full details.
import { test } from ".";
test.describe("Read receipts", () => {
test.describe("Read receipts", { tag: "@mergequeue" }, () => {
test.describe("messages with missing referents", () => {
test.fixme(
"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 ".";
test.describe("Read receipts", () => {
test.describe("Read receipts", { tag: "@mergequeue" }, () => {
test.describe("new messages", () => {
test.describe("in threads", () => {
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 ".";
test.describe("Read receipts", () => {
test.describe("Read receipts", { tag: "@mergequeue" }, () => {
test.describe("new messages", () => {
test.describe("in the main timeline", () => {
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 ".";
test.describe("Read receipts", () => {
test.describe("Read receipts", { tag: "@mergequeue" }, () => {
test.describe("new messages", () => {
test.describe("thread roots", () => {
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 ".";
test.describe("Read receipts", () => {
test.describe("Read receipts", { tag: "@mergequeue" }, () => {
test.describe("Notifications", () => {
test.describe("in the main timeline", () => {
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 ".";
test.describe("Read receipts", () => {
test.describe("Read receipts", { tag: "@mergequeue" }, () => {
test.describe("reactions", () => {
test.describe("in threads", () => {
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 ".";
test.describe("Read receipts", () => {
test.describe("Read receipts", { tag: "@mergequeue" }, () => {
test.describe("reactions", () => {
test.describe("in the main timeline", () => {
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 ".";
test.describe("Read receipts", () => {
test.describe("Read receipts", { tag: "@mergequeue" }, () => {
test.describe("reactions", () => {
test.describe("thread roots", () => {
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 { test } from ".";
test.describe("Read receipts", () => {
test.describe("Read receipts", { tag: "@mergequeue" }, () => {
test.use({
displayName: "Mae",
botCreateOpts: { displayName: "Other User" },

View file

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

View file

@ -20,7 +20,7 @@ import { randB64Bytes } from "../../utils/rand";
// 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.
// This digest is updated by the playwright-image-updates.yaml workflow periodically.
const DOCKER_TAG = "develop@sha256:48308e18c5b3ad20bc0d090119618f45b6be4ba727522e37fbf7827d1a109531";
const DOCKER_TAG = "develop@sha256:6b82dba715fa7ae641010b4cc5e71edaeb9cc05a50ac5b9e4ff09afa9cd2a80d";
async function cfgDirFromTemplate(opts: StartHomeserverOpts): Promise<Omit<HomeserverConfig, "dockerUrl">> {
const templateDir = path.join(__dirname, "templates", opts.template);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

Before After
Before After

View file

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

View file

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

View file

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

View file

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

View file

@ -132,6 +132,7 @@ import { SessionLockStolenView } from "./auth/SessionLockStolenView";
import { ConfirmSessionLockTheftView } from "./auth/ConfirmSessionLockTheftView";
import { LoginSplashView } from "./auth/LoginSplashView";
import { cleanUpDraftsIfRequired } from "../../DraftCleaner";
import { InitialCryptoSetupStore } from "../../stores/InitialCryptoSetupStore";
// legacy export
export { default as Views } from "../../Views";
@ -428,6 +429,12 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
!(await shouldSkipSetupEncryption(cli))
) {
// 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 });
} else {
this.onLoggedIn();
@ -2073,14 +2080,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
} else if (this.state.view === Views.COMPLETE_SECURITY) {
view = <CompleteSecurity onFinished={this.onCompleteSecurityE2eSetupFinished} />;
} else if (this.state.view === Views.E2E_SETUP) {
view = (
<E2eSetup
matrixClient={MatrixClientPeg.safeGet()}
onFinished={this.onCompleteSecurityE2eSetupFinished}
accountPassword={this.stores.accountPasswordStore.getPassword()}
tokenLogin={!!this.tokenLogin}
/>
);
view = <E2eSetup onFinished={this.onCompleteSecurityE2eSetupFinished} />;
} else if (this.state.view === Views.LOGGED_IN) {
// `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`,

View file

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

View file

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

View file

@ -235,12 +235,7 @@ export default class SoftLogout extends React.Component<IProps, IState> {
value={this.state.password}
disabled={this.state.busy}
/>
<AccessibleButton
onClick={this.onPasswordLogin}
kind="primary"
type="submit"
disabled={this.state.busy}
>
<AccessibleButton onClick={this.onPasswordLogin} kind="primary" disabled={this.state.busy}>
{_t("action|sign_in")}
</AccessibleButton>
<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> {
protected popupWindow: Window | null;
protected fallbackButton = createRef<HTMLButtonElement>();
protected fallbackButton = createRef<HTMLDivElement>();
public constructor(props: IAuthEntryProps & T) {
super(props);

View file

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

View file

@ -1,99 +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, 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

@ -0,0 +1,71 @@
/*
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>
<AccessibleButton
tabIndex={-1}
alt={_t("spotlight_dialog|remove_filter", {
title={_t("spotlight_dialog|remove_filter", {
filter: filterToLabel(filter),
})}
className="mx_SpotlightDialog_filter--close"

View file

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

View file

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

View file

@ -6,7 +6,15 @@ 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, { ComponentProps, forwardRef, FunctionComponent, HTMLAttributes, InputHTMLAttributes, Ref } from "react";
import React, {
ComponentProps,
ComponentPropsWithoutRef,
forwardRef,
FunctionComponent,
ReactElement,
KeyboardEvent,
Ref,
} from "react";
import classnames from "classnames";
import { Tooltip } from "@vector-im/compound-web";
@ -38,20 +46,8 @@ export type AccessibleButtonKind =
| "icon_primary"
| "icon_primary_outline";
/**
* 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 ElementType = keyof HTMLElementTagNameMap;
const defaultElement = "div";
type TooltipProps = ComponentProps<typeof Tooltip>;
@ -60,7 +56,7 @@ type TooltipProps = ComponentProps<typeof Tooltip>;
*
* Extends props accepted by the underlying element specified using the `element` prop.
*/
type Props<T extends keyof JSX.IntrinsicElements> = DynamicHtmlElementProps<T> & {
type Props<T extends ElementType = "div"> = {
/**
* The base element type. "div" by default.
*/
@ -105,14 +101,12 @@ type Props<T extends keyof JSX.IntrinsicElements> = DynamicHtmlElementProps<T> &
disableTooltip?: TooltipProps["disabled"];
};
export type ButtonProps<T extends keyof JSX.IntrinsicElements> = Props<T>;
export type ButtonProps<T extends ElementType> = Props<T> & Omit<ComponentPropsWithoutRef<T>, keyof Props<T>>;
/**
* Type of the props passed to the element that is rendered by AccessibleButton.
*/
interface RenderedElementProps extends React.InputHTMLAttributes<Element> {
ref?: React.Ref<Element>;
}
type RenderedElementProps<T extends ElementType> = React.InputHTMLAttributes<Element> & RefProp<T>;
/**
* AccessibleButton is a generic wrapper for any element that should be treated
@ -124,9 +118,9 @@ interface RenderedElementProps extends React.InputHTMLAttributes<Element> {
* @param {Object} props react element properties
* @returns {Object} rendered react
*/
const AccessibleButton = forwardRef(function <T extends keyof JSX.IntrinsicElements>(
const AccessibleButton = forwardRef(function <T extends ElementType = typeof defaultElement>(
{
element = "div" as T,
element,
onClick,
children,
kind,
@ -141,10 +135,10 @@ const AccessibleButton = forwardRef(function <T extends keyof JSX.IntrinsicEleme
onTooltipOpenChange,
disableTooltip,
...restProps
}: Props<T>,
ref: Ref<HTMLElement>,
}: ButtonProps<T>,
ref: Ref<HTMLElementTagNameMap[T]>,
): JSX.Element {
const newProps: RenderedElementProps = restProps;
const newProps = restProps as RenderedElementProps<T>;
newProps["aria-label"] = newProps["aria-label"] ?? title;
if (disabled) {
newProps["aria-disabled"] = true;
@ -162,7 +156,7 @@ const AccessibleButton = forwardRef(function <T extends keyof JSX.IntrinsicEleme
// 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
// inconsistencies here
newProps.onKeyDown = (e) => {
newProps.onKeyDown = (e: KeyboardEvent<never>) => {
const action = getKeyBindingsManager().getAccessibilityAction(e);
switch (action) {
@ -178,7 +172,7 @@ const AccessibleButton = forwardRef(function <T extends keyof JSX.IntrinsicEleme
onKeyDown?.(e);
}
};
newProps.onKeyUp = (e) => {
newProps.onKeyUp = (e: KeyboardEvent<never>) => {
const action = getKeyBindingsManager().getAccessibilityAction(e);
switch (action) {
@ -207,7 +201,7 @@ const AccessibleButton = forwardRef(function <T extends keyof JSX.IntrinsicEleme
});
// React.createElement expects InputHTMLAttributes
const button = React.createElement(element, newProps, children);
const button = React.createElement(element ?? defaultElement, newProps, children);
if (title) {
return (
@ -233,4 +227,15 @@ const AccessibleButton = forwardRef(function <T extends keyof JSX.IntrinsicEleme
};
(AccessibleButton as FunctionComponent).displayName = "AccessibleButton";
export default AccessibleButton;
interface RefProp<T extends ElementType> {
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,12 +133,7 @@ export default class EditableItemList<P = {}> extends React.PureComponent<IProps
onChange={this.onNewItemChanged}
list={this.props.suggestionsListId}
/>
<AccessibleButton
onClick={this.onItemAdded}
kind="primary"
type="submit"
disabled={!this.props.newItem}
>
<AccessibleButton onClick={this.onItemAdded} kind="primary" disabled={!this.props.newItem}>
{_t("action|add")}
</AccessibleButton>
</form>

View file

@ -31,7 +31,7 @@ class Emoji extends React.PureComponent<IProps> {
return (
<RovingAccessibleButton
id={this.props.id}
onClick={(ev) => onClick(ev, emoji)}
onClick={(ev: ButtonEvent) => onClick(ev, emoji)}
onMouseEnter={() => onMouseEnter(emoji)}
onMouseLeave={() => onMouseLeave(emoji)}
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);
if (!pollStartEvent) {
const pollEndFallbackMessage = M_TEXT.findIn(mxEvent.getContent()) || textForEvent(mxEvent, cli);
const pollEndFallbackMessage = M_TEXT.findIn<string>(mxEvent.getContent()) || textForEvent(mxEvent, cli);
return (
<>
<PollIcon className="mx_MPollEndBody_icon" />

View file

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

View file

@ -6,10 +6,10 @@
* Please see LICENSE files in the repository root for full details.
*/
import React, { JSX, useEffect, useState } from "react";
import React, { JSX, useEffect, useRef, useState } from "react";
import PinIcon from "@vector-im/compound-design-tokens/assets/web/icons/pin-solid";
import { Button } from "@vector-im/compound-web";
import { Room } from "matrix-js-sdk/src/matrix";
import { MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
import classNames from "classnames";
import { usePinnedEvents, useSortedFetchedPinnedEvents } from "../../../hooks/usePinnedEvents";
@ -25,6 +25,7 @@ import { Action } from "../../../dispatcher/actions";
import MessageEvent from "../messages/MessageEvent";
import PosthogTrackers from "../../../PosthogTrackers.ts";
import { EventPreview } from "./EventPreview.tsx";
import ResizeNotifier from "../../../utils/ResizeNotifier";
/**
* The props for the {@link PinnedMessageBanner} component.
@ -38,12 +39,20 @@ interface PinnedMessageBannerProps {
* The room where the banner is displayed
*/
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.
*/
export function PinnedMessageBanner({ room, permalinkCreator }: PinnedMessageBannerProps): JSX.Element | null {
export function PinnedMessageBanner({
room,
permalinkCreator,
resizeNotifier,
}: PinnedMessageBannerProps): JSX.Element | null {
const pinnedEventIds = usePinnedEvents(room);
const pinnedEvents = useSortedFetchedPinnedEvents(room, pinnedEventIds);
const eventCount = pinnedEvents.length;
@ -56,6 +65,8 @@ export function PinnedMessageBanner({ room, permalinkCreator }: PinnedMessageBan
}, [eventCount]);
const pinnedEvent = pinnedEvents[currentEventIndex];
useNotifyTimeline(pinnedEvent, resizeNotifier);
if (!pinnedEvent) return null;
const shouldUseMessageEvent = pinnedEvent.isRedacted() || pinnedEvent.isDecryptionFailure();
@ -128,6 +139,23 @@ export function PinnedMessageBanner({ room, permalinkCreator }: PinnedMessageBan
);
}
/**
* 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;
/**

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.
*/
import React, { ReactNode, createRef, useCallback, useEffect, useState } from "react";
import React, { ReactNode, createRef, useCallback, useEffect, useState, useId } from "react";
import EditIcon from "@vector-im/compound-design-tokens/assets/web/icons/edit";
import UploadIcon from "@vector-im/compound-design-tokens/assets/web/icons/share";
import DeleteIcon from "@vector-im/compound-design-tokens/assets/web/icons/delete";
@ -16,7 +16,6 @@ import classNames from "classnames";
import { _t } from "../../../languageHandler";
import { mediaFromMxc } from "../../../customisations/Media";
import { chromeFileInputFix } from "../../../utils/BrowserWorkarounds";
import { useId } from "../../../utils/useId";
import AccessibleButton from "../elements/AccessibleButton";
import BaseAvatar from "../avatars/BaseAvatar";
import Modal from "../../../Modal.tsx";

View file

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

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.
*/
import React, { ChangeEvent, ReactNode, useCallback, useEffect, useMemo, useState } from "react";
import React, { ChangeEvent, ReactNode, useCallback, useEffect, useMemo, useState, useId } from "react";
import { logger } from "matrix-js-sdk/src/logger";
import { EditInPlace, Alert, ErrorMessage } from "@vector-im/compound-web";
import PopOutIcon from "@vector-im/compound-design-tokens/assets/web/icons/pop-out";
@ -20,7 +20,6 @@ import { formatBytes } from "../../../utils/FormattingUtils";
import { useToastContext } from "../../../contexts/ToastContext";
import InlineSpinner from "../elements/InlineSpinner";
import UserIdentifierCustomisations from "../../../customisations/UserIdentifier";
import { useId } from "../../../utils/useId";
import CopyableText from "../elements/CopyableText";
import { useMatrixClientContext } from "../../../contexts/MatrixClientContext";
import AccessibleButton from "../elements/AccessibleButton";

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -398,7 +398,7 @@
},
"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.",
"before_submitting": "Before submitting logs, you must <a>create a GitHub issue</a> to describe your problem.",
"before_submitting": "We recommend <a>creating a GitHub issue</a> to ensure that your report is reviewed.",
"collecting_information": "Collecting app version information",
"collecting_logs": "Collecting logs",
"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?",
"sign_in_description": "Użyj swojego konta, aby kontynuować.",
"sign_in_instead": "Zamiast tego zaloguj się",
"sign_in_instead_prompt": "Zamiast tego zaloguj się",
"sign_in_instead_prompt": "Masz już konto? <a>Zaloguj się tutaj</a>",
"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_prompt": "Posiadasz już konto? <a>Zaloguj się</a>",
@ -505,6 +505,7 @@
"matrix": "Matrix",
"message": "Wiadomość",
"message_layout": "Wygląd wiadomości",
"message_timestamp_invalid": "Nieprawidłowy znacznik czasu",
"microphone": "Mikrofon",
"model": "Model",
"modern": "Współczesny",
@ -908,6 +909,8 @@
"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>",
"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": {
"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.",
@ -996,7 +999,7 @@
"unverified_sessions_toast_description": "Sprawdź, by upewnić się że Twoje konto jest bezpieczne",
"unverified_sessions_toast_reject": "Później",
"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.",
"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_dialog_title_device": "Zweryfikuj drugie urządzenie",
"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.",
@ -1102,7 +1105,15 @@
"you": "Dodano reakcję %(reaction)s do %(message)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": {
"cancelled": "Eksport został anulowany",
@ -2939,6 +2950,7 @@
"warning": "<w>OSTRZEŻENIE:</w> <description/>"
},
"share": {
"link_copied": "Skopiowano link",
"permalink_message": "Link do zaznaczonej wiadomości",
"permalink_most_recent": "Link do najnowszej wiadomości",
"share_call": "Link zaproszenia do konferencji",
@ -3247,8 +3259,8 @@
"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_user_not_joined": "Nie masz dostępu do tej wiadomości",
"sender_identity_previously_verified": "Zweryfikowana tożsamość uległa zmianie",
"sender_unsigned_device": "Zaszyfrowano przez urządzenie niezweryfikowane przez właściciela.",
"sender_identity_previously_verified": "Zweryfikowana tożsamość nadawcy uległa zmianie",
"sender_unsigned_device": "Wysłano z niezabezpieczonego urządzenia.",
"unable_to_decrypt": "Nie można rozszyfrować wiadomości"
},
"disambiguated_profile": "%(displayName)s (%(matrixId)s)",
@ -3720,6 +3732,7 @@
"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_title": "Błąd wysyłania",
"not_image": "Wybrany plik nie jest prawidłowym plikiem obrazu.",
"title": "Prześlij pliki",
"title_progress": "Prześlij pliki (%(current)s z %(total)s)",
"upload_all_button": "Prześlij wszystko",

View file

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

View file

@ -33,6 +33,11 @@ export enum Phase {
ConfirmReset = 6,
}
/**
* 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 {
private started?: boolean;
public phase?: Phase;

View file

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

View file

@ -1,16 +0,0 @@
/*
Copyright 2024 New Vector Ltd.
Copyright 2024 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";
const getUniqueId = (() => {
return () => `:r${Math.random()}:`;
})();
// Replace this with React's own useId once we switch to React 18
export const useId = (): string => React.useMemo(getUniqueId, []);

View file

@ -1,131 +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, 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

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

View file

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

View file

@ -20,6 +20,7 @@ import RightPanelStore from "../../../../../src/stores/right-panel/RightPanelSto
import { RightPanelPhases } from "../../../../../src/stores/right-panel/RightPanelStorePhases";
import { UPDATE_EVENT } from "../../../../../src/stores/AsyncStore";
import { Action } from "../../../../../src/dispatcher/actions";
import ResizeNotifier from "../../../../../src/utils/ResizeNotifier.ts";
describe("<PinnedMessageBanner />", () => {
const userId = "@alice:server.org";
@ -28,10 +29,12 @@ describe("<PinnedMessageBanner />", () => {
let mockClient: MatrixClient;
let room: Room;
let permalinkCreator: RoomPermalinkCreator;
let resizeNotifier: ResizeNotifier;
beforeEach(() => {
mockClient = stubClient();
room = new Room(roomId, mockClient, userId);
permalinkCreator = new RoomPermalinkCreator(room);
resizeNotifier = new ResizeNotifier();
jest.spyOn(dis, "dispatch").mockReturnValue(undefined);
});
@ -77,7 +80,7 @@ describe("<PinnedMessageBanner />", () => {
*/
function renderBanner() {
return render(
<PinnedMessageBanner permalinkCreator={permalinkCreator} room={room} />,
<PinnedMessageBanner permalinkCreator={permalinkCreator} room={room} resizeNotifier={resizeNotifier} />,
withClientContextRenderOptions(mockClient),
);
}
@ -145,7 +148,9 @@ describe("<PinnedMessageBanner />", () => {
event3.getId()!,
]);
jest.spyOn(pinnedEventHooks, "useSortedFetchedPinnedEvents").mockReturnValue([event1, event2, event3]);
rerender(<PinnedMessageBanner permalinkCreator={permalinkCreator} room={room} />);
rerender(
<PinnedMessageBanner permalinkCreator={permalinkCreator} room={room} resizeNotifier={resizeNotifier} />,
);
await expect(screen.findByText("Third pinned message")).resolves.toBeVisible();
expect(asFragment()).toMatchSnapshot();
});
@ -206,6 +211,42 @@ describe("<PinnedMessageBanner />", () => {
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", () => {
beforeEach(() => {
jest.spyOn(pinnedEventHooks, "usePinnedEvents").mockReturnValue([event1.getId()!, event2.getId()!]);
@ -217,6 +258,8 @@ describe("<PinnedMessageBanner />", () => {
jest.spyOn(RightPanelStore.instance, "isOpenForRoom").mockReturnValue(false);
renderBanner();
await expect(screen.findByText("Second pinned message")).resolves.toBeVisible();
expect(screen.getByRole("button", { name: "View all" })).toBeVisible();
});
@ -228,6 +271,8 @@ describe("<PinnedMessageBanner />", () => {
});
renderBanner();
await expect(screen.findByText("Second pinned message")).resolves.toBeVisible();
expect(screen.getByRole("button", { name: "View all" })).toBeVisible();
});
@ -239,6 +284,8 @@ describe("<PinnedMessageBanner />", () => {
});
renderBanner();
await expect(screen.findByText("Second pinned message")).resolves.toBeVisible();
expect(screen.getByRole("button", { name: "Close list" })).toBeVisible();
});
@ -263,6 +310,7 @@ describe("<PinnedMessageBanner />", () => {
});
renderBanner();
await expect(screen.findByText("Second pinned message")).resolves.toBeVisible();
expect(screen.getByRole("button", { name: "Close list" })).toBeVisible();
jest.spyOn(RightPanelStore.instance, "isOpenForRoom").mockReturnValue(false);

View file

@ -10,6 +10,7 @@ import React, { ComponentProps } from "react";
import { render, screen, waitFor } from "jest-matrix-react";
import { RoomMember } from "matrix-js-sdk/src/matrix";
import userEvent from "@testing-library/user-event";
import { mocked } from "jest-mock";
import {
determineAvatarPosition,
@ -20,6 +21,9 @@ import * as languageHandler from "../../../../../src/languageHandler";
import { stubClient } from "../../../../test-utils";
import dispatcher from "../../../../../src/dispatcher/dispatcher";
import { Action } from "../../../../../src/dispatcher/actions";
import { formatDate } from "../../../../../src/DateUtils";
jest.mock("../../../../../src/DateUtils");
describe("ReadReceiptGroup", () => {
describe("TooltipText", () => {
@ -87,6 +91,10 @@ describe("ReadReceiptGroup", () => {
describe("<ReadReceiptPerson />", () => {
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 USER_ID = "@alice:example.org";

View file

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

View file

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

View file

@ -19,14 +19,14 @@ exports[`<LayoutSwitcher /> should render 1`] = `
class="mx_SettingsSubsection_content mx_SettingsSubsection_content_newUi"
>
<form
class="_root_dgy0u_24 mx_LayoutSwitcher_LayoutSelector"
class="_root_ssths_24 mx_LayoutSwitcher_LayoutSelector"
>
<div
class="_field_dgy0u_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio"
class="_field_ssths_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio"
>
<label
aria-label="Modern"
class="_label_dgy0u_67"
class="_label_ssths_67"
for="radix-:r0:"
>
<div
@ -149,11 +149,11 @@ exports[`<LayoutSwitcher /> should render 1`] = `
</label>
</div>
<div
class="_field_dgy0u_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio"
class="_field_ssths_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio"
>
<label
aria-label="Message bubbles"
class="_label_dgy0u_67"
class="_label_ssths_67"
for="radix-:r9:"
>
<div
@ -275,11 +275,11 @@ exports[`<LayoutSwitcher /> should render 1`] = `
</label>
</div>
<div
class="_field_dgy0u_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio"
class="_field_ssths_34 mxLayoutSwitcher_LayoutSelector_LayoutRadio"
>
<label
aria-label="IRC (experimental)"
class="_label_dgy0u_67"
class="_label_ssths_67"
for="radix-:ri:"
>
<div
@ -402,13 +402,13 @@ exports[`<LayoutSwitcher /> should render 1`] = `
</div>
</form>
<form
class="_root_dgy0u_24"
class="_root_ssths_24"
>
<div
class="_inline-field_dgy0u_40"
class="_inline-field_ssths_40"
>
<div
class="_inline-field-control_dgy0u_52"
class="_inline-field-control_ssths_52"
>
<div
class="_container_qnvru_18"
@ -427,16 +427,16 @@ exports[`<LayoutSwitcher /> should render 1`] = `
</div>
</div>
<div
class="_inline-field-body_dgy0u_46"
class="_inline-field-body_ssths_46"
>
<label
class="_label_dgy0u_67"
class="_label_ssths_67"
for="radix-:rr:"
>
Show compact text and messages
</label>
<span
class="_message_dgy0u_98 _help-message_dgy0u_104"
class="_message_ssths_93 _help-message_ssths_99"
id="radix-:rs:"
>
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"
>
<form
class="_root_dgy0u_24"
class="_root_ssths_24"
>
<div
class="_inline-field_dgy0u_40"
class="_inline-field_ssths_40"
>
<div
class="_inline-field-control_dgy0u_52"
class="_inline-field-control_ssths_52"
>
<div
class="_container_qnvru_18"
@ -43,10 +43,10 @@ exports[`<ThemeChoicePanel /> custom theme should display custom theme 1`] = `
</div>
</div>
<div
class="_inline-field-body_dgy0u_46"
class="_inline-field-body_ssths_46"
>
<label
class="_label_dgy0u_67"
class="_label_ssths_67"
for="radix-:r28:"
>
Match system theme
@ -55,13 +55,13 @@ exports[`<ThemeChoicePanel /> custom theme should display custom theme 1`] = `
</div>
</form>
<form
class="_root_dgy0u_24 mx_ThemeChoicePanel_ThemeSelectors"
class="_root_ssths_24 mx_ThemeChoicePanel_ThemeSelectors"
>
<div
class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_enabled cpd-theme-light"
class="_inline-field_ssths_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_enabled cpd-theme-light"
>
<div
class="_inline-field-control_dgy0u_52"
class="_inline-field-control_ssths_52"
>
<div
class="_container_1vw5h_18"
@ -81,10 +81,10 @@ exports[`<ThemeChoicePanel /> custom theme should display custom theme 1`] = `
</div>
</div>
<div
class="_inline-field-body_dgy0u_46"
class="_inline-field-body_ssths_46"
>
<label
class="_label_dgy0u_67 mx_ThemeChoicePanel_themeSelector_Label"
class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label"
for="radix-:r29:"
>
Light
@ -92,10 +92,10 @@ exports[`<ThemeChoicePanel /> custom theme should display custom theme 1`] = `
</div>
</div>
<div
class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark"
class="_inline-field_ssths_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark"
>
<div
class="_inline-field-control_dgy0u_52"
class="_inline-field-control_ssths_52"
>
<div
class="_container_1vw5h_18"
@ -114,10 +114,10 @@ exports[`<ThemeChoicePanel /> custom theme should display custom theme 1`] = `
</div>
</div>
<div
class="_inline-field-body_dgy0u_46"
class="_inline-field-body_ssths_46"
>
<label
class="_label_dgy0u_67 mx_ThemeChoicePanel_themeSelector_Label"
class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label"
for="radix-:r2a:"
>
Dark
@ -125,10 +125,10 @@ exports[`<ThemeChoicePanel /> custom theme should display custom theme 1`] = `
</div>
</div>
<div
class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector cpd-theme-light"
class="_inline-field_ssths_40 mx_ThemeChoicePanel_themeSelector cpd-theme-light"
>
<div
class="_inline-field-control_dgy0u_52"
class="_inline-field-control_ssths_52"
>
<div
class="_container_1vw5h_18"
@ -147,10 +147,10 @@ exports[`<ThemeChoicePanel /> custom theme should display custom theme 1`] = `
</div>
</div>
<div
class="_inline-field-body_dgy0u_46"
class="_inline-field-body_ssths_46"
>
<label
class="_label_dgy0u_67 mx_ThemeChoicePanel_themeSelector_Label"
class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label"
for="radix-:r2b:"
>
High contrast
@ -158,10 +158,10 @@ exports[`<ThemeChoicePanel /> custom theme should display custom theme 1`] = `
</div>
</div>
<div
class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark"
class="_inline-field_ssths_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark"
>
<div
class="_inline-field-control_dgy0u_52"
class="_inline-field-control_ssths_52"
>
<div
class="_container_1vw5h_18"
@ -180,10 +180,10 @@ exports[`<ThemeChoicePanel /> custom theme should display custom theme 1`] = `
</div>
</div>
<div
class="_inline-field-body_dgy0u_46"
class="_inline-field-body_ssths_46"
>
<label
class="_label_dgy0u_67 mx_ThemeChoicePanel_themeSelector_Label"
class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label"
for="radix-:r2c:"
>
Alice theme
@ -195,13 +195,13 @@ exports[`<ThemeChoicePanel /> custom theme should display custom theme 1`] = `
class="mx_ThemeChoicePanel_CustomTheme"
>
<form
class="_root_dgy0u_24 mx_ThemeChoicePanel_CustomTheme_EditInPlace"
class="_root_ssths_24 mx_ThemeChoicePanel_CustomTheme_EditInPlace"
>
<div
class="_field_dgy0u_34"
class="_field_ssths_34"
>
<label
class="_label_dgy0u_67"
class="_label_ssths_67"
for="radix-:r2d:"
>
Add custom theme
@ -219,7 +219,7 @@ exports[`<ThemeChoicePanel /> custom theme should display custom theme 1`] = `
/>
</div>
<span
class="_message_dgy0u_98 _help-message_dgy0u_104"
class="_message_ssths_93 _help-message_ssths_99"
id="radix-:r2e:"
>
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"
>
<form
class="_root_dgy0u_24"
class="_root_ssths_24"
>
<div
class="_inline-field_dgy0u_40"
class="_inline-field_ssths_40"
>
<div
class="_inline-field-control_dgy0u_52"
class="_inline-field-control_ssths_52"
>
<div
class="_container_qnvru_18"
@ -320,10 +320,10 @@ exports[`<ThemeChoicePanel /> custom theme should render the custom theme sectio
</div>
</div>
<div
class="_inline-field-body_dgy0u_46"
class="_inline-field-body_ssths_46"
>
<label
class="_label_dgy0u_67"
class="_label_ssths_67"
for="radix-:r10:"
>
Match system theme
@ -332,13 +332,13 @@ exports[`<ThemeChoicePanel /> custom theme should render the custom theme sectio
</div>
</form>
<form
class="_root_dgy0u_24 mx_ThemeChoicePanel_ThemeSelectors"
class="_root_ssths_24 mx_ThemeChoicePanel_ThemeSelectors"
>
<div
class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_enabled cpd-theme-light"
class="_inline-field_ssths_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_enabled cpd-theme-light"
>
<div
class="_inline-field-control_dgy0u_52"
class="_inline-field-control_ssths_52"
>
<div
class="_container_1vw5h_18"
@ -358,10 +358,10 @@ exports[`<ThemeChoicePanel /> custom theme should render the custom theme sectio
</div>
</div>
<div
class="_inline-field-body_dgy0u_46"
class="_inline-field-body_ssths_46"
>
<label
class="_label_dgy0u_67 mx_ThemeChoicePanel_themeSelector_Label"
class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label"
for="radix-:r11:"
>
Light
@ -369,10 +369,10 @@ exports[`<ThemeChoicePanel /> custom theme should render the custom theme sectio
</div>
</div>
<div
class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark"
class="_inline-field_ssths_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark"
>
<div
class="_inline-field-control_dgy0u_52"
class="_inline-field-control_ssths_52"
>
<div
class="_container_1vw5h_18"
@ -391,10 +391,10 @@ exports[`<ThemeChoicePanel /> custom theme should render the custom theme sectio
</div>
</div>
<div
class="_inline-field-body_dgy0u_46"
class="_inline-field-body_ssths_46"
>
<label
class="_label_dgy0u_67 mx_ThemeChoicePanel_themeSelector_Label"
class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label"
for="radix-:r12:"
>
Dark
@ -402,10 +402,10 @@ exports[`<ThemeChoicePanel /> custom theme should render the custom theme sectio
</div>
</div>
<div
class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector cpd-theme-light"
class="_inline-field_ssths_40 mx_ThemeChoicePanel_themeSelector cpd-theme-light"
>
<div
class="_inline-field-control_dgy0u_52"
class="_inline-field-control_ssths_52"
>
<div
class="_container_1vw5h_18"
@ -424,10 +424,10 @@ exports[`<ThemeChoicePanel /> custom theme should render the custom theme sectio
</div>
</div>
<div
class="_inline-field-body_dgy0u_46"
class="_inline-field-body_ssths_46"
>
<label
class="_label_dgy0u_67 mx_ThemeChoicePanel_themeSelector_Label"
class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label"
for="radix-:r13:"
>
High contrast
@ -435,10 +435,10 @@ exports[`<ThemeChoicePanel /> custom theme should render the custom theme sectio
</div>
</div>
<div
class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark"
class="_inline-field_ssths_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark"
>
<div
class="_inline-field-control_dgy0u_52"
class="_inline-field-control_ssths_52"
>
<div
class="_container_1vw5h_18"
@ -457,10 +457,10 @@ exports[`<ThemeChoicePanel /> custom theme should render the custom theme sectio
</div>
</div>
<div
class="_inline-field-body_dgy0u_46"
class="_inline-field-body_ssths_46"
>
<label
class="_label_dgy0u_67 mx_ThemeChoicePanel_themeSelector_Label"
class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label"
for="radix-:r14:"
>
Alice theme
@ -472,13 +472,13 @@ exports[`<ThemeChoicePanel /> custom theme should render the custom theme sectio
class="mx_ThemeChoicePanel_CustomTheme"
>
<form
class="_root_dgy0u_24 mx_ThemeChoicePanel_CustomTheme_EditInPlace"
class="_root_ssths_24 mx_ThemeChoicePanel_CustomTheme_EditInPlace"
>
<div
class="_field_dgy0u_34"
class="_field_ssths_34"
>
<label
class="_label_dgy0u_67"
class="_label_ssths_67"
for="radix-:r15:"
>
Add custom theme
@ -496,7 +496,7 @@ exports[`<ThemeChoicePanel /> custom theme should render the custom theme sectio
/>
</div>
<span
class="_message_dgy0u_98 _help-message_dgy0u_104"
class="_message_ssths_93 _help-message_ssths_99"
id="radix-:r16:"
>
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"
>
<form
class="_root_dgy0u_24"
class="_root_ssths_24"
>
<div
class="_inline-field_dgy0u_40"
class="_inline-field_ssths_40"
>
<div
class="_inline-field-control_dgy0u_52"
class="_inline-field-control_ssths_52"
>
<div
class="_container_qnvru_18"
@ -597,10 +597,10 @@ exports[`<ThemeChoicePanel /> renders the theme choice UI 1`] = `
</div>
</div>
<div
class="_inline-field-body_dgy0u_46"
class="_inline-field-body_ssths_46"
>
<label
class="_label_dgy0u_67"
class="_label_ssths_67"
for="radix-:r0:"
>
Match system theme
@ -609,13 +609,13 @@ exports[`<ThemeChoicePanel /> renders the theme choice UI 1`] = `
</div>
</form>
<form
class="_root_dgy0u_24 mx_ThemeChoicePanel_ThemeSelectors"
class="_root_ssths_24 mx_ThemeChoicePanel_ThemeSelectors"
>
<div
class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_enabled cpd-theme-light"
class="_inline-field_ssths_40 mx_ThemeChoicePanel_themeSelector mx_ThemeChoicePanel_themeSelector_enabled cpd-theme-light"
>
<div
class="_inline-field-control_dgy0u_52"
class="_inline-field-control_ssths_52"
>
<div
class="_container_1vw5h_18"
@ -635,10 +635,10 @@ exports[`<ThemeChoicePanel /> renders the theme choice UI 1`] = `
</div>
</div>
<div
class="_inline-field-body_dgy0u_46"
class="_inline-field-body_ssths_46"
>
<label
class="_label_dgy0u_67 mx_ThemeChoicePanel_themeSelector_Label"
class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label"
for="radix-:r1:"
>
Light
@ -646,10 +646,10 @@ exports[`<ThemeChoicePanel /> renders the theme choice UI 1`] = `
</div>
</div>
<div
class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark"
class="_inline-field_ssths_40 mx_ThemeChoicePanel_themeSelector cpd-theme-dark"
>
<div
class="_inline-field-control_dgy0u_52"
class="_inline-field-control_ssths_52"
>
<div
class="_container_1vw5h_18"
@ -668,10 +668,10 @@ exports[`<ThemeChoicePanel /> renders the theme choice UI 1`] = `
</div>
</div>
<div
class="_inline-field-body_dgy0u_46"
class="_inline-field-body_ssths_46"
>
<label
class="_label_dgy0u_67 mx_ThemeChoicePanel_themeSelector_Label"
class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label"
for="radix-:r2:"
>
Dark
@ -679,10 +679,10 @@ exports[`<ThemeChoicePanel /> renders the theme choice UI 1`] = `
</div>
</div>
<div
class="_inline-field_dgy0u_40 mx_ThemeChoicePanel_themeSelector cpd-theme-light"
class="_inline-field_ssths_40 mx_ThemeChoicePanel_themeSelector cpd-theme-light"
>
<div
class="_inline-field-control_dgy0u_52"
class="_inline-field-control_ssths_52"
>
<div
class="_container_1vw5h_18"
@ -701,10 +701,10 @@ exports[`<ThemeChoicePanel /> renders the theme choice UI 1`] = `
</div>
</div>
<div
class="_inline-field-body_dgy0u_46"
class="_inline-field-body_ssths_46"
>
<label
class="_label_dgy0u_67 mx_ThemeChoicePanel_themeSelector_Label"
class="_label_ssths_67 mx_ThemeChoicePanel_themeSelector_Label"
for="radix-:r3:"
>
High contrast

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -187,18 +187,6 @@ module.exports = (env, argv) => {
},
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
extensions: [".js", ".json", ".ts", ".tsx"],
alias: {

407
yarn.lock
View file

@ -34,7 +34,7 @@
dependencies:
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.0.0", "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0", "@babel/code-frame@^7.26.2":
version "7.26.2"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85"
integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==
@ -51,11 +51,16 @@
"@babel/highlight" "^7.25.7"
picocolors "^1.0.0"
"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.25.9", "@babel/compat-data@^7.26.0":
"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.26.0":
version "7.26.2"
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.2.tgz#278b6b13664557de95b8f35b90d96785850bb56e"
integrity sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==
"@babel/compat-data@^7.25.9":
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"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.0.tgz#d78b6023cc8f3114ccf049eb219613f74a747b40"
@ -103,13 +108,13 @@
"@jridgewell/trace-mapping" "^0.3.25"
jsesc "^3.0.2"
"@babel/generator@^7.25.9", "@babel/generator@^7.26.0":
version "7.26.2"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.2.tgz#87b75813bec87916210e5e01939a4c823d6bb74f"
integrity sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==
"@babel/generator@^7.26.0", "@babel/generator@^7.26.3":
version "7.26.3"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.3.tgz#ab8d4360544a425c90c248df7059881f4b2ce019"
integrity sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==
dependencies:
"@babel/parser" "^7.26.2"
"@babel/types" "^7.26.0"
"@babel/parser" "^7.26.3"
"@babel/types" "^7.26.3"
"@jridgewell/gen-mapping" "^0.3.5"
"@jridgewell/trace-mapping" "^0.3.25"
jsesc "^3.0.2"
@ -303,12 +308,12 @@
dependencies:
"@babel/types" "^7.25.8"
"@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==
"@babel/parser@^7.25.9", "@babel/parser@^7.26.0", "@babel/parser@^7.26.3":
version "7.26.3"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.3.tgz#8c51c5db6ddf08134af1ddbacf16aaab48bac234"
integrity sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==
dependencies:
"@babel/types" "^7.26.0"
"@babel/types" "^7.26.3"
"@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.25.9":
version "7.25.9"
@ -1060,9 +1065,9 @@
esutils "^2.0.2"
"@babel/preset-react@^7.12.10", "@babel/preset-react@^7.18.6":
version "7.25.9"
resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.25.9.tgz#5f473035dc2094bcfdbc7392d0766bd42dce173e"
integrity sha512-D3to0uSPiWE7rBrdIICCd0tJSIGpLaaGptna2+w7Pft5xMqLpA1sz99DK5TZ1TjGbdQ/VI1eCSZ06dv3lT4JOw==
version "7.26.3"
resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.26.3.tgz#7c5e028d623b4683c1f83a0bd4713b9100560caa"
integrity sha512-Nl03d6T9ky516DGK2YMxrTqvnpUW63TnJMOMonj+Zae0JiPC5BC9xPMSL6L8fiSpA5vP88qfygavVQvnLp+6Cw==
dependencies:
"@babel/helper-plugin-utils" "^7.25.9"
"@babel/helper-validator-option" "^7.25.9"
@ -1121,15 +1126,15 @@
globals "^11.1.0"
"@babel/traverse@^7.25.9":
version "7.25.9"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.25.9.tgz#a50f8fe49e7f69f53de5bea7e413cd35c5e13c84"
integrity sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==
version "7.26.4"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.4.tgz#ac3a2a84b908dde6d463c3bfa2c5fdc1653574bd"
integrity sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==
dependencies:
"@babel/code-frame" "^7.25.9"
"@babel/generator" "^7.25.9"
"@babel/parser" "^7.25.9"
"@babel/code-frame" "^7.26.2"
"@babel/generator" "^7.26.3"
"@babel/parser" "^7.26.3"
"@babel/template" "^7.25.9"
"@babel/types" "^7.25.9"
"@babel/types" "^7.26.3"
debug "^4.3.1"
globals "^11.1.0"
@ -1142,7 +1147,7 @@
"@babel/helper-validator-identifier" "^7.25.7"
to-fast-properties "^2.0.0"
"@babel/types@^7.25.7", "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.4.4":
"@babel/types@^7.25.7", "@babel/types@^7.4.4":
version "7.26.0"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.0.tgz#deabd08d6b753bc8e0f198f8709fb575e31774ff"
integrity sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==
@ -1150,6 +1155,14 @@
"@babel/helper-string-parser" "^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":
version "7.26.3"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.3.tgz#37e79830f04c2b5687acc77db97fbc75fb81f3c0"
integrity sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==
dependencies:
"@babel/helper-string-parser" "^7.25.9"
"@babel/helper-validator-identifier" "^7.25.9"
"@bcoe/v8-coverage@^0.2.3":
version "0.2.3"
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
@ -1571,10 +1584,10 @@
dependencies:
"@floating-ui/dom" "^1.0.0"
"@floating-ui/react@^0.26.24":
version "0.26.25"
resolved "https://registry.yarnpkg.com/@floating-ui/react/-/react-0.26.25.tgz#cf4c8a2b89fab1a71712d15e6551df3bfbd2ea1d"
integrity sha512-hZOmgN0NTOzOuZxI1oIrDu3Gcl8WViIkvPMpB4xdd4QD6xAMtwgwr3VPoiyH/bLtRcS1cDnhxLSD1NsMJmwh/A==
"@floating-ui/react@^0.27.0":
version "0.27.0"
resolved "https://registry.yarnpkg.com/@floating-ui/react/-/react-0.27.0.tgz#e0931fd09374ab4b8ce1a1af5cb44d1ccd1bb95a"
integrity sha512-WLEksq7fJapXSJbmfiyq9pAW0a7ZFMEJToFE4oTDESxGjoa+nZu3YMjmZE2KvoUtQhqOK2yMMfWQFZyeWD0wGQ==
dependencies:
"@floating-ui/react-dom" "^2.1.2"
"@floating-ui/utils" "^0.2.8"
@ -1595,36 +1608,37 @@
resolved "https://registry.yarnpkg.com/@fontsource/inter/-/inter-5.1.0.tgz#ab629b2c662457022d2d6a29854b8dc8ba538c47"
integrity sha512-zKZR3kf1G0noIes1frLfOHP5EXVVm0M7sV/l9f/AaYf+M/DId35FO4LkigWjqWYjTJZGgplhdv4cB+ssvCqr5A==
"@formatjs/ecma402-abstract@2.2.4":
version "2.2.4"
resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-2.2.4.tgz#355e42d375678229d46dc8ad7a7139520dd03e7b"
integrity sha512-lFyiQDVvSbQOpU+WFd//ILolGj4UgA/qXrKeZxdV14uKiAUiPAtX6XAn7WBCRi7Mx6I7EybM9E5yYn4BIpZWYg==
"@formatjs/ecma402-abstract@2.3.1":
version "2.3.1"
resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.1.tgz#cdeb3ffe1aeea9c4284b85b7e37e8e8615314c39"
integrity sha512-Ip9uV+/MpLXWRk03U/GzeJMuPeOXpJBSB5V1tjA6kJhvqssye5J5LoYLc7Z5IAHb7nR62sRoguzrFiVCP/hnzw==
dependencies:
"@formatjs/fast-memoize" "2.2.3"
"@formatjs/intl-localematcher" "0.5.8"
"@formatjs/fast-memoize" "2.2.5"
"@formatjs/intl-localematcher" "0.5.9"
decimal.js "10"
tslib "2"
"@formatjs/fast-memoize@2.2.3":
version "2.2.3"
resolved "https://registry.yarnpkg.com/@formatjs/fast-memoize/-/fast-memoize-2.2.3.tgz#74e64109279d5244f9fc281f3ae90c407cece823"
integrity sha512-3jeJ+HyOfu8osl3GNSL4vVHUuWFXR03Iz9jjgI7RwjG6ysu/Ymdr0JRCPHfF5yGbTE6JCrd63EpvX1/WybYRbA==
"@formatjs/fast-memoize@2.2.5":
version "2.2.5"
resolved "https://registry.yarnpkg.com/@formatjs/fast-memoize/-/fast-memoize-2.2.5.tgz#54a4a1793d773b72c372d3dcab3595149aee7880"
integrity sha512-6PoewUMrrcqxSoBXAOJDiW1m+AmkrAj0RiXnOMD59GRaswjXhm3MDhgepXPBgonc09oSirAJTsAggzAGQf6A6g==
dependencies:
tslib "2"
"@formatjs/intl-localematcher@0.5.8":
version "0.5.8"
resolved "https://registry.yarnpkg.com/@formatjs/intl-localematcher/-/intl-localematcher-0.5.8.tgz#b11bbd04bd3551f7cadcb1ef1e231822d0e3c97e"
integrity sha512-I+WDNWWJFZie+jkfkiK5Mp4hEDyRSEvmyfYadflOno/mmKJKcB17fEpEH0oJu/OWhhCJ8kJBDz2YMd/6cDl7Mg==
"@formatjs/intl-localematcher@0.5.9":
version "0.5.9"
resolved "https://registry.yarnpkg.com/@formatjs/intl-localematcher/-/intl-localematcher-0.5.9.tgz#43c6ee22be85b83340bcb09bdfed53657a2720db"
integrity sha512-8zkGu/sv5euxbjfZ/xmklqLyDGQSxsLqg8XOq88JW3cmJtzhCP8EtSJXlaKZnVO4beEaoiT9wj4eIoCQ9smwxA==
dependencies:
tslib "2"
"@formatjs/intl-segmenter@^11.5.7":
version "11.7.4"
resolved "https://registry.yarnpkg.com/@formatjs/intl-segmenter/-/intl-segmenter-11.7.4.tgz#f99d87ee3f98515069285438a4913681fc243252"
integrity sha512-pyHgFO86/CReKl20oK9jgaTMzSaG/nIMteMW8YuwUcS22EoMI1qbGTZ65oQ38KMT05SiHiMee2CP3WZvCi8YSQ==
version "11.7.7"
resolved "https://registry.yarnpkg.com/@formatjs/intl-segmenter/-/intl-segmenter-11.7.7.tgz#8a5aaa316e11ca2d31b99222e6fcf1ab539b085e"
integrity sha512-610J5xz5DxtEpa16zNR89CrvA9qWHxQFkUB3FKiGao0Nwn7i8cl+oyBhuH9SvtXF9j2LUOM9VMdVCMzJkVANNw==
dependencies:
"@formatjs/ecma402-abstract" "2.2.4"
"@formatjs/intl-localematcher" "0.5.8"
"@formatjs/ecma402-abstract" "2.3.1"
"@formatjs/intl-localematcher" "0.5.9"
tslib "2"
"@humanwhocodes/config-array@^0.13.0":
@ -1867,9 +1881,9 @@
chalk "^4.0.0"
"@jridgewell/gen-mapping@^0.3.5":
version "0.3.5"
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36"
integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==
version "0.3.8"
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142"
integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==
dependencies:
"@jridgewell/set-array" "^1.2.1"
"@jridgewell/sourcemap-codec" "^1.4.10"
@ -2419,10 +2433,10 @@
"@sentry/core" "8.40.0"
"@sentry/types" "8.40.0"
"@sentry/babel-plugin-component-annotate@2.22.6":
version "2.22.6"
resolved "https://registry.yarnpkg.com/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-2.22.6.tgz#829d6caf2c95c1c46108336de4e1049e6521435e"
integrity sha512-V2g1Y1I5eSe7dtUVMBvAJr8BaLRr4CLrgNgtPaZyMT4Rnps82SrZ5zqmEkLXPumlXhLUWR6qzoMNN2u+RXVXfQ==
"@sentry/babel-plugin-component-annotate@2.22.7":
version "2.22.7"
resolved "https://registry.yarnpkg.com/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-2.22.7.tgz#604c7e33d48528a13477e7af597c4d5fca51b8bd"
integrity sha512-aa7XKgZMVl6l04NY+3X7BP7yvQ/s8scn8KzQfTLrGRarziTlMGrsCOBQtCNWXOPEbtxAIHpZ9dsrAn5EJSivOQ==
"@sentry/browser@^8.0.0":
version "8.40.0"
@ -2436,14 +2450,14 @@
"@sentry/core" "8.40.0"
"@sentry/types" "8.40.0"
"@sentry/bundler-plugin-core@2.22.6":
version "2.22.6"
resolved "https://registry.yarnpkg.com/@sentry/bundler-plugin-core/-/bundler-plugin-core-2.22.6.tgz#a1ea1fd43700a3ece9e7db016997e79a2782b87d"
integrity sha512-1esQdgSUCww9XAntO4pr7uAM5cfGhLsgTK9MEwAKNfvpMYJi9NUTYa3A7AZmdA8V6107Lo4OD7peIPrDRbaDCg==
"@sentry/bundler-plugin-core@2.22.7":
version "2.22.7"
resolved "https://registry.yarnpkg.com/@sentry/bundler-plugin-core/-/bundler-plugin-core-2.22.7.tgz#28204a224cd1fef58d157e5beeb2493947a9bc35"
integrity sha512-ouQh5sqcB8vsJ8yTTe0rf+iaUkwmeUlGNFi35IkCFUQlWJ22qS6OfvNjOqFI19e6eGUXks0c/2ieFC4+9wJ+1g==
dependencies:
"@babel/core" "^7.18.5"
"@sentry/babel-plugin-component-annotate" "2.22.6"
"@sentry/cli" "^2.36.1"
"@sentry/babel-plugin-component-annotate" "2.22.7"
"@sentry/cli" "2.39.1"
dotenv "^16.3.1"
find-up "^5.0.0"
glob "^9.3.2"
@ -2485,7 +2499,7 @@
resolved "https://registry.yarnpkg.com/@sentry/cli-win32-x64/-/cli-win32-x64-2.39.1.tgz#1a874a5570c6d162b35d9d001c96e5389d07d2cb"
integrity sha512-xv0R2CMf/X1Fte3cMWie1NXuHmUyQPDBfCyIt6k6RPFPxAYUgcqgMPznYwVMwWEA1W43PaOkSn3d8ZylsDaETw==
"@sentry/cli@^2.36.1":
"@sentry/cli@2.39.1":
version "2.39.1"
resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-2.39.1.tgz#916bb5b7567ccf7fdf94ef6cf8a2b9ab78370d29"
integrity sha512-JIb3e9vh0+OmQ0KxmexMXg9oZsR/G7HMwxt5BUIKAXZ9m17Xll4ETXTRnRUBT3sf7EpNGAmlQk1xEmVN9pYZYQ==
@ -2517,11 +2531,11 @@
integrity sha512-nuCf3U3deolPM9BjNnwCc33UtFl9ec15/r74ngAkNccn+A2JXdIAsDkGJMO/9mgSFykLe1QyeJ0pQFRisCGOiA==
"@sentry/webpack-plugin@^2.7.1":
version "2.22.6"
resolved "https://registry.yarnpkg.com/@sentry/webpack-plugin/-/webpack-plugin-2.22.6.tgz#8c9d27d5cd89153a5b6e08cc9dcb3048b122ffbc"
integrity sha512-BiLhAzQYAz/9kCXKj2LeUKWf/9GBVn2dD0DeYK89s+sjDEaxjbcLBBiLlLrzT7eC9QVj2tUZRKOi6puCfc8ysw==
version "2.22.7"
resolved "https://registry.yarnpkg.com/@sentry/webpack-plugin/-/webpack-plugin-2.22.7.tgz#992c6c782c736f22e72eb318745e28cc24aabad7"
integrity sha512-j5h5LZHWDlm/FQCCmEghQ9FzYXwfZdlOf3FE/X6rK6lrtx0JCAkq+uhMSasoyP4XYKL4P4vRS6WFSos4jxf/UA==
dependencies:
"@sentry/bundler-plugin-core" "2.22.6"
"@sentry/bundler-plugin-core" "2.22.7"
unplugin "1.0.1"
uuid "^9.0.0"
@ -3034,9 +3048,9 @@
"@types/node" "*"
"@types/jsrsasign@^10.5.4":
version "10.5.14"
resolved "https://registry.yarnpkg.com/@types/jsrsasign/-/jsrsasign-10.5.14.tgz#61d1dbd791ecd11db556c1ca5d82453fc7207338"
integrity sha512-lppSlfK6etu+cuKs40K4rg8As79PH6hzIB+v55zSqImbSH3SE6Fm8MBHCiI91cWlAP3Z4igtJK1VL3fSN09blQ==
version "10.5.15"
resolved "https://registry.yarnpkg.com/@types/jsrsasign/-/jsrsasign-10.5.15.tgz#5cf1ee506b2fa2435b6e1786a873285c7110eb82"
integrity sha512-3stUTaSRtN09PPzVWR6aySD9gNnuymz+WviNHoTb85dKu+BjaV4uBbWWGykBBJkfwPtcNZVfTn2lbX00U+yhpQ==
"@types/katex@^0.16.0":
version "0.16.7"
@ -3100,9 +3114,9 @@
undici-types "~6.20.0"
"@types/node@18":
version "18.19.66"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.66.tgz#0937a47904ceba5994eedf5cf4b6d503d8d6136c"
integrity sha512-14HmtUdGxFUalGRfLLn9Gc1oNWvWh5zNbsyOLo5JV6WARSeN1QcEBKRnZm9QqNfrutgsl/hY4eJW63aZ44aBCg==
version "18.19.68"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.68.tgz#f4f10d9927a7eaf3568c46a6d739cc0967ccb701"
integrity sha512-QGtpFH1vB99ZmTa63K4/FU8twThj4fuVSBkGddTp7uIL/cuoLWIUSL2RcOaigBhfR+hg5pgGkBnkoOxrTVBMKw==
dependencies:
undici-types "~5.26.4"
@ -3387,12 +3401,12 @@
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==
"@vector-im/compound-web@^7.4.0":
version "7.4.0"
resolved "https://registry.yarnpkg.com/@vector-im/compound-web/-/compound-web-7.4.0.tgz#a5af8af6346f8ff6c14c70f5d4eb2eab7357a7cc"
integrity sha512-ZRBUeEGNmj/fTkIRa8zGnyVN7ytowpfOtHChqNm+m/+OTJN3o/lOMuQHDV8jeSEW2YwPJqGvPuG/dRr89IcQkA==
"@vector-im/compound-web@^7.5.0":
version "7.5.0"
resolved "https://registry.yarnpkg.com/@vector-im/compound-web/-/compound-web-7.5.0.tgz#1547af5f0ee27b94f79ab11eee006059f3d09707"
integrity sha512-Xhef8H5WrRmPuanzRBs8rnl+hwbcQnC7nKSCupUczAQ5hjlieBx4vcQYQ/nMkrs4rMGjgfFtR3E18wT5LlML/A==
dependencies:
"@floating-ui/react" "^0.26.24"
"@floating-ui/react" "^0.27.0"
"@radix-ui/react-context-menu" "^2.2.1"
"@radix-ui/react-dropdown-menu" "^2.1.1"
"@radix-ui/react-form" "^0.1.0"
@ -3410,7 +3424,7 @@
dependencies:
eslint-plugin-unicorn "^54.0.0"
"@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.12.1":
"@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.14.1":
version "1.14.1"
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.14.1.tgz#a9f6a07f2b03c95c8d38c4536a1fdfb521ff55b6"
integrity sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==
@ -3476,7 +3490,7 @@
resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.13.2.tgz#917a20e93f71ad5602966c2d685ae0c6c21f60f1"
integrity sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==
"@webassemblyjs/wasm-edit@^1.12.1":
"@webassemblyjs/wasm-edit@^1.14.1":
version "1.14.1"
resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz#ac6689f502219b59198ddec42dcd496b1004d597"
integrity sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==
@ -3511,7 +3525,7 @@
"@webassemblyjs/wasm-gen" "1.14.1"
"@webassemblyjs/wasm-parser" "1.14.1"
"@webassemblyjs/wasm-parser@1.14.1", "@webassemblyjs/wasm-parser@^1.12.1":
"@webassemblyjs/wasm-parser@1.14.1", "@webassemblyjs/wasm-parser@^1.14.1":
version "1.14.1"
resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz#b3e13f1893605ca78b52c68e54cf6a865f90b9fb"
integrity sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==
@ -4197,7 +4211,15 @@ bytes@3.1.2:
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7:
call-bind-apply-helpers@^1.0.0:
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"
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9"
integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==
@ -4208,6 +4230,16 @@ call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7:
get-intrinsic "^1.2.4"
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:
version "3.1.0"
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
@ -4789,11 +4821,11 @@ css-tree@^2.3.1:
source-map-js "^1.0.1"
css-tree@^3.0.0, css-tree@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-3.0.1.tgz#bea6deaea60bb5bcf416adfb1ecf607a8d9471f6"
integrity sha512-8Fxxv+tGhORlshCdCwnNJytvlvq46sOLSYEx2ZIGurahWvMucSRnyjPA3AmrMq4VPRYbHVpWj5VkiVasrM2H4Q==
version "3.1.0"
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-3.1.0.tgz#7aabc035f4e66b5c86f54570d55e05b1346eb0fd"
integrity sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==
dependencies:
mdn-data "2.12.1"
mdn-data "2.12.2"
source-map-js "^1.0.1"
css-tree@~2.2.0:
@ -4970,10 +5002,10 @@ debug@2.6.9:
dependencies:
ms "2.0.0"
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.3.7"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52"
integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==
debug@4, debug@^4.1.0, debug@^4.3.1:
version "4.4.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a"
integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==
dependencies:
ms "^2.1.3"
@ -4984,12 +5016,19 @@ debug@^3.2.7:
dependencies:
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:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==
decimal.js@^10.4.2:
decimal.js@10, decimal.js@^10.4.2:
version "10.4.3"
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23"
integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==
@ -5241,9 +5280,18 @@ dot-case@^3.0.4:
tslib "^2.0.3"
dotenv@^16.0.2, dotenv@^16.3.1:
version "16.4.5"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f"
integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==
version "16.4.7"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.7.tgz#0e20c5b82950140aa99be360a8a5f52335f53c26"
integrity sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==
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:
version "0.1.2"
@ -5282,9 +5330,9 @@ ejs@^3.1.8:
jake "^10.8.5"
electron-to-chromium@^1.5.41:
version "1.5.67"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.67.tgz#66ebd2be4a77469ac2760ef5e9e460ba9a43a845"
integrity sha512-nz88NNBsD7kQSAGGJyp8hS6xSPtWwqNogA0mjtc2nUYeEf3nURK9qpV18TuBdDmEDgVWotS8Wkzf+V52dSQ/LQ==
version "1.5.72"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.72.tgz#a732805986d3a5b5fedd438ddf4616c7d78ac2df"
integrity sha512-ZpSAUOZ2Izby7qnZluSrAlGgGQzucmFbN0n64dYzocYxnxV5ufurpj3VgEe4cUp7ir9LmeLxNYo8bVnlM8bQHw==
emittery@^0.13.1:
version "0.13.1"
@ -5433,12 +5481,10 @@ es-abstract@^1.17.5, es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23
unbox-primitive "^1.0.2"
which-typed-array "^1.1.15"
es-define-property@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845"
integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==
dependencies:
get-intrinsic "^1.2.4"
es-define-property@^1.0.0, es-define-property@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa"
integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==
es-errors@^1.2.1, es-errors@^1.3.0:
version "1.3.0"
@ -5921,9 +5967,9 @@ expect@^29.0.0, expect@^29.7.0:
jest-util "^29.7.0"
express@^4.18.2, express@^4.19.2:
version "4.21.1"
resolved "https://registry.yarnpkg.com/express/-/express-4.21.1.tgz#9dae5dda832f16b4eec941a4e44aa89ec481b281"
integrity sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==
version "4.21.2"
resolved "https://registry.yarnpkg.com/express/-/express-4.21.2.tgz#cf250e48362174ead6cea4a566abef0162c1ec32"
integrity sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==
dependencies:
accepts "~1.3.8"
array-flatten "1.1.1"
@ -5944,7 +5990,7 @@ express@^4.18.2, express@^4.19.2:
methods "~1.1.2"
on-finished "2.4.1"
parseurl "~1.3.3"
path-to-regexp "0.1.10"
path-to-regexp "0.1.12"
proxy-addr "~2.0.7"
qs "6.13.0"
range-parser "~1.2.1"
@ -6305,7 +6351,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"
integrity sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==
get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4:
get-intrinsic@^1.2.1, get-intrinsic@^1.2.3:
version "1.2.4"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd"
integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==
@ -6316,6 +6362,20 @@ get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4:
has-symbols "^1.0.3"
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:
version "1.0.1"
resolved "https://registry.yarnpkg.com/get-nonce/-/get-nonce-1.0.1.tgz#fdf3f0278073820d2ce9426c18f07481b1e0cdf3"
@ -6492,7 +6552,12 @@ globjoin@^0.1.4:
resolved "https://registry.yarnpkg.com/globjoin/-/globjoin-0.1.4.tgz#2f4494ac8919e3767c5cbb691e9f463324285d43"
integrity sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==
gopd@^1.0.1, gopd@^1.1.0:
gopd@^1.0.1, gopd@^1.2.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"
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.1.0.tgz#df8f0839c2d48caefc32a025a49294d39606c912"
integrity sha512-FQoVQnqcdk4hVM4JN1eromaun4iuS34oStkdlLENLdpULsuQcTyXj8w7ayhuUfPwEYZ1ZOooOTT6fdA9Vmx/RA==
@ -6543,18 +6608,30 @@ has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2:
dependencies:
es-define-property "^1.0.0"
has-proto@^1.0.1, has-proto@^1.0.3:
has-proto@^1.0.1:
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"
resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.1.0.tgz#deb10494cbbe8809bce168a3b961f42969f5ed43"
integrity sha512-QLdzI9IIO1Jg7f9GT1gXpPpXArAn6cS31R1eEZqz08Gc+uQ8/XiqHWt17Fiw+2p6oTTIq5GXEpQkAlA88YRl/Q==
dependencies:
call-bind "^1.0.7"
has-symbols@^1.0.2, has-symbols@^1.0.3:
has-symbols@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
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:
version "1.0.2"
resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc"
@ -7752,16 +7829,21 @@ jsdom@^20.0.0:
ws "^8.11.0"
xml-name-validator "^4.0.0"
jsesc@^3.0.2, 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==
jsesc@^3.0.2:
version "3.1.0"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d"
integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==
jsesc@~0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
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:
version "3.0.1"
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
@ -7878,9 +7960,9 @@ kleur@^3.0.3:
integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
knip@^5.36.2:
version "5.38.3"
resolved "https://registry.yarnpkg.com/knip/-/knip-5.38.3.tgz#a139e0c6215c9958d213ce1a9e3985cf5de2774e"
integrity sha512-pg3CMzWlZy4mnuwxieGoK74oOgzFPvsUR/aE8NSqx2oQr56soXTzmw8GsHR277pU52Fe0h4/pho2PMhVeEvj8g==
version "5.39.2"
resolved "https://registry.yarnpkg.com/knip/-/knip-5.39.2.tgz#1faacd8d8ef36b509b2f6e396cce85b645abb04e"
integrity sha512-BuvuWRllLWV/r2G4m9ggNH+DZ6gouP/dhtJPXVlMbWNF++w9/EfrF6k2g7YBKCwjzCC+PXmYtpH8S2t8RjnY4Q==
dependencies:
"@nodelib/fs.walk" "1.2.8"
"@snyk/github-codeowners" "1.1.0"
@ -7959,10 +8041,10 @@ lines-and-columns@^1.1.6:
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
linkify-element@4.1.4:
version "4.1.4"
resolved "https://registry.yarnpkg.com/linkify-element/-/linkify-element-4.1.4.tgz#d4050b41fb47c44871e5eed93bc11865e403cc90"
integrity sha512-XhSTTF7b7OoX4KIkwVG8MET5DSFEHohT0Gp5pjmsByYp+JCyZq5rSZGsar5dYzeuKUV6TqTSLtsH/NzBBwBxgQ==
linkify-element@4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/linkify-element/-/linkify-element-4.2.0.tgz#fb5c6d47576487a463fd22a0cc889e15833aa943"
integrity sha512-LahyRMhXAgWTP9TOid7pTv8UUZFDz+saLkIVAoGNmOvISt+uSeBzdGhk3dsvkdzAh1QMhkO+fVJjmkMEITre5g==
linkify-it@^4.0.1:
version "4.0.1"
@ -7971,20 +8053,20 @@ linkify-it@^4.0.1:
dependencies:
uc.micro "^1.0.1"
linkify-react@4.1.4:
version "4.1.4"
resolved "https://registry.yarnpkg.com/linkify-react/-/linkify-react-4.1.4.tgz#6c709f3f96543914874982f4b0b00f9c9270ce93"
integrity sha512-UI9nqHtFzHYRUvVRrYeua5GIXkc0Jy3RpLsJBWEht7HwqjAa2qSaIksGmNSLqclNpO/5AkwaxUJv71I/pQsk9Q==
linkify-react@4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/linkify-react/-/linkify-react-4.2.0.tgz#d143b2af8efa5e3b09517b66ed442624c3e06bcf"
integrity sha512-dIcDGo+n4FP2FPIHDcqB7cUE+omkcEgQJpc7sNNP4+XZ9FUhFAkKjGnHMzsZM+B4yF93sK166z9K5cKTe/JpzA==
linkify-string@4.1.4:
version "4.1.4"
resolved "https://registry.yarnpkg.com/linkify-string/-/linkify-string-4.1.4.tgz#89fb814e05c5b22f76d2a2a640bc8b1db4c6694f"
integrity sha512-4z2UEzEi4SxnhWMzzZ8Pa8vIOwX/2U0XWxk/0UIA7lI+Dn0ZRKqTE9ildnO6Jl6K5hqVuLKTeMD8p4bdFW6P8g==
linkify-string@4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/linkify-string/-/linkify-string-4.2.0.tgz#e8c5a9d57698e81e7cce7f4915ddbcbde17134c0"
integrity sha512-LqOKk0+RlqibFkxjPAGOL7Mfssqj6/SdG9QWGvzeVDpoWXhaw9OXxseCtFanjIl7C6UyTTZizhyGr4IWLfijiw==
linkifyjs@4.1.4:
version "4.1.4"
resolved "https://registry.yarnpkg.com/linkifyjs/-/linkifyjs-4.1.4.tgz#2766605a20078d50c90f35af22275a42dfb7dfc4"
integrity sha512-0/NxkHNpiJ0k9VrYCkAn9OtU1eu8xEr1tCCpDtSsVRm/SF0xAak2Gzv3QimSfgUgqLBCDlfhMbu73XvaEHUTPQ==
linkifyjs@4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/linkifyjs/-/linkifyjs-4.2.0.tgz#9dd30222b9cbabec9c950e725ec00031c7fa3f08"
integrity sha512-pCj3PrQyATaoTYKHrgWRF3SJwsm61udVh+vuls/Rl6SptiDhgE7ziUIudAedRY9QEfynmM7/RmLEfPUyw1HPCw==
lint-staged@^15.0.2:
version "15.2.10"
@ -8286,12 +8368,7 @@ mdn-data@2.0.30:
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc"
integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==
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:
mdn-data@2.12.2, mdn-data@^2.12.2:
version "2.12.2"
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.12.2.tgz#9ae6c41a9e65adf61318b32bff7b64fbfb13f8cf"
integrity sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==
@ -8575,9 +8652,9 @@ node-int64@^0.4.0:
integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==
node-releases@^2.0.18:
version "2.0.18"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f"
integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==
version "2.0.19"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314"
integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==
normalize-package-data@^2.5.0:
version "2.5.0"
@ -8963,10 +9040,10 @@ path-scurry@^2.0.0:
lru-cache "^11.0.0"
minipass "^7.1.2"
path-to-regexp@0.1.10:
version "0.1.10"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz#67e9108c5c0551b9e5326064387de4763c4d5f8b"
integrity sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==
path-to-regexp@0.1.12:
version "0.1.12"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7"
integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==
path-to-regexp@^2.2.1:
version "2.4.0"
@ -9704,10 +9781,10 @@ prelude-ls@^1.2.1:
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
prettier@3.4.1:
version "3.4.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.4.1.tgz#e211d451d6452db0a291672ca9154bc8c2579f7b"
integrity sha512-G+YdqtITVZmOJje6QkXQWzl3fSfMxFwm1tjTyo9exhkmWSqC4Yhd1+lug++IlR2mvRVAxEDDWYkQdeSztajqgg==
prettier@3.4.2:
version "3.4.2"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.4.2.tgz#a5ce1fb522a588bf2b78ca44c6e6fe5aa5a2b13f"
integrity sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==
pretty-error@^4.0.0:
version "4.0.0"
@ -10520,7 +10597,7 @@ set-blocking@^2.0.0:
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==
set-function-length@^1.2.1:
set-function-length@^1.2.1, set-function-length@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449"
integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==
@ -11180,7 +11257,7 @@ terser-webpack-plugin@^5.3.10, terser-webpack-plugin@^5.3.9:
serialize-javascript "^6.0.1"
terser "^5.26.0"
terser@^5.10.0, terser@^5.26.0:
terser@^5.10.0:
version "5.36.0"
resolved "https://registry.yarnpkg.com/terser/-/terser-5.36.0.tgz#8b0dbed459ac40ff7b4c9fd5a3a2029de105180e"
integrity sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==
@ -11190,6 +11267,16 @@ terser@^5.10.0, terser@^5.26.0:
commander "^2.20.0"
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:
version "6.0.0"
resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e"
@ -11885,15 +11972,15 @@ webpack-virtual-modules@^0.5.0:
integrity sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==
webpack@^5.89.0:
version "5.96.1"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.96.1.tgz#3676d1626d8312b6b10d0c18cc049fba7ac01f0c"
integrity sha512-l2LlBSvVZGhL4ZrPwyr8+37AunkcYj5qh8o6u2/2rzoPc8gxFJkLj1WxNgooi9pnoc06jh0BjuXnamM4qlujZA==
version "5.97.1"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.97.1.tgz#972a8320a438b56ff0f1d94ade9e82eac155fa58"
integrity sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==
dependencies:
"@types/eslint-scope" "^3.7.7"
"@types/estree" "^1.0.6"
"@webassemblyjs/ast" "^1.12.1"
"@webassemblyjs/wasm-edit" "^1.12.1"
"@webassemblyjs/wasm-parser" "^1.12.1"
"@webassemblyjs/ast" "^1.14.1"
"@webassemblyjs/wasm-edit" "^1.14.1"
"@webassemblyjs/wasm-parser" "^1.14.1"
acorn "^8.14.0"
browserslist "^4.24.0"
chrome-trace-event "^1.0.2"
@ -12232,6 +12319,6 @@ zod-validation-error@^3.0.3:
integrity sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ==
zod@^3.22.4:
version "3.23.8"
resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d"
integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==
version "3.24.0"
resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.0.tgz#babb32313f7c5f4a99812feee806d186b4f76bde"
integrity sha512-Hz+wiY8yD0VLA2k/+nsg2Abez674dDGTai33SwNvMPuf9uIrBC9eFgIMQxBBbHFxVXi8W+5nX9DcAh9YNSQm/w==