Compare commits

..

6 Commits

Author SHA1 Message Date
Robin
eeec10e298 Merge branch 'livekit' into hs/remove-memory-store 2024-11-20 10:52:55 -05:00
Timo
7d4d1445d0 another js-sdk bump (to a version that is rebased on develop again)
- includes the indexedDB fix
2024-11-20 12:28:22 +01:00
Timo
9dbc43d692 newer js-sdk 2024-11-19 18:57:17 +01:00
Timo
dfece88d66 js-sdk-bump to nov branch without custom state event ignore logic 2024-11-19 15:01:54 +01:00
Half-Shot
f504a6ab28 Import latest js-sdk commit 2024-11-15 14:39:02 +00:00
Half-Shot
319c9aa7da Remove memory store option 2024-11-15 14:38:47 +00:00
28 changed files with 683 additions and 878 deletions

View File

@@ -25,38 +25,230 @@ There are two formats for Element Call urls.
```
With this format the livekit alias that will be used is the `<room_name>`.
All people connecting to this URL will end up in the same unencrypted room.
All ppl connecting to this url will end up in the same unencrypted room.
This does not scale, is super unsecure
(people could end up in the same room by accident) and it also is not really
(ppl could end up in the same room by accident) and it also is not really
possible to support encryption.
The url parameters are spit into two categories: **general** and **widget related**.
## Parameters
## Widget related params
| Name | Values | Required for widget | Required for SPA | Description |
| ------------------------- | ---------------------------------------------------------------------------------------------------- | ----------------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `allowIceFallback` | `true` or `false` | No, defaults to `false` | No, defaults to `false` | Allows use of fallback STUN servers for ICE if the user's homeserver doesnt provide any. |
| `analyticsID` | Posthog analytics ID | No | No | Available only with user's consent for sharing telemetry in Element Web. |
| `appPrompt` | `true` or `false` | No, defaults to `true` | No, defaults to `true` | Prompts the user to launch the native mobile app upon entering a room, applicable only on Android and iOS, and must be enabled in config. |
| `baseUrl` | | Yes | Not applicable | The base URL of the homeserver to use for media lookups. |
| `confineToRoom` | `true` or `false` | No, defaults to `false` | No, defaults to `false` | Keeps the user confined to the current call/room. |
| `deviceId` | Matrix device ID | Yes | Not applicable | The Matrix device ID for the widget host. |
| `displayName` | | No | No | Display name used for auto-registration. |
| `enableE2EE` (deprecated) | `true` or `false` | No, defaults to `true` | No, defaults to `true` | Legacy flag to enable end-to-end encryption, not used in the `livekit` branch. |
| `fontScale` | A decimal number such as `0.9` | No | No | Factor by which to scale the interface's font size. |
| `fonts` | | No | No | Defines the font(s) used by the interface. Multiple font parameters can be specified: `?font=font-one&font=font-two...`. |
| `hideHeader` | `true` or `false` | No, defaults to `false` | No, defaults to `false` | Hides the room header when in a call. |
| `hideScreensharing` | `true` or `false` | No, defaults to `false` | No, defaults to `false` | Hides the screen-sharing button. |
| `homeserver` | | Not applicable | No | Homeserver for registering a new (guest) user, configures non-default guest user server when creating a spa link. |
| `lang` | [BCP 47](https://www.rfc-editor.org/info/bcp47) code | No | No | The language the app should use. |
| `parentUrl` | | Yes | Not applicable | The url used to send widget action postMessages. This should be the domain of the client or the webview the widget is hosted in. (in case the widget is not in an Iframe but in a dedicated webview we send the postMessages same WebView the widget lives in. Filtering is done in the widget so it ignores the messages it receives from itself) |
| `password` | | No | No | E2EE password when using a shared secret. (For individual sender keys in embedded mode this is not required.) |
| `perParticipantE2EE` | `true` or `false` | No, defaults to `false` | No, defaults to `false` | Enables per participant encryption with Keys exchanged over encrypted matrix room messages. |
| `preload` | `true` or `false` | No, defaults to `false` | Not applicable | Pauses app before joining a call until an `io.element.join` widget action is seen, allowing preloading. |
| `returnToLobby` | `true` or `false` | No, defaults to `false` | Not applicable | Displays the lobby in widget mode after leaving a call; shows a blank page if set to `false`. Useful for video rooms. |
| `roomId` | [Matrix Room ID](https://spec.matrix.org/v1.12/appendices/#room-ids) | Yes | No | Anything about what room we're pointed to should be from useRoomIdentifier which parses the path and resolves alias with respect to the default server name, however roomId is an exception as we need the room ID in embedded widget mode, and not the room alias (or even the via params because we are not trying to join it). This is also not validated, where it is in `useRoomIdentifier()`. |
| `showControls` | `true` or `false` | No, defaults to `true` | No, defaults to `true` | Displays controls like mute, screen-share, invite, and hangup buttons during a call. |
| `skipLobby` | `true` or `false` | No, defaults to `false` | No, defaults to `false` | Skips the lobby to join a call directly, can be combined with preload in widget. |
| `theme` | One of: `light`, `dark`, `light-high-contrast`, `dark-high-contrast` | No, defaults to `dark` | No, defaults to `dark` | UI theme to use. |
| `userId` | [Matrix User Identifier](https://spec.matrix.org/v1.12/appendices/#user-identifiers) | Yes | Not applicable | The Matrix user ID. |
| `viaServers` | Comma separated list of [Matrix Server Names](https://spec.matrix.org/v1.12/appendices/#server-name) | Not applicable | No | Homeserver for joining a room, non-empty value required for rooms not on the users default homeserver. |
| `widgetId` | [MSC2774](https://github.com/matrix-org/matrix-spec-proposals/pull/2774) format widget ID | Yes | Not applicable | The id used by the widget. The presence of this parameter implies that element call will not connect to a homeserver directly and instead tries to establish postMessage communication via the `parentUrl`. |
**widgetId**
The id used by the widget. The presence of this parameter implies that element
call will not connect to a homeserver directly and instead tries to establish
postMessage communication via the `parentUrl`.
```ts
widgetId: string | null;
```
**parentUrl**
The url used to send widget action postMessages. This should be the domain of
the client or the webview the widget is hosted in. (in case the widget is not
in an Iframe but in a dedicated webview we send the postMessages same webview
the widget lives in. Filtering is done in the widget so it ignores the messages
it receives from itself)
```ts
parentUrl: string | null;
```
**userId**
The user's ID (only used in matryoshka mode).
```ts
userId: string | null;
```
**deviceId**
The device's ID (only used in matryoshka mode).
```ts
deviceId: string | null;
```
**baseUrl**
The base URL of the homeserver to use for media lookups in matryoshka mode.
```ts
baseUrl: string | null;
```
### General url parameters
**roomId**
Anything about what room we're pointed to should be from useRoomIdentifier which
parses the path and resolves alias with respect to the default server name, however
roomId is an exception as we need the room ID in embedded (matroyska) mode, and not
the room alias (or even the via params because we are not trying to join it). This
is also not validated, where it is in useRoomIdentifier().
```ts
roomId: string | null;
```
**confineToRoom**
Whether the app should keep the user confined to the current call/room.
```ts
confineToRoom: boolean; (default: false)
```
**appPrompt**
Whether upon entering a room, the user should be prompted to launch the
native mobile app. (Affects only Android and iOS.)
The app prompt must also be enabled in the config for this to take effect.
```ts
appPrompt: boolean; (default: true)
```
**preload**
Whether the app should pause before joining the call until it sees an
io.element.join widget action, allowing it to be preloaded.
```ts
preload: boolean; (default: false)
```
**hideHeader**
Whether to hide the room header when in a call.
```ts
hideHeader: boolean; (default: false)
```
**showControls**
Whether to show the buttons to mute, screen-share, invite, hangup are shown
when in a call.
```ts
showControls: boolean; (default: true)
```
**hideScreensharing**
Whether to hide the screen-sharing button.
```ts
hideScreensharing: boolean; (default: false)
```
**enableE2EE** (Deprecated)
Whether to use end-to-end encryption. This is a legacy flag for the full mesh branch.
It is not used on the livekit branch and has no impact there!
```ts
enableE2EE: boolean; (default: true)
```
**perParticipantE2EE**
Whether to use per participant encryption.
Keys will be exchanged over encrypted matrix room messages.
```ts
perParticipantE2EE: boolean; (default: false)
```
**password**
E2EE password when using a shared secret.
(For individual sender keys in embedded mode this is not required.)
```ts
password: string | null;
```
**displayName**
The display name to use for auto-registration.
```ts
displayName: string | null;
```
**lang**
The BCP 47 code of the language the app should use.
```ts
lang: string | null;
```
**fonts**
The font/fonts which the interface should use.
There can be multiple font url parameters: `?font=font-one&font=font-two...`
```ts
font: string;
font: string;
...
```
**fontScale**
The factor by which to scale the interface's font size.
```ts
fontScale: number | null;
```
**analyticsID**
The Posthog analytics ID. It is only available if the user has given consent for
sharing telemetry in element web.
```ts
analyticsID: string | null;
```
**allowIceFallback**
Whether the app is allowed to use fallback STUN servers for ICE in case the
user's homeserver doesn't provide any.
```ts
allowIceFallback: boolean; (default: false)
```
**skipLobby**
Setting this flag skips the lobby and brings you in the call directly.
In the widget this can be combined with preload to pass the device settings
with the join widget action.
```ts
skipLobby: boolean; (default: false)
```
**returnToLobby**
Setting this flag makes element call show the lobby in widget mode after leaving
a call.
This is useful for video rooms.
If set to false, the widget will show a blank page after leaving the call.
```ts
returnToLobby: boolean; (default: false)
```
**theme**
The theme to use for element call.
can be "light", "dark", "light-high-contrast" or "dark-high-contrast".
If not set element call will use the dark theme.
```ts
theme: string | null;
```
**viaServers**
This defines the homeserver that is going to be used when joining a room.
It has to be set to a non default value for links to rooms
that are not on the default homeserver,
that is in use for the current user.
```ts
viaServers: string; (default: undefined)
```
**homeserver**
This defines the homeserver that is going to be used when registering
a new (guest) user.
This can be user to configure a non default guest user server when
creating a spa link.
```ts
homeserver: string; (default: undefined)
```

View File

@@ -171,7 +171,7 @@
"preferences_tab_show_hand_raised_timer_label": "Show hand raise duration",
"speaker_device_selection_label": "Speaker"
},
"star_rating_input_label_one": "{{count}} star",
"star_rating_input_label_one": "{{count}} stars",
"star_rating_input_label_other": "{{count}} stars",
"start_new_call": "Start new call",
"start_video_button_label": "Start video",

View File

@@ -33,7 +33,7 @@
"@livekit/components-react": "^2.0.0",
"@opentelemetry/api": "^1.4.0",
"@opentelemetry/core": "^1.25.1",
"@opentelemetry/exporter-trace-otlp-http": "^0.55.0",
"@opentelemetry/exporter-trace-otlp-http": "^0.54.0",
"@opentelemetry/resources": "^1.25.1",
"@opentelemetry/sdk-trace-base": "^1.25.1",
"@opentelemetry/sdk-trace-web": "^1.9.1",
@@ -45,7 +45,6 @@
"@sentry/react": "^8.0.0",
"@sentry/vite-plugin": "^2.0.0",
"@testing-library/dom": "^10.1.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.0.0",
"@testing-library/react-hooks": "^8.0.1",
"@testing-library/user-event": "^14.5.1",
@@ -91,7 +90,7 @@
"livekit-client": "^2.5.7",
"lodash-es": "^4.17.21",
"loglevel": "^1.9.1",
"matrix-js-sdk": "matrix-org/matrix-js-sdk#2210255d6ffc909c574fb8ef16f92140b2fb7797",
"matrix-js-sdk": "matrix-org/matrix-js-sdk#80582a734684b532585b52cde63ef34dc7d774fa",
"matrix-widget-api": "^1.10.0",
"normalize.css": "^8.0.1",
"observable-hooks": "^4.2.3",

View File

@@ -0,0 +1,26 @@
{
"applinks": {
"details": [
{
"appIDs": [
"7J4U792NQT.io.element.elementx",
"7J4U792NQT.io.element.elementx.nightly",
"7J4U792NQT.io.element.elementx.pr"
],
"components": [
{
"?": {
"no_universal_links": "?*"
},
"exclude": true,
"comment": "Opt out of universal links"
},
{
"/": "/*",
"comment": "Matches any URL"
}
]
}
]
}
}

View File

@@ -0,0 +1,32 @@
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "io.element.android.x.debug",
"sha256_cert_fingerprints": [
"B0:B0:51:DC:56:5C:81:2F:E1:7F:6F:3E:94:5B:4D:79:04:71:23:AB:0D:A6:12:86:76:9E:B2:94:91:97:13:0E"
]
}
},
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "io.element.android.x.nightly",
"sha256_cert_fingerprints": [
"CA:D3:85:16:84:3A:05:CC:EB:00:AB:7B:D3:80:0F:01:BA:8F:E0:4B:38:86:F3:97:D8:F7:9A:1B:C4:54:E4:0F"
]
}
},
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "io.element.android.x",
"sha256_cert_fingerprints": [
"C6:DB:9B:9C:8C:BD:D6:5D:16:E8:EC:8C:8B:91:C8:31:B9:EF:C9:5C:BF:98:AE:41:F6:A9:D8:35:15:1A:7E:16"
]
}
}
]

View File

@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/
import { useMemo, FC, CSSProperties } from "react";
import { useMemo, FC } from "react";
import { Avatar as CompoundAvatar } from "@vector-im/compound-web";
import { getAvatarUrl } from "./utils/matrix";
@@ -33,7 +33,6 @@ interface Props {
className?: string;
src?: string;
size?: Size | number;
style?: CSSProperties;
}
export const Avatar: FC<Props> = ({
@@ -42,8 +41,6 @@ export const Avatar: FC<Props> = ({
name,
src,
size = Size.MD,
style,
...props
}) => {
const { client } = useClient();
@@ -67,8 +64,6 @@ export const Avatar: FC<Props> = ({
name={name}
size={`${sizePx}px`}
src={resolvedSrc}
style={style}
{...props}
/>
);
};

View File

@@ -7,7 +7,7 @@ Please see LICENSE in the repository root for full details.
import { describe, expect, it } from "vitest";
import { getRoomIdentifierFromUrl, getUrlParams } from "../src/UrlParams";
import { getRoomIdentifierFromUrl } from "../src/UrlParams";
const ROOM_NAME = "roomNameHere";
const ROOM_ID = "!d45f138fsd";
@@ -86,18 +86,4 @@ describe("UrlParams", () => {
.roomAlias,
).toBeFalsy();
});
describe("preload", () => {
it("defaults to false", () => {
expect(getUrlParams().preload).toBe(false);
});
it("ignored in SPA mode", () => {
expect(getUrlParams("?preload=true").preload).toBe(false);
});
it("respected in widget mode", () => {
expect(getUrlParams("?preload=true&widgetId=12345").preload).toBe(true);
});
});
});

View File

@@ -211,11 +211,8 @@ export const getUrlParams = (
const fontScale = parseFloat(parser.getParam("fontScale") ?? "");
const widgetId = parser.getParam("widgetId");
const isWidget = !!widgetId;
return {
widgetId,
widgetId: parser.getParam("widgetId"),
parentUrl: parser.getParam("parentUrl"),
// NB. we don't validate roomId here as we do in getRoomIdentifierFromUrl:
@@ -227,7 +224,7 @@ export const getUrlParams = (
confineToRoom:
parser.getFlagParam("confineToRoom") || parser.getFlagParam("embed"),
appPrompt: parser.getFlagParam("appPrompt", true),
preload: isWidget ? parser.getFlagParam("preload") : false,
preload: parser.getFlagParam("preload"),
hideHeader: parser.getFlagParam("hideHeader"),
showControls: parser.getFlagParam("showControls", true),
hideScreensharing: parser.getFlagParam("hideScreensharing"),

View File

@@ -24,7 +24,6 @@ import {
audioInput as audioInputSetting,
audioOutput as audioOutputSetting,
videoInput as videoInputSetting,
Setting,
} from "../settings/settings";
import { isFirefox } from "../Platform";
@@ -59,7 +58,7 @@ function useObservableState<T>(
function useMediaDevice(
kind: MediaDeviceKind,
setting: Setting<string | undefined>,
fallbackDevice: string | undefined,
usingNames: boolean,
alwaysDefault: boolean = false,
): MediaDevice {
@@ -85,21 +84,15 @@ function useMediaDevice(
[kind, requestPermissions],
);
const available = useObservableState(deviceObserver, []);
const [preferredId, select] = useSetting(setting);
const [selectedId, select] = useState(fallbackDevice);
return useMemo(() => {
let selectedId: string | undefined = undefined;
if (!alwaysDefault && available) {
// If the preferred device is available, use it. Or if every available
// device ID is falsy, the browser is probably just being paranoid about
// fingerprinting and we should still try using the preferred device.
// Worst case it is not available and the browser will gracefully fall
// back to some other device for us when requesting the media stream.
// Otherwise, select the first available device.
selectedId =
available.some((d) => d.deviceId === preferredId) ||
available.every((d) => d.deviceId === "")
? preferredId
let devId;
if (available) {
devId = available.some((d) => d.deviceId === selectedId)
? selectedId
: available.some((d) => d.deviceId === fallbackDevice)
? fallbackDevice
: available.at(0)?.deviceId;
}
@@ -109,10 +102,10 @@ function useMediaDevice(
// device entries for the exact same device ID; deduplicate them
[...new Map(available.map((d) => [d.deviceId, d])).values()]
: [],
selectedId,
selectedId: alwaysDefault ? undefined : devId,
select,
};
}, [available, preferredId, select, alwaysDefault]);
}, [available, selectedId, fallbackDevice, select, alwaysDefault]);
}
const deviceStub: MediaDevice = {
@@ -148,22 +141,36 @@ export const MediaDevicesProvider: FC<Props> = ({ children }) => {
// for ouput devices because the selector wont be shown on FF.
const useOutputNames = usingNames && !isFirefox();
const audioInput = useMediaDevice(
"audioinput",
audioInputSetting,
usingNames,
);
const [storedAudioInput, setStoredAudioInput] = useSetting(audioInputSetting);
const [storedAudioOutput, setStoredAudioOutput] =
useSetting(audioOutputSetting);
const [storedVideoInput, setStoredVideoInput] = useSetting(videoInputSetting);
const audioInput = useMediaDevice("audioinput", storedAudioInput, usingNames);
const audioOutput = useMediaDevice(
"audiooutput",
audioOutputSetting,
storedAudioOutput,
useOutputNames,
alwaysUseDefaultAudio,
);
const videoInput = useMediaDevice(
"videoinput",
videoInputSetting,
usingNames,
);
const videoInput = useMediaDevice("videoinput", storedVideoInput, usingNames);
useEffect(() => {
if (audioInput.selectedId !== undefined)
setStoredAudioInput(audioInput.selectedId);
}, [setStoredAudioInput, audioInput.selectedId]);
useEffect(() => {
// Skip setting state for ff output. Redundent since it is set to always return 'undefined'
// but makes it clear while debugging that this is not happening on FF. + perf ;)
if (audioOutput.selectedId !== undefined && !isFirefox())
setStoredAudioOutput(audioOutput.selectedId);
}, [setStoredAudioOutput, audioOutput.selectedId]);
useEffect(() => {
if (videoInput.selectedId !== undefined)
setStoredVideoInput(videoInput.selectedId);
}, [setStoredVideoInput, videoInput.selectedId]);
const startUsingDeviceNames = useCallback(
() => setNumCallersUsingNames((n) => n + 1),

View File

@@ -20,6 +20,7 @@ import {
TrackEvent,
} from "livekit-client";
import { useObservable, useObservableEagerState } from "observable-hooks";
import { useEffect } from "react";
import { logger } from "matrix-js-sdk/src/logger";
import { useMediaDevices } from "../livekit/MediaDevicesContext";
@@ -34,7 +35,6 @@ export function useSwitchCamera(
video: Observable<LocalVideoTrack | null>,
): (() => void) | null {
const mediaDevices = useMediaDevices();
const setVideoInput = useLatest(mediaDevices.videoInput.select);
// Produce an observable like the input 'video' observable, except make it
// emit whenever the track is muted or the device changes
@@ -75,12 +75,6 @@ export function useSwitchCamera(
.restartTrack({
facingMode: facingMode === "user" ? "environment" : "user",
})
.then(() => {
// Inform the MediaDeviceContext which camera was chosen
const deviceId =
track.mediaStreamTrack.getSettings().deviceId;
if (deviceId !== undefined) setVideoInput.current(deviceId);
})
.catch((e) =>
logger.error("Failed to switch camera", facingMode, e),
);
@@ -89,5 +83,16 @@ export function useSwitchCamera(
[videoTrack],
);
const setVideoInput = useLatest(mediaDevices.videoInput.select);
useEffect(() => {
// Watch for device changes due to switching the camera and feed them back
// into the MediaDeviceContext
const subscription = videoTrack.subscribe((track) => {
const deviceId = track?.mediaStreamTrack.getSettings().deviceId;
if (deviceId !== undefined) setVideoInput.current(deviceId);
});
return (): void => subscription.unsubscribe();
}, [videoTrack, setVideoInput]);
return useObservableEagerState(switchCamera);
}

View File

@@ -135,20 +135,7 @@ const widgetPostHangupProcedure = async (
// We send the hangup event after the memberships have been updated
// calling leaveRTCSession.
// We need to wait because this makes the client hosting this widget killing the IFrame.
try {
await widget.api.transport.send(ElementWidgetActions.HangupCall, {});
} catch (e) {
logger.error("Failed to send hangup action", e);
}
// To make the hangup procedure behave more similarly to what future versions
// of Element Call will do, we additionally send a close action (even though
// we're not yet employing the distinction between 'hangup' and 'close' to
// display error screens)
try {
await widget.api.transport.send(ElementWidgetActions.Close, {});
} catch (e) {
logger.error("Failed to send close action", e);
}
await widget.api.transport.send(ElementWidgetActions.HangupCall, {});
};
export async function leaveRTCSession(

View File

@@ -1,18 +0,0 @@
.selection {
gap: 0;
}
.title {
color: var(--cpd-color-text-secondary);
margin-block: var(--cpd-space-3x) 0;
}
.separator {
margin-block: 6px var(--cpd-space-4x);
}
.options {
display: flex;
flex-direction: column;
gap: var(--cpd-space-4x);
}

View File

@@ -1,71 +0,0 @@
/*
Copyright 2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/
import { ChangeEvent, FC, useCallback, useId } from "react";
import {
Heading,
InlineField,
Label,
RadioControl,
Separator,
} from "@vector-im/compound-web";
import { MediaDevice } from "../livekit/MediaDevicesContext";
import styles from "./DeviceSelection.module.css";
interface Props {
devices: MediaDevice;
caption: string;
}
export const DeviceSelection: FC<Props> = ({ devices, caption }) => {
const groupId = useId();
const onChange = useCallback(
(e: ChangeEvent<HTMLInputElement>) => {
devices.select(e.target.value);
},
[devices],
);
if (devices.available.length == 0) return null;
return (
<div className={styles.selection}>
<Heading
type="body"
weight="semibold"
size="sm"
as="h4"
className={styles.title}
>
{caption}
</Heading>
<Separator className={styles.separator} />
<div className={styles.options}>
{devices.available.map(({ deviceId, label }, index) => (
<InlineField
key={deviceId}
name={groupId}
control={
<RadioControl
checked={deviceId === devices.selectedId}
onChange={onChange}
value={deviceId}
/>
}
>
<Label>
{!!label && label.trim().length > 0
? label
: `${caption} ${index + 1}`}
</Label>
</InlineField>
))}
</div>
</div>
);
};

View File

@@ -5,10 +5,10 @@ SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/
import { ChangeEvent, FC, useCallback } from "react";
import { ChangeEvent, FC, ReactNode, useCallback } from "react";
import { Trans, useTranslation } from "react-i18next";
import { MatrixClient } from "matrix-js-sdk/src/matrix";
import { Root as Form, Text } from "@vector-im/compound-web";
import { Dropdown, Separator, Text } from "@vector-im/compound-web";
import { Modal } from "../Modal";
import styles from "./SettingsModal.module.css";
@@ -19,6 +19,7 @@ import { ProfileSettingsTab } from "./ProfileSettingsTab";
import { FeedbackSettingsTab } from "./FeedbackSettingsTab";
import {
useMediaDevices,
MediaDevice,
useMediaDeviceNames,
} from "../livekit/MediaDevicesContext";
import { widget } from "../widget";
@@ -32,7 +33,6 @@ import {
import { isFirefox } from "../Platform";
import { PreferencesSettingsTab } from "./PreferencesSettingsTab";
import { Slider } from "../Slider";
import { DeviceSelection } from "./DeviceSelection";
type SettingsTab =
| "audio"
@@ -70,6 +70,40 @@ export const SettingsModal: FC<Props> = ({
);
const [duplicateTiles, setDuplicateTiles] = useSetting(duplicateTilesSetting);
// Generate a `SelectInput` with a list of devices for a given device kind.
const generateDeviceSelection = (
devices: MediaDevice,
caption: string,
): ReactNode => {
if (devices.available.length == 0) return null;
const values = devices.available.map(
({ deviceId, label }, index) =>
[
deviceId,
!!label && label.trim().length > 0
? label
: `${caption} ${index + 1}`,
] as [string, string],
);
return (
<Dropdown
label={caption}
defaultValue={
devices.selectedId === "" || !devices.selectedId
? "default"
: devices.selectedId
}
onValueChange={(id): void => devices.select(id)}
values={values}
// XXX This is unused because we set a defaultValue. The component
// shouldn't require this prop.
placeholder=""
/>
);
};
const optInDescription = (
<Text size="sm">
<Trans i18nKey="settings.opt_in_description">
@@ -91,30 +125,25 @@ export const SettingsModal: FC<Props> = ({
name: t("common.audio"),
content: (
<>
<Form>
<DeviceSelection
devices={devices.audioInput}
caption={t("common.microphone")}
/>
{!isFirefox() && (
<DeviceSelection
devices={devices.audioOutput}
caption={t("settings.speaker_device_selection_label")}
/>
{generateDeviceSelection(devices.audioInput, t("common.microphone"))}
{!isFirefox() &&
generateDeviceSelection(
devices.audioOutput,
t("settings.speaker_device_selection_label"),
)}
<div className={styles.volumeSlider}>
<label>{t("settings.audio_tab.effect_volume_label")}</label>
<p>{t("settings.audio_tab.effect_volume_description")}</p>
<Slider
label={t("video_tile.volume")}
value={soundVolume}
onValueChange={setSoundVolume}
min={0}
max={1}
step={0.01}
/>
</div>
</Form>
<Separator />
<div className={styles.volumeSlider}>
<label>{t("settings.audio_tab.effect_volume_label")}</label>
<p>{t("settings.audio_tab.effect_volume_description")}</p>
<Slider
label={t("video_tile.volume")}
value={soundVolume}
onValueChange={setSoundVolume}
min={0}
max={1}
step={0.01}
/>
</div>
</>
),
};
@@ -122,14 +151,7 @@ export const SettingsModal: FC<Props> = ({
const videoTab: Tab<SettingsTab> = {
key: "video",
name: t("common.video"),
content: (
<Form>
<DeviceSelection
devices={devices.videoInput}
caption={t("common.camera")}
/>
</Form>
),
content: generateDeviceSelection(devices.videoInput, t("common.camera")),
};
const preferencesTab: Tab<SettingsTab> = {

View File

@@ -20,7 +20,6 @@ import {
ConnectionState,
LocalParticipant,
Participant,
ParticipantEvent,
RemoteParticipant,
} from "livekit-client";
import * as ComponentsCore from "@livekit/components-core";
@@ -31,7 +30,7 @@ import {
mockLivekitRoom,
mockLocalParticipant,
mockMatrixRoom,
mockMatrixRoomMember,
mockMember,
mockRemoteParticipant,
withTestScheduler,
} from "../utils/test";
@@ -43,10 +42,10 @@ import { E2eeType } from "../e2ee/e2eeType";
vi.mock("@livekit/components-core");
const alice = mockMatrixRoomMember({ userId: "@alice:example.org" });
const bob = mockMatrixRoomMember({ userId: "@bob:example.org" });
const carol = mockMatrixRoomMember({ userId: "@carol:example.org" });
const dave = mockMatrixRoomMember({ userId: "@dave:example.org" });
const alice = mockMember({ userId: "@alice:example.org" });
const bob = mockMember({ userId: "@bob:example.org" });
const carol = mockMember({ userId: "@carol:example.org" });
const dave = mockMember({ userId: "@dave:example.org" });
const aliceId = `${alice.userId}:AAAA`;
const bobId = `${bob.userId}:BBBB`;
@@ -189,15 +188,11 @@ function withCallViewModel(
);
const eventsSpy = vi
.spyOn(ComponentsCore, "observeParticipantEvents")
.mockImplementation((p, ...eventTypes) => {
if (eventTypes.includes(ParticipantEvent.IsSpeakingChanged)) {
return (speaking.get(p) ?? of(false)).pipe(
map((s) => ({ ...p, isSpeaking: s }) as Participant),
);
} else {
return of(p);
}
});
.mockImplementation((p) =>
(speaking.get(p) ?? of(false)).pipe(
map((s) => ({ ...p, isSpeaking: s }) as Participant),
),
);
const roomEventSelectorSpy = vi
.spyOn(ComponentsCore, "roomEventSelector")
@@ -234,7 +229,7 @@ function withCallViewModel(
}
test("participants are retained during a focus switch", () => {
withTestScheduler(({ hot, expectObservable }) => {
withTestScheduler(({ cold, expectObservable }) => {
// Participants disappear on frame 2 and come back on frame 3
const participantInputMarbles = "a-ba";
// Start switching focus on frame 1 and reconnect on frame 3
@@ -243,11 +238,11 @@ test("participants are retained during a focus switch", () => {
const expectedLayoutMarbles = " a";
withCallViewModel(
hot(participantInputMarbles, {
cold(participantInputMarbles, {
a: [aliceParticipant, bobParticipant],
b: [],
}),
hot(connectionInputMarbles, {
cold(connectionInputMarbles, {
c: ConnectionState.Connected,
s: ECAddonConnectionState.ECSwitchingFocus,
}),
@@ -269,7 +264,7 @@ test("participants are retained during a focus switch", () => {
});
test("screen sharing activates spotlight layout", () => {
withTestScheduler(({ hot, schedule, expectObservable }) => {
withTestScheduler(({ cold, schedule, expectObservable }) => {
// Start with no screen shares, then have Alice and Bob share their screens,
// then return to no screen shares, then have just Alice share for a bit
const participantInputMarbles = " abcda-ba";
@@ -282,7 +277,7 @@ test("screen sharing activates spotlight layout", () => {
const expectedLayoutMarbles = " abcdaefeg";
const expectedShowSpeakingMarbles = "y----nyny";
withCallViewModel(
hot(participantInputMarbles, {
cold(participantInputMarbles, {
a: [aliceParticipant, bobParticipant],
b: [aliceSharingScreen, bobParticipant],
c: [aliceSharingScreen, bobSharingScreen],
@@ -352,7 +347,7 @@ test("screen sharing activates spotlight layout", () => {
});
test("participants stay in the same order unless to appear/disappear", () => {
withTestScheduler(({ hot, schedule, expectObservable }) => {
withTestScheduler(({ cold, schedule, expectObservable }) => {
const modeInputMarbles = " a";
// First Bob speaks, then Dave, then Alice
const aSpeakingInputMarbles = "n- 1998ms - 1999ms y";
@@ -368,9 +363,9 @@ test("participants stay in the same order unless to appear/disappear", () => {
of([aliceParticipant, bobParticipant, daveParticipant]),
of(ConnectionState.Connected),
new Map([
[aliceParticipant, hot(aSpeakingInputMarbles, { y: true, n: false })],
[bobParticipant, hot(bSpeakingInputMarbles, { y: true, n: false })],
[daveParticipant, hot(dSpeakingInputMarbles, { y: true, n: false })],
[aliceParticipant, cold(aSpeakingInputMarbles, { y: true, n: false })],
[bobParticipant, cold(bSpeakingInputMarbles, { y: true, n: false })],
[daveParticipant, cold(dSpeakingInputMarbles, { y: true, n: false })],
]),
(vm) => {
schedule(modeInputMarbles, {
@@ -412,7 +407,7 @@ test("participants stay in the same order unless to appear/disappear", () => {
});
test("spotlight speakers swap places", () => {
withTestScheduler(({ hot, schedule, expectObservable }) => {
withTestScheduler(({ cold, schedule, expectObservable }) => {
// Go immediately into spotlight mode for the test
const modeInputMarbles = " s";
// First Bob speaks, then Dave, then Alice
@@ -429,9 +424,9 @@ test("spotlight speakers swap places", () => {
of([aliceParticipant, bobParticipant, daveParticipant]),
of(ConnectionState.Connected),
new Map([
[aliceParticipant, hot(aSpeakingInputMarbles, { y: true, n: false })],
[bobParticipant, hot(bSpeakingInputMarbles, { y: true, n: false })],
[daveParticipant, hot(dSpeakingInputMarbles, { y: true, n: false })],
[aliceParticipant, cold(aSpeakingInputMarbles, { y: true, n: false })],
[bobParticipant, cold(bSpeakingInputMarbles, { y: true, n: false })],
[daveParticipant, cold(dSpeakingInputMarbles, { y: true, n: false })],
]),
(vm) => {
schedule(modeInputMarbles, { s: () => vm.setGridMode("spotlight") });

View File

@@ -27,6 +27,7 @@ import {
EMPTY,
Observable,
Subject,
audit,
combineLatest,
concat,
distinctUntilChanged,
@@ -75,7 +76,6 @@ import { spotlightExpandedLayout } from "./SpotlightExpandedLayout";
import { oneOnOneLayout } from "./OneOnOneLayout";
import { pipLayout } from "./PipLayout";
import { EncryptionSystem } from "../e2ee/sharedKeyManagement";
import { observeSpeaker } from "./observeSpeaker";
// How long we wait after a focus switch before showing the real participant
// list again
@@ -248,7 +248,22 @@ class UserMedia {
livekitRoom,
);
this.speaker = observeSpeaker(this.vm.speaking).pipe(this.scope.state());
this.speaker = this.vm.speaking.pipe(
// Require 1 s of continuous speaking to become a speaker, and 60 s of
// continuous silence to stop being considered a speaker
audit((s) =>
merge(
timer(s ? 1000 : 60000),
// If the speaking flag resets to its original value during this time,
// end the silencing window to stick with that original value
this.vm.speaking.pipe(filter((s1) => s1 !== s)),
),
),
startWith(false),
// Make this Observable hot so that the timers don't reset when you
// resubscribe
this.scope.state(),
);
this.presenter = observeParticipantEvents(
participant,
@@ -291,7 +306,7 @@ class ScreenShare {
type MediaItem = UserMedia | ScreenShare;
function findMatrixRoomMember(
function findMatrixMember(
room: MatrixRoom,
id: string,
): RoomMember | undefined {
@@ -326,16 +341,12 @@ export class CallViewModel extends ViewModel {
}),
);
/**
* The raw list of RemoteParticipants as reported by LiveKit
*/
private readonly rawRemoteParticipants: Observable<RemoteParticipant[]> =
connectedParticipantsObserver(this.livekitRoom).pipe(this.scope.state());
private readonly rawRemoteParticipants = connectedParticipantsObserver(
this.livekitRoom,
).pipe(this.scope.state());
/**
* Lists of RemoteParticipants to "hold" on display, even if LiveKit claims that
* they've left
*/
// Lists of participants to "hold" on display, even if LiveKit claims that
// they've left
private readonly remoteParticipantHolds: Observable<RemoteParticipant[][]> =
this.connectionState.pipe(
withLatestFrom(this.rawRemoteParticipants),
@@ -370,9 +381,6 @@ export class CallViewModel extends ViewModel {
),
);
/**
* The RemoteParticipants including those that are being "held" on the screen
*/
private readonly remoteParticipants: Observable<RemoteParticipant[]> =
combineLatest(
[this.rawRemoteParticipants, this.remoteParticipantHolds],
@@ -394,9 +402,6 @@ export class CallViewModel extends ViewModel {
},
);
/**
* List of MediaItems that we want to display
*/
private readonly mediaItems: Observable<MediaItem[]> = combineLatest([
this.remoteParticipants,
observeParticipantMedia(this.livekitRoom.localParticipant),
@@ -413,7 +418,7 @@ export class CallViewModel extends ViewModel {
function* (this: CallViewModel): Iterable<[string, MediaItem]> {
for (const p of [localParticipant, ...remoteParticipants]) {
const id = p === localParticipant ? "local" : p.identity;
const member = findMatrixRoomMember(this.matrixRoom, id);
const member = findMatrixMember(this.matrixRoom, id);
if (member === undefined)
logger.warn(
`Ruh, roh! No matrix member found for SFU participant '${p.identity}': creating g-g-g-ghost!`,
@@ -466,9 +471,6 @@ export class CallViewModel extends ViewModel {
this.scope.state(),
);
/**
* List of MediaItems that we want to display, that are of type UserMedia
*/
private readonly userMedia: Observable<UserMedia[]> = this.mediaItems.pipe(
map((mediaItems) =>
mediaItems.filter((m): m is UserMedia => m instanceof UserMedia),
@@ -480,9 +482,6 @@ export class CallViewModel extends ViewModel {
map((ms) => ms.find((m) => m.vm.local)!.vm as LocalUserMediaViewModel),
);
/**
* List of MediaItems that we want to display, that are of type ScreenShare
*/
private readonly screenShares: Observable<ScreenShare[]> =
this.mediaItems.pipe(
map((mediaItems) =>
@@ -941,7 +940,7 @@ export class CallViewModel extends ViewModel {
this.scope.state(),
);
public readonly showFooter: Observable<boolean> = this.windowMode.pipe(
public readonly showFooter = this.windowMode.pipe(
switchMap((mode) => {
switch (mode) {
case "pip":

View File

@@ -1,119 +0,0 @@
/*
Copyright 2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/
import { describe, test } from "vitest";
import { withTestScheduler } from "../utils/test";
import { observeSpeaker } from "./observeSpeaker";
const yesNo = {
y: true,
n: false,
};
describe("observeSpeaker", () => {
describe("does not activate", () => {
const expectedOutputMarbles = "n";
test("starts correctly", () => {
// should default to false when no input is given
const speakingInputMarbles = "";
withTestScheduler(({ hot, expectObservable }) => {
expectObservable(observeSpeaker(hot(speakingInputMarbles, yesNo))).toBe(
expectedOutputMarbles,
yesNo,
);
});
});
test("after no speaking", () => {
const speakingInputMarbles = "n";
withTestScheduler(({ hot, expectObservable }) => {
expectObservable(observeSpeaker(hot(speakingInputMarbles, yesNo))).toBe(
expectedOutputMarbles,
yesNo,
);
});
});
test("with speaking for 1ms", () => {
const speakingInputMarbles = "y n";
withTestScheduler(({ hot, expectObservable }) => {
expectObservable(observeSpeaker(hot(speakingInputMarbles, yesNo))).toBe(
expectedOutputMarbles,
yesNo,
);
});
});
test("with speaking for 999ms", () => {
const speakingInputMarbles = "y 999ms n";
withTestScheduler(({ hot, expectObservable }) => {
expectObservable(observeSpeaker(hot(speakingInputMarbles, yesNo))).toBe(
expectedOutputMarbles,
yesNo,
);
});
});
test("with speaking intermittently", () => {
const speakingInputMarbles =
"y 199ms n 199ms y 199ms n 199ms y 199ms n 199ms y 199ms n 199ms y 199ms n 199ms y 199ms n 199ms y 199ms n 199ms y 199ms n";
withTestScheduler(({ hot, expectObservable }) => {
expectObservable(observeSpeaker(hot(speakingInputMarbles, yesNo))).toBe(
expectedOutputMarbles,
yesNo,
);
});
});
test("with consecutive speaking then stops speaking", () => {
const speakingInputMarbles = "y y y y y y y y y y n";
withTestScheduler(({ hot, expectObservable }) => {
expectObservable(observeSpeaker(hot(speakingInputMarbles, yesNo))).toBe(
expectedOutputMarbles,
yesNo,
);
});
});
});
describe("activates", () => {
test("after 1s", () => {
// this will active after 1s as no `n` follows it:
const speakingInputMarbles = " y";
const expectedOutputMarbles = "n 999ms y";
withTestScheduler(({ hot, expectObservable }) => {
expectObservable(observeSpeaker(hot(speakingInputMarbles, yesNo))).toBe(
expectedOutputMarbles,
yesNo,
);
});
});
test("speaking for 1001ms activates for 60s", () => {
const speakingInputMarbles = " y 1s n ";
const expectedOutputMarbles = "n 999ms y 60s n";
withTestScheduler(({ hot, expectObservable }) => {
expectObservable(observeSpeaker(hot(speakingInputMarbles, yesNo))).toBe(
expectedOutputMarbles,
yesNo,
);
});
});
test("speaking for 5s activates for 64s", () => {
const speakingInputMarbles = " y 5s n ";
const expectedOutputMarbles = "n 999ms y 64s n";
withTestScheduler(({ hot, expectObservable }) => {
expectObservable(observeSpeaker(hot(speakingInputMarbles, yesNo))).toBe(
expectedOutputMarbles,
yesNo,
);
});
});
});
});

View File

@@ -1,36 +0,0 @@
/*
Copyright 2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/
import {
Observable,
audit,
merge,
timer,
filter,
startWith,
distinctUntilChanged,
} from "rxjs";
/**
* Require 1 second of continuous speaking to become a speaker, and 60 second of
* continuous silence to stop being considered a speaker
*/
export function observeSpeaker(
isSpeakingObservable: Observable<boolean>,
): Observable<boolean> {
const distinct = isSpeakingObservable.pipe(distinctUntilChanged());
return distinct.pipe(
// Either change to the new value after the timer or re-emit the same value if it toggles back
// (audit will return the latest (toggled back) value) before the timeout.
audit((s) =>
merge(timer(s ? 1000 : 60000), distinct.pipe(filter((s1) => s1 !== s))),
),
// Filter the re-emissions (marked as: | ) that happen if we toggle quickly (<1s) from false->true->false|->..
startWith(false),
distinctUntilChanged(),
);
}

View File

@@ -34,6 +34,10 @@ Please see LICENSE in the repository root for full details.
object-fit: contain;
}
.media.videoMuted video {
display: none;
}
.bg {
background-color: var(--cpd-color-bg-subtle-secondary);
inline-size: 100%;
@@ -43,6 +47,7 @@ Please see LICENSE in the repository root for full details.
}
.avatar {
display: none;
position: absolute;
top: 50%;
left: 50%;
@@ -50,6 +55,10 @@ Please see LICENSE in the repository root for full details.
pointer-events: none;
}
.media.videoMuted .avatar {
display: initial;
}
/* CSS makes us put a condition here, even though all we want to do is
unconditionally select the container so we can use cqmin units */
@container mediaView (width > 0) {

View File

@@ -1,117 +0,0 @@
/*
Copyright 2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/
import { describe, expect, test } from "vitest";
import { render, screen } from "@testing-library/react";
import { axe } from "vitest-axe";
import { TooltipProvider } from "@vector-im/compound-web";
import {
TrackReference,
TrackReferencePlaceholder,
} from "@livekit/components-core";
import { Track, TrackPublication } from "livekit-client";
import { type ComponentProps } from "react";
import { MediaView } from "./MediaView";
import { EncryptionStatus } from "../state/MediaViewModel";
import { mockLocalParticipant } from "../utils/test";
describe("MediaView", () => {
const participant = mockLocalParticipant({});
const trackReferencePlaceholder: TrackReferencePlaceholder = {
participant,
source: Track.Source.Camera,
};
const trackReference: TrackReference = {
...trackReferencePlaceholder,
publication: new TrackPublication(Track.Kind.Video, "id", "name"),
};
const baseProps: ComponentProps<typeof MediaView> = {
displayName: "some name",
videoEnabled: true,
videoFit: "contain",
targetWidth: 300,
targetHeight: 200,
encryptionStatus: EncryptionStatus.Connecting,
mirror: false,
unencryptedWarning: false,
video: trackReference,
member: undefined,
};
test("is accessible", async () => {
const { container } = render(<MediaView {...baseProps} />);
expect(await axe(container)).toHaveNoViolations();
});
describe("placeholder track", () => {
test("neither video nor avatar are shown", () => {
render(<MediaView {...baseProps} video={trackReferencePlaceholder} />);
expect(screen.queryByTestId("video")).toBeNull();
expect(screen.queryAllByRole("img", { name: "some name" }).length).toBe(
0,
);
});
});
describe("name tag", () => {
test("is shown with name", () => {
render(<MediaView {...baseProps} displayName="Bob" />);
expect(screen.getByTestId("name_tag")).toHaveTextContent("Bob");
});
});
describe("unencryptedWarning", () => {
test("is shown and accessible", async () => {
const { container } = render(
<TooltipProvider>
<MediaView {...baseProps} unencryptedWarning={true} />
</TooltipProvider>,
);
expect(await axe(container)).toHaveNoViolations();
expect(
screen.getByRole("img", { name: "common.unencrypted" }),
).toBeTruthy();
});
test("is not shown", () => {
render(
<TooltipProvider>
<MediaView {...baseProps} unencryptedWarning={false} />
</TooltipProvider>,
);
expect(
screen.queryAllByRole("img", { name: "common.unencrypted" }).length,
).toBe(0);
});
});
describe("videoEnabled", () => {
test("just video is visible", () => {
render(
<TooltipProvider>
<MediaView {...baseProps} videoEnabled={true} />
</TooltipProvider>,
);
expect(screen.getByTestId("video")).toBeVisible();
expect(screen.queryAllByRole("img", { name: "some name" }).length).toBe(
0,
);
});
test("just avatar is visible", () => {
render(
<TooltipProvider>
<MediaView {...baseProps} videoEnabled={false} />
</TooltipProvider>,
);
expect(screen.getByRole("img", { name: "some name" })).toBeVisible();
expect(screen.getByTestId("video")).not.toBeVisible();
});
});
});

View File

@@ -76,6 +76,7 @@ export const MediaView = forwardRef<HTMLDivElement, Props>(
<animated.div
className={classNames(styles.media, className, {
[styles.mirror]: mirror,
[styles.videoMuted]: !videoEnabled,
})}
style={style}
ref={ref}
@@ -90,7 +91,6 @@ export const MediaView = forwardRef<HTMLDivElement, Props>(
size={avatarSize}
src={member?.getMxcAvatarUrl()}
className={styles.avatar}
style={{ display: videoEnabled ? "none" : "initial" }}
/>
{video.publication !== undefined && (
<VideoTrack
@@ -98,8 +98,6 @@ export const MediaView = forwardRef<HTMLDivElement, Props>(
// There's no reason for this to be focusable
tabIndex={-1}
disablePictureInPicture
style={{ display: videoEnabled ? "block" : "none" }}
data-testid="video"
/>
)}
</div>
@@ -135,13 +133,7 @@ export const MediaView = forwardRef<HTMLDivElement, Props>(
)*/}
<div className={styles.nameTag}>
{nameTagLeadingIcon}
<Text
as="span"
size="sm"
weight="medium"
className={styles.name}
data-testid="name_tag"
>
<Text as="span" size="sm" weight="medium" className={styles.name}>
{displayName}
</Text>
{unencryptedWarning && (
@@ -154,8 +146,6 @@ export const MediaView = forwardRef<HTMLDivElement, Props>(
width={20}
height={20}
className={styles.errorIcon}
role="img"
aria-label={t("common.unencrypted")}
/>
</Tooltip>
)}

View File

@@ -93,16 +93,6 @@ test("reactions can be sent via keyboard presses", async () => {
}
});
test("reaction is not sent when modifier key is held", async () => {
const user = userEvent.setup();
const sendReaction = vi.fn();
render(<TestComponent sendReaction={sendReaction} />);
await user.keyboard("{Meta>}1{/Meta}");
expect(sendReaction).not.toHaveBeenCalled();
});
test("raised hand can be sent via keyboard presses", async () => {
const user = userEvent.setup();

View File

@@ -43,8 +43,6 @@ export function useCallViewKeyboardShortcuts(
(event: KeyboardEvent) => {
if (focusElement.current === null) return;
if (!mayReceiveKeyEvents(focusElement.current)) return;
if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey)
return;
if (event.key === "m") {
event.preventDefault();

View File

@@ -99,7 +99,7 @@ function mockEmitter<T>(): EmitterMock<T> {
// Maybe it'd be good to move this to matrix-js-sdk? Our testing needs are
// rather simple, but if one util to mock a member is good enough for us, maybe
// it's useful for matrix-js-sdk consumers in general.
export function mockMatrixRoomMember(member: Partial<RoomMember>): RoomMember {
export function mockMember(member: Partial<RoomMember>): RoomMember {
return { ...mockEmitter(), ...member } as RoomMember;
}
@@ -149,7 +149,7 @@ export async function withLocalMedia(
const localParticipant = mockLocalParticipant({});
const vm = new LocalUserMediaViewModel(
"local",
mockMatrixRoomMember(member),
mockMember(member),
localParticipant,
{
kind: E2eeType.PER_PARTICIPANT,
@@ -184,7 +184,7 @@ export async function withRemoteMedia(
const remoteParticipant = mockRemoteParticipant(participant);
const vm = new RemoteUserMediaViewModel(
"remote",
mockMatrixRoomMember(member),
mockMember(member),
remoteParticipant,
{
kind: E2eeType.PER_PARTICIPANT,

View File

@@ -15,7 +15,6 @@ import { afterEach } from "vitest";
import { cleanup } from "@testing-library/react";
import "vitest-axe/extend-expect";
import { logger } from "matrix-js-sdk/src/logger";
import "@testing-library/jest-dom/vitest";
import EN_GB from "../locales/en-GB/app.json";
import { Config } from "./config/Config";

View File

@@ -17,11 +17,10 @@ import { getUrlParams } from "./UrlParams";
import { Config } from "./config/Config";
import { ElementCallReactionEventType } from "./reactions";
// Subset of the actions in element-web
// Subset of the actions in matrix-react-sdk
export enum ElementWidgetActions {
JoinCall = "io.element.join",
HangupCall = "im.vector.hangup",
Close = "io.element.close",
TileLayout = "io.element.tile_layout",
SpotlightLayout = "io.element.spotlight_layout",
// This can be sent as from or to widget

View File

@@ -82,10 +82,6 @@ export default defineConfig(({ mode }) => {
// Default naming fallback
return "assets/[name]-[hash][extname]";
},
manualChunks: {
// we should be able to remove this one https://github.com/matrix-org/matrix-rust-sdk-crypto-wasm/pull/167 lands
"matrix-sdk-crypto-wasm": ["@matrix-org/matrix-sdk-crypto-wasm"],
},
},
},
},

513
yarn.lock
View File

@@ -40,11 +40,6 @@
resolved "https://registry.yarnpkg.com/@actions/io/-/io-1.1.3.tgz#4cdb6254da7962b07473ff5c335f3da485d94d71"
integrity sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==
"@adobe/css-tools@^4.4.0":
version "4.4.1"
resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.4.1.tgz#2447a230bfe072c1659e6815129c03cf170710e3"
integrity sha512-12WGKBQzjUAI4ayyF4IAtfw2QR/IDoqk6jTddXDhtYTJF9ASmoE1zst7cVtP0aL/F1jUJL5r+JxKXKEgHNbEUQ==
"@ampproject/remapping@^2.2.0", "@ampproject/remapping@^2.3.0":
version "2.3.0"
resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4"
@@ -1806,9 +1801,9 @@
rxjs "7.8.1"
"@livekit/components-react@^2.0.0":
version "2.6.9"
resolved "https://registry.yarnpkg.com/@livekit/components-react/-/components-react-2.6.9.tgz#2ff4691dc2cae6ed4c4b2e586a255d00e494bf9c"
integrity sha512-j43i/Dm8dlI2jxv5wv0s+69QPVqVEjg0y2tyznfs/7RDcaIZsIIzNijPu1kLditerzvzQdRsOgFQ3UWONcTkGA==
version "2.6.8"
resolved "https://registry.yarnpkg.com/@livekit/components-react/-/components-react-2.6.8.tgz#faa60410aef0f5d426afcc6f9b577686983c6b7b"
integrity sha512-G6P+mrOyBiAnHjbmBTG28CxA6AT7wXT6/5dqu7M7uZAlvOCDKhPjhOs65awDQvaFlTxd/JlND75fa9d+oSbvIA==
dependencies:
"@livekit/components-core" "0.11.10"
clsx "2.1.1"
@@ -1949,10 +1944,10 @@
dependencies:
"@octokit/openapi-types" "^22.2.0"
"@opentelemetry/api-logs@0.55.0":
version "0.55.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.55.0.tgz#5cd7461820d864600250deb3803c32367a6bb2d2"
integrity sha512-3cpa+qI45VHYcA5c0bHM6VHo9gicv3p5mlLHNG3rLyjQU8b7e0st1rWtrUn3JbZ3DwwCfhKop4eQ9UuYlC6Pkg==
"@opentelemetry/api-logs@0.54.2":
version "0.54.2"
resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.54.2.tgz#bb8aa11cdc69b327b58d7e10cc2bc26bf540421f"
integrity sha512-4MTVwwmLgUh5QrJnZpYo6YRO5IBLAggf2h8gWDblwRagDStY13aEvt7gGk3jewrMaPlHiF83fENhIx0HO97/cQ==
dependencies:
"@opentelemetry/api" "^1.3.0"
@@ -1961,98 +1956,93 @@
resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.9.0.tgz#d03eba68273dc0f7509e2a3d5cba21eae10379fe"
integrity sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==
"@opentelemetry/core@1.28.0", "@opentelemetry/core@^1.25.1":
version "1.28.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.28.0.tgz#e97290a3e36c59480ffb2287fe2713c66749274c"
integrity sha512-ZLwRMV+fNDpVmF2WYUdBHlq0eOWtEaUJSusrzjGnBt7iSRvfjFE3RXYUZJrqou/wIDWV0DwQ5KIfYe9WXg9Xqw==
"@opentelemetry/core@1.27.0", "@opentelemetry/core@^1.25.1":
version "1.27.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.27.0.tgz#9f1701a654ab01abcebb12931b418f3393b94b75"
integrity sha512-yQPKnK5e+76XuiqUH/gKyS8wv/7qITd5ln56QkBTf3uggr0VkXOXfcaAuG330UfdYu83wsyoBwqwxigpIG+Jkg==
dependencies:
"@opentelemetry/semantic-conventions" "1.27.0"
"@opentelemetry/exporter-trace-otlp-http@^0.55.0":
version "0.55.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.55.0.tgz#275e458aa3bd33c52d77f4357706bcfa53d27f28"
integrity sha512-lMiNic63EVHpW+eChmLD2CieDmwQBFi72+LFbh8+5hY0ShrDGrsGP/zuT5MRh7M/vM/UZYO/2A/FYd7CMQGR7A==
"@opentelemetry/exporter-trace-otlp-http@^0.54.0":
version "0.54.2"
resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.54.2.tgz#e6fab84405b95ece2977a80e4cec907e568ef0f3"
integrity sha512-BgWKKyD/h2zpISdmYHN/sapwTjvt1P4p5yx4xeBV8XAEqh4OQUhOtSGFG80+nPQ1F8of3mKOT1DDoDbJp1u25w==
dependencies:
"@opentelemetry/core" "1.28.0"
"@opentelemetry/otlp-exporter-base" "0.55.0"
"@opentelemetry/otlp-transformer" "0.55.0"
"@opentelemetry/resources" "1.28.0"
"@opentelemetry/sdk-trace-base" "1.28.0"
"@opentelemetry/core" "1.27.0"
"@opentelemetry/otlp-exporter-base" "0.54.2"
"@opentelemetry/otlp-transformer" "0.54.2"
"@opentelemetry/resources" "1.27.0"
"@opentelemetry/sdk-trace-base" "1.27.0"
"@opentelemetry/otlp-exporter-base@0.55.0":
version "0.55.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.55.0.tgz#db17332497e4a97e4ca85d394fb91cbbcfd76d84"
integrity sha512-iHQI0Zzq3h1T6xUJTVFwmFl5Dt5y1es+fl4kM+k5T/3YvmVyeYkSiF+wHCg6oKrlUAJfk+t55kaAu3sYmt7ZYA==
"@opentelemetry/otlp-exporter-base@0.54.2":
version "0.54.2"
resolved "https://registry.yarnpkg.com/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.54.2.tgz#fb2361665baec9e9600c5408747fc03124889f0a"
integrity sha512-NrNyxu6R/bGAwanhz1HI0aJWKR6xUED4TjCH4iWMlAfyRukGbI9Kt/Akd2sYLwRKNhfS+sKetKGCUQPMDyYYMA==
dependencies:
"@opentelemetry/core" "1.28.0"
"@opentelemetry/otlp-transformer" "0.55.0"
"@opentelemetry/core" "1.27.0"
"@opentelemetry/otlp-transformer" "0.54.2"
"@opentelemetry/otlp-transformer@0.55.0":
version "0.55.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/otlp-transformer/-/otlp-transformer-0.55.0.tgz#316b9325983e660cb4f18cb76fa84ce1c0cdad42"
integrity sha512-kVqEfxtp6mSN2Dhpy0REo1ghP4PYhC1kMHQJ2qVlO99Pc+aigELjZDfg7/YKmL71gR6wVGIeJfiql/eXL7sQPA==
"@opentelemetry/otlp-transformer@0.54.2":
version "0.54.2"
resolved "https://registry.yarnpkg.com/@opentelemetry/otlp-transformer/-/otlp-transformer-0.54.2.tgz#5952072cf37a7d5da0ac5491426126459c13c839"
integrity sha512-2tIjahJlMRRUz0A2SeE+qBkeBXBFkSjR0wqJ08kuOqaL8HNGan5iZf+A8cfrfmZzPUuMKCyY9I+okzFuFs6gKQ==
dependencies:
"@opentelemetry/api-logs" "0.55.0"
"@opentelemetry/core" "1.28.0"
"@opentelemetry/resources" "1.28.0"
"@opentelemetry/sdk-logs" "0.55.0"
"@opentelemetry/sdk-metrics" "1.28.0"
"@opentelemetry/sdk-trace-base" "1.28.0"
"@opentelemetry/api-logs" "0.54.2"
"@opentelemetry/core" "1.27.0"
"@opentelemetry/resources" "1.27.0"
"@opentelemetry/sdk-logs" "0.54.2"
"@opentelemetry/sdk-metrics" "1.27.0"
"@opentelemetry/sdk-trace-base" "1.27.0"
protobufjs "^7.3.0"
"@opentelemetry/resources@1.28.0", "@opentelemetry/resources@^1.25.1":
version "1.28.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.28.0.tgz#c8c27ae7559c817f9d117f1bf96d76f893fb29f5"
integrity sha512-cIyXSVJjGeTICENN40YSvLDAq4Y2502hGK3iN7tfdynQLKWb3XWZQEkPc+eSx47kiy11YeFAlYkEfXwR1w8kfw==
"@opentelemetry/resources@1.27.0", "@opentelemetry/resources@^1.25.1":
version "1.27.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.27.0.tgz#1f91c270eb95be32f3511e9e6624c1c0f993c4ac"
integrity sha512-jOwt2VJ/lUD5BLc+PMNymDrUCpm5PKi1E9oSVYAvz01U/VdndGmrtV3DU1pG4AwlYhJRHbHfOUIlpBeXCPw6QQ==
dependencies:
"@opentelemetry/core" "1.28.0"
"@opentelemetry/core" "1.27.0"
"@opentelemetry/semantic-conventions" "1.27.0"
"@opentelemetry/sdk-logs@0.55.0":
version "0.55.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-logs/-/sdk-logs-0.55.0.tgz#78844e502167723a258c75a6b4f3de3900c13ea3"
integrity sha512-TSx+Yg/d48uWW6HtjS1AD5x6WPfLhDWLl/WxC7I2fMevaiBuKCuraxTB8MDXieCNnBI24bw9ytyXrDCswFfWgA==
"@opentelemetry/sdk-logs@0.54.2":
version "0.54.2"
resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-logs/-/sdk-logs-0.54.2.tgz#07cc61135b5acb09affa8cd290966027ee8c886a"
integrity sha512-yIbYqDLS/AtBbPjCjh6eSToGNRMqW2VR8RrKEy+G+J7dFG7pKoptTH5T+XlKPleP9NY8JZYIpgJBlI+Osi0rFw==
dependencies:
"@opentelemetry/api-logs" "0.55.0"
"@opentelemetry/core" "1.28.0"
"@opentelemetry/resources" "1.28.0"
"@opentelemetry/api-logs" "0.54.2"
"@opentelemetry/core" "1.27.0"
"@opentelemetry/resources" "1.27.0"
"@opentelemetry/sdk-metrics@1.28.0":
version "1.28.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-metrics/-/sdk-metrics-1.28.0.tgz#257b5295bbe9de1ad31c5e8cb43a660c25911d20"
integrity sha512-43tqMK/0BcKTyOvm15/WQ3HLr0Vu/ucAl/D84NO7iSlv6O4eOprxSHa3sUtmYkaZWHqdDJV0AHVz/R6u4JALVQ==
"@opentelemetry/sdk-metrics@1.27.0":
version "1.27.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-metrics/-/sdk-metrics-1.27.0.tgz#fb4f55017dc95a95ee00260262952b18e3e7c25c"
integrity sha512-JzWgzlutoXCydhHWIbLg+r76m+m3ncqvkCcsswXAQ4gqKS+LOHKhq+t6fx1zNytvLuaOUBur7EvWxECc4jPQKg==
dependencies:
"@opentelemetry/core" "1.28.0"
"@opentelemetry/resources" "1.28.0"
"@opentelemetry/core" "1.27.0"
"@opentelemetry/resources" "1.27.0"
"@opentelemetry/sdk-trace-base@1.28.0", "@opentelemetry/sdk-trace-base@^1.25.1":
version "1.28.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.28.0.tgz#6195dc8cd78bd74394cf54c67c5cbd8d1528516c"
integrity sha512-ceUVWuCpIao7Y5xE02Xs3nQi0tOGmMea17ecBdwtCvdo9ekmO+ijc9RFDgfifMl7XCBf41zne/1POM3LqSTZDA==
"@opentelemetry/sdk-trace-base@1.27.0", "@opentelemetry/sdk-trace-base@^1.25.1":
version "1.27.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.27.0.tgz#2276e4cd0d701a8faba77382b2938853a0907b54"
integrity sha512-btz6XTQzwsyJjombpeqCX6LhiMQYpzt2pIYNPnw0IPO/3AhT6yjnf8Mnv3ZC2A4eRYOjqrg+bfaXg9XHDRJDWQ==
dependencies:
"@opentelemetry/core" "1.28.0"
"@opentelemetry/resources" "1.28.0"
"@opentelemetry/core" "1.27.0"
"@opentelemetry/resources" "1.27.0"
"@opentelemetry/semantic-conventions" "1.27.0"
"@opentelemetry/sdk-trace-web@^1.9.1":
version "1.28.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-web/-/sdk-trace-web-1.28.0.tgz#0b8652a05ded13308c7afd162ca2ba55ff204efd"
integrity sha512-/QOIrJc/A/caKbA9voLua4isf///cjQKB6gomEzX2fL18TBqZhIkm9k2DpjlbtrQoYCJDZ9x7Phrec22aQGpQw==
version "1.27.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-web/-/sdk-trace-web-1.27.0.tgz#cdc9b43aab44b12741e408fb70b2bc0e941f0c7c"
integrity sha512-ORZfG8Sm5IkJeI+P8MyW8v4m5OcmjEtD7VsjBghv5sDKH3f5p2mQpEEoJWlCr5GiW50Y1MaI2R4uFGIsxmDE9A==
dependencies:
"@opentelemetry/core" "1.28.0"
"@opentelemetry/sdk-trace-base" "1.28.0"
"@opentelemetry/core" "1.27.0"
"@opentelemetry/sdk-trace-base" "1.27.0"
"@opentelemetry/semantic-conventions" "1.27.0"
"@opentelemetry/semantic-conventions@1.27.0":
"@opentelemetry/semantic-conventions@1.27.0", "@opentelemetry/semantic-conventions@^1.25.1":
version "1.27.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.27.0.tgz#1a857dcc95a5ab30122e04417148211e6f945e6c"
integrity sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg==
"@opentelemetry/semantic-conventions@^1.25.1":
version "1.28.0"
resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz#337fb2bca0453d0726696e745f50064411f646d6"
integrity sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==
"@parcel/watcher-android-arm64@2.5.0":
version "2.5.0"
resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz#e32d3dda6647791ee930556aee206fcd5ea0fb7a"
@@ -2664,56 +2654,61 @@
resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8"
integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==
"@sentry-internal/browser-utils@8.40.0":
version "8.40.0"
resolved "https://registry.yarnpkg.com/@sentry-internal/browser-utils/-/browser-utils-8.40.0.tgz#972925a9d600723dd1a022297100e97e92f4c903"
integrity sha512-tx7gb/PWMbTEyil/XPETVeRUeS3nKHIvQY2omyebw30TbhyLnibPZsUmXJiaIysL5PcY3k9maub3W/o0Y37T7Q==
"@sentry-internal/browser-utils@8.38.0":
version "8.38.0"
resolved "https://registry.yarnpkg.com/@sentry-internal/browser-utils/-/browser-utils-8.38.0.tgz#d7f6d398778906efb0c017e63d3c59d3203dfa7d"
integrity sha512-5QMVcssrAcmjKT0NdFYcX0b0wwZovGAZ9L2GajErXtHkBenjI2sgR2+5J7n+QZGuk2SC1qhGmT1O9i3p3UEwew==
dependencies:
"@sentry/core" "8.40.0"
"@sentry/types" "8.40.0"
"@sentry/core" "8.38.0"
"@sentry/types" "8.38.0"
"@sentry/utils" "8.38.0"
"@sentry-internal/feedback@8.40.0":
version "8.40.0"
resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-8.40.0.tgz#5549f73d32b9a2509ffb0a07bf462ed8085178ec"
integrity sha512-1O9F3z80HNE0VfepKS+v+dixdatNqWlrlwgvvWl4BGzzoA+XhqvZo+HWxiOt7yx7+k1TuZNrB6Gy3u/QvpozXA==
"@sentry-internal/feedback@8.38.0":
version "8.38.0"
resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-8.38.0.tgz#726661a01f7ff40b93c8ee05c985fd0436a1c033"
integrity sha512-AW5HCCAlc3T1jcSuNhbFVNO1CHyJ5g5tsGKEP4VKgu+D1Gg2kZ5S2eFatLBUP/BD5JYb1A7p6XPuzYp1XfMq0A==
dependencies:
"@sentry/core" "8.40.0"
"@sentry/types" "8.40.0"
"@sentry/core" "8.38.0"
"@sentry/types" "8.38.0"
"@sentry/utils" "8.38.0"
"@sentry-internal/replay-canvas@8.40.0":
version "8.40.0"
resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-8.40.0.tgz#6de0d67ee2fe3e503c6f85faeefab5df742a3ebe"
integrity sha512-Zr+m/le0SH4RowZB7rBCM0aRnvH3wZTaOFhwUk03/oGf2BRcgKuDCUMjnXKC9MyOpmey7UYXkzb8ro+81R6Q8w==
"@sentry-internal/replay-canvas@8.38.0":
version "8.38.0"
resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-8.38.0.tgz#26e9bc937dab73e1a26d57dc1015b7ff1f2d76c5"
integrity sha512-OxmlWzK9J8mRM+KxdSnQ5xuxq+p7TiBzTz70FT3HltxmeugvDkyp6803UcFqHOPHR35OYeVLOalym+FmvNn9kw==
dependencies:
"@sentry-internal/replay" "8.40.0"
"@sentry/core" "8.40.0"
"@sentry/types" "8.40.0"
"@sentry-internal/replay" "8.38.0"
"@sentry/core" "8.38.0"
"@sentry/types" "8.38.0"
"@sentry/utils" "8.38.0"
"@sentry-internal/replay@8.40.0":
version "8.40.0"
resolved "https://registry.yarnpkg.com/@sentry-internal/replay/-/replay-8.40.0.tgz#54c7f1e3d115f9324f34e1b8875a95463a23049f"
integrity sha512-0SaDsBCSWxNVgNmPKu23frrHEXzN/MKl0hIkfuO55vL5TgjLTwpgkf0Ne4rNvaZQ5omIKk9Qd63HuQP3PHAMaw==
"@sentry-internal/replay@8.38.0":
version "8.38.0"
resolved "https://registry.yarnpkg.com/@sentry-internal/replay/-/replay-8.38.0.tgz#9a9b945a3c066f5610a363774e3c99420c3f4fce"
integrity sha512-mQPShKnIab7oKwkwrRxP/D8fZYHSkDY+cvqORzgi+wAwgnunytJQjz9g6Ww2lJu98rHEkr5SH4V4rs6PZYZmnQ==
dependencies:
"@sentry-internal/browser-utils" "8.40.0"
"@sentry/core" "8.40.0"
"@sentry/types" "8.40.0"
"@sentry-internal/browser-utils" "8.38.0"
"@sentry/core" "8.38.0"
"@sentry/types" "8.38.0"
"@sentry/utils" "8.38.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/browser@8.40.0":
version "8.40.0"
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-8.40.0.tgz#de7b4531be2ac4667755e9e1b5da3808851392ae"
integrity sha512-m/Yor6IDBeDHtQochu8n6z4HXrXkrPhu6+o5Ouve0Zi3ptthSoK1FOGvJxVBat3nRq0ydQyuuPuTB6WfdWbwHQ==
"@sentry/browser@8.38.0":
version "8.38.0"
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-8.38.0.tgz#c562accdc2bbe0b0074d98bfe7ff460e39ce3109"
integrity sha512-AZR+b0EteNZEGv6JSdBD22S9VhQ7nrljKsSnzxobBULf3BpwmhmCzTbDrqWszKDAIDYmL+yQJIR2glxbknneWQ==
dependencies:
"@sentry-internal/browser-utils" "8.40.0"
"@sentry-internal/feedback" "8.40.0"
"@sentry-internal/replay" "8.40.0"
"@sentry-internal/replay-canvas" "8.40.0"
"@sentry/core" "8.40.0"
"@sentry/types" "8.40.0"
"@sentry-internal/browser-utils" "8.38.0"
"@sentry-internal/feedback" "8.38.0"
"@sentry-internal/replay" "8.38.0"
"@sentry-internal/replay-canvas" "8.38.0"
"@sentry/core" "8.38.0"
"@sentry/types" "8.38.0"
"@sentry/utils" "8.38.0"
"@sentry/bundler-plugin-core@2.22.6":
version "2.22.6"
@@ -2783,27 +2778,36 @@
"@sentry/cli-win32-i686" "2.38.1"
"@sentry/cli-win32-x64" "2.38.1"
"@sentry/core@8.40.0":
version "8.40.0"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.40.0.tgz#cb5c02d12e29070bf88692c64cfd7db7700be4ea"
integrity sha512-u/U2CJpG/+SmTR2bPM4ZZoPYTJAOUuxzj/0IURnvI0v9+rNu939J/fzrO9huA5IJVxS5TiYykhQm7o6I3Zuo3Q==
"@sentry/core@8.38.0":
version "8.38.0"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.38.0.tgz#5d1b74770c79e489e786018a3e514cddeb777bcb"
integrity sha512-sGD+5TEHU9G7X7zpyaoJxpOtwjTjvOd1f/MKBrWW2vf9UbYK+GUJrOzLhMoSWp/pHSYgvObkJkDb/HwieQjvhQ==
dependencies:
"@sentry/types" "8.40.0"
"@sentry/types" "8.38.0"
"@sentry/utils" "8.38.0"
"@sentry/react@^8.0.0":
version "8.40.0"
resolved "https://registry.yarnpkg.com/@sentry/react/-/react-8.40.0.tgz#9dfbbeaaeb8731103292d771c52b3d06b7e1500b"
integrity sha512-Ohq/po83r9sh/DCO6VAxx4xU+1ztvFzmXTl3fUnAEc+2bFJK1MsRt6BWfG37XxjQN//mfmyS9KEBgsOpOyd4LQ==
version "8.38.0"
resolved "https://registry.yarnpkg.com/@sentry/react/-/react-8.38.0.tgz#513cbd9ba35bb0258d10b74d272800cbc5f05631"
integrity sha512-5396tewO00wbJFHUkmU+ikmp4A+wuBpStNc7UDyAm642jfbPajj51+GWld/ZYNFiQaZ/8I9tvvpHqVLnUh21gg==
dependencies:
"@sentry/browser" "8.40.0"
"@sentry/core" "8.40.0"
"@sentry/types" "8.40.0"
"@sentry/browser" "8.38.0"
"@sentry/core" "8.38.0"
"@sentry/types" "8.38.0"
"@sentry/utils" "8.38.0"
hoist-non-react-statics "^3.3.2"
"@sentry/types@8.40.0":
version "8.40.0"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.40.0.tgz#a98d2bcc48adbc066b403713688ded3ac5eb1cec"
integrity sha512-nuCf3U3deolPM9BjNnwCc33UtFl9ec15/r74ngAkNccn+A2JXdIAsDkGJMO/9mgSFykLe1QyeJ0pQFRisCGOiA==
"@sentry/types@8.38.0":
version "8.38.0"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.38.0.tgz#9c48734a8b4055bfd553a0141efec78e9680ed09"
integrity sha512-fP5H9ZX01W4Z/EYctk3mkSHi7d06cLcX2/UWqwdWbyPWI+pL2QpUPICeO/C+8SnmYx//wFj3qWDhyPCh1PdFAA==
"@sentry/utils@8.38.0":
version "8.38.0"
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-8.38.0.tgz#2f91ca7d044f6e17b993c866ca02a981c4c1bc25"
integrity sha512-3X7MgIKIx+2q5Al7QkhaRB4wV6DvzYsaeIwdqKUzGLuRjXmNgJrLoU87TAwQRmZ6Wr3IoEpThZZMNrzYPXxArw==
dependencies:
"@sentry/types" "8.38.0"
"@sentry/vite-plugin@^2.0.0":
version "2.22.6"
@@ -2919,19 +2923,6 @@
lz-string "^1.5.0"
pretty-format "^27.0.2"
"@testing-library/jest-dom@^6.6.3":
version "6.6.3"
resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-6.6.3.tgz#26ba906cf928c0f8172e182c6fe214eb4f9f2bd2"
integrity sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==
dependencies:
"@adobe/css-tools" "^4.4.0"
aria-query "^5.0.0"
chalk "^3.0.0"
css.escape "^1.5.1"
dom-accessibility-api "^0.6.3"
lodash "^4.17.21"
redent "^3.0.0"
"@testing-library/react-hooks@^8.0.1":
version "8.0.1"
resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-8.0.1.tgz#0924bbd5b55e0c0c0502d1754657ada66947ca12"
@@ -3066,9 +3057,9 @@
undici-types "~6.19.8"
"@types/node@^20.0.0":
version "20.17.7"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.17.7.tgz#790151a28c5a172773d95d53a0c23d3c59a883c4"
integrity sha512-sZXXnpBFMKbao30dUAvzKbdwA2JM1fwUtVEq/kxKuPI5mMwZiRElCpTXb0Biq/LMEVpXDZL5G5V0RPnxKeyaYg==
version "20.17.6"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.17.6.tgz#6e4073230c180d3579e8c60141f99efdf5df0081"
integrity sha512-VEI7OdvK2wP7XHnsuXbAJnEpEkF6NjSN45QJlL4VGqZSXsnicpesdTWsg9RISeSdYd3yeRj/y3k5KGjUXYnFwQ==
dependencies:
undici-types "~6.19.2"
@@ -3169,15 +3160,15 @@
"@types/yargs-parser" "*"
"@typescript-eslint/eslint-plugin@^8.0.0":
version "8.15.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.15.0.tgz#c95c6521e70c8b095a684d884d96c0c1c63747d2"
integrity sha512-+zkm9AR1Ds9uLWN3fkoeXgFppaQ+uEVtfOV62dDmsy9QCNqlRHWNEck4yarvRNrvRcHQLGfqBNui3cimoz8XAg==
version "8.14.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.14.0.tgz#7dc0e419c87beadc8f554bf5a42e5009ed3748dc"
integrity sha512-tqp8H7UWFaZj0yNO6bycd5YjMwxa6wIHOLZvWPkidwbgLCsBMetQoGj7DPuAlWa2yGO3H48xmPwjhsSPPCGU5w==
dependencies:
"@eslint-community/regexpp" "^4.10.0"
"@typescript-eslint/scope-manager" "8.15.0"
"@typescript-eslint/type-utils" "8.15.0"
"@typescript-eslint/utils" "8.15.0"
"@typescript-eslint/visitor-keys" "8.15.0"
"@typescript-eslint/scope-manager" "8.14.0"
"@typescript-eslint/type-utils" "8.14.0"
"@typescript-eslint/utils" "8.14.0"
"@typescript-eslint/visitor-keys" "8.14.0"
graphemer "^1.4.0"
ignore "^5.3.1"
natural-compare "^1.4.0"
@@ -3191,14 +3182,14 @@
"@typescript-eslint/utils" "5.62.0"
"@typescript-eslint/parser@^8.0.0":
version "8.15.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.15.0.tgz#92610da2b3af702cfbc02a46e2a2daa6260a9045"
integrity sha512-7n59qFpghG4uazrF9qtGKBZXn7Oz4sOMm8dwNWDQY96Xlm2oX67eipqcblDj+oY1lLCbf1oltMZFpUso66Kl1A==
version "8.14.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.14.0.tgz#0a7e9dbc11bc07716ab2d7b1226217e9f6b51fc8"
integrity sha512-2p82Yn9juUJq0XynBXtFCyrBDb6/dJombnz6vbo6mgQEtWHfvHbQuEa9kAOVIt1c9YFwi7H6WxtPj1kg+80+RA==
dependencies:
"@typescript-eslint/scope-manager" "8.15.0"
"@typescript-eslint/types" "8.15.0"
"@typescript-eslint/typescript-estree" "8.15.0"
"@typescript-eslint/visitor-keys" "8.15.0"
"@typescript-eslint/scope-manager" "8.14.0"
"@typescript-eslint/types" "8.14.0"
"@typescript-eslint/typescript-estree" "8.14.0"
"@typescript-eslint/visitor-keys" "8.14.0"
debug "^4.3.4"
"@typescript-eslint/scope-manager@5.62.0":
@@ -3209,21 +3200,21 @@
"@typescript-eslint/types" "5.62.0"
"@typescript-eslint/visitor-keys" "5.62.0"
"@typescript-eslint/scope-manager@8.15.0":
version "8.15.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.15.0.tgz#28a1a0f13038f382424f45a988961acaca38f7c6"
integrity sha512-QRGy8ADi4J7ii95xz4UoiymmmMd/zuy9azCaamnZ3FM8T5fZcex8UfJcjkiEZjJSztKfEBe3dZ5T/5RHAmw2mA==
"@typescript-eslint/scope-manager@8.14.0":
version "8.14.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.14.0.tgz#01f37c147a735cd78f0ff355e033b9457da1f373"
integrity sha512-aBbBrnW9ARIDn92Zbo7rguLnqQ/pOrUguVpbUwzOhkFg2npFDwTgPGqFqE0H5feXcOoJOfX3SxlJaKEVtq54dw==
dependencies:
"@typescript-eslint/types" "8.15.0"
"@typescript-eslint/visitor-keys" "8.15.0"
"@typescript-eslint/types" "8.14.0"
"@typescript-eslint/visitor-keys" "8.14.0"
"@typescript-eslint/type-utils@8.15.0":
version "8.15.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.15.0.tgz#a6da0f93aef879a68cc66c73fe42256cb7426c72"
integrity sha512-UU6uwXDoI3JGSXmcdnP5d8Fffa2KayOhUUqr/AiBnG1Gl7+7ut/oyagVeSkh7bxQ0zSXV9ptRh/4N15nkCqnpw==
"@typescript-eslint/type-utils@8.14.0":
version "8.14.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.14.0.tgz#455c6af30c336b24a1af28bc4f81b8dd5d74d94d"
integrity sha512-Xcz9qOtZuGusVOH5Uk07NGs39wrKkf3AxlkK79RBK6aJC1l03CobXjJbwBPSidetAOV+5rEVuiT1VSBUOAsanQ==
dependencies:
"@typescript-eslint/typescript-estree" "8.15.0"
"@typescript-eslint/utils" "8.15.0"
"@typescript-eslint/typescript-estree" "8.14.0"
"@typescript-eslint/utils" "8.14.0"
debug "^4.3.4"
ts-api-utils "^1.3.0"
@@ -3232,10 +3223,10 @@
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f"
integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==
"@typescript-eslint/types@8.15.0":
version "8.15.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.15.0.tgz#4958edf3d83e97f77005f794452e595aaf6430fc"
integrity sha512-n3Gt8Y/KyJNe0S3yDCD2RVKrHBC4gTUcLTebVBXacPy091E6tNspFLKRXlk3hwT4G55nfr1n2AdFqi/XMxzmPQ==
"@typescript-eslint/types@8.14.0":
version "8.14.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.14.0.tgz#0d33d8d0b08479c424e7d654855fddf2c71e4021"
integrity sha512-yjeB9fnO/opvLJFAsPNYlKPnEM8+z4og09Pk504dkqonT02AyL5Z9SSqlE0XqezS93v6CXn49VHvB2G7XSsl0g==
"@typescript-eslint/typescript-estree@5.62.0":
version "5.62.0"
@@ -3250,13 +3241,13 @@
semver "^7.3.7"
tsutils "^3.21.0"
"@typescript-eslint/typescript-estree@8.15.0":
version "8.15.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.15.0.tgz#915c94e387892b114a2a2cc0df2d7f19412c8ba7"
integrity sha512-1eMp2JgNec/niZsR7ioFBlsh/Fk0oJbhaqO0jRyQBMgkz7RrFfkqF9lYYmBoGBaSiLnu8TAPQTwoTUiSTUW9dg==
"@typescript-eslint/typescript-estree@8.14.0":
version "8.14.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.14.0.tgz#a7a3a5a53a6c09313e12fb4531d4ff582ee3c312"
integrity sha512-OPXPLYKGZi9XS/49rdaCbR5j/S14HazviBlUQFvSKz3npr3NikF+mrgK7CFVur6XEt95DZp/cmke9d5i3vtVnQ==
dependencies:
"@typescript-eslint/types" "8.15.0"
"@typescript-eslint/visitor-keys" "8.15.0"
"@typescript-eslint/types" "8.14.0"
"@typescript-eslint/visitor-keys" "8.14.0"
debug "^4.3.4"
fast-glob "^3.3.2"
is-glob "^4.0.3"
@@ -3278,15 +3269,15 @@
eslint-scope "^5.1.1"
semver "^7.3.7"
"@typescript-eslint/utils@8.15.0":
version "8.15.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.15.0.tgz#ac04679ad19252776b38b81954b8e5a65567cef6"
integrity sha512-k82RI9yGhr0QM3Dnq+egEpz9qB6Un+WLYhmoNcvl8ltMEededhh7otBVVIDDsEEttauwdY/hQoSsOv13lxrFzQ==
"@typescript-eslint/utils@8.14.0":
version "8.14.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.14.0.tgz#ac2506875e03aba24e602364e43b2dfa45529dbd"
integrity sha512-OGqj6uB8THhrHj0Fk27DcHPojW7zKwKkPmHXHvQ58pLYp4hy8CSUdTKykKeh+5vFqTTVmjz0zCOOPKRovdsgHA==
dependencies:
"@eslint-community/eslint-utils" "^4.4.0"
"@typescript-eslint/scope-manager" "8.15.0"
"@typescript-eslint/types" "8.15.0"
"@typescript-eslint/typescript-estree" "8.15.0"
"@typescript-eslint/scope-manager" "8.14.0"
"@typescript-eslint/types" "8.14.0"
"@typescript-eslint/typescript-estree" "8.14.0"
"@typescript-eslint/visitor-keys@5.62.0":
version "5.62.0"
@@ -3296,13 +3287,13 @@
"@typescript-eslint/types" "5.62.0"
eslint-visitor-keys "^3.3.0"
"@typescript-eslint/visitor-keys@8.15.0":
version "8.15.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.15.0.tgz#9ea5a85eb25401d2aa74ec8a478af4e97899ea12"
integrity sha512-h8vYOulWec9LhpwfAdZf2bjr8xIp0KNKnpgqSz0qqYYKAW/QZKw3ktRndbiAtUz4acH4QLQavwZBYCc0wulA/Q==
"@typescript-eslint/visitor-keys@8.14.0":
version "8.14.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.14.0.tgz#2418d5a54669af9658986ade4e6cfb7767d815ad"
integrity sha512-vG0XZo8AdTH9OE6VFRwAZldNc7qtJ/6NLGWak+BtENuEUXGZgFpihILPiBvKXvJ2nFu27XNGC6rKiwuaoMbYzQ==
dependencies:
"@typescript-eslint/types" "8.15.0"
eslint-visitor-keys "^4.2.0"
"@typescript-eslint/types" "8.14.0"
eslint-visitor-keys "^3.4.3"
"@ungap/structured-clone@^1.2.0":
version "1.2.0"
@@ -3329,9 +3320,9 @@
prettier "^3.3.3"
"@vector-im/compound-web@^7.2.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==
version "7.3.0"
resolved "https://registry.yarnpkg.com/@vector-im/compound-web/-/compound-web-7.3.0.tgz#9594113ac50bff4794715104a30a60c52d15517d"
integrity sha512-gDppQUtpk5LvNHUg+Zlv9qzo1iBAag0s3g8Ec0qS5q4zGBKG6ruXXrNUKg1aK8rpbo2hYQsGaHM6dD8NqLoq3Q==
dependencies:
"@floating-ui/react" "^0.26.24"
"@radix-ui/react-context-menu" "^2.2.1"
@@ -3545,7 +3536,7 @@ aria-query@5.3.0:
dependencies:
dequal "^2.0.3"
aria-query@^5.0.0, aria-query@^5.3.2:
aria-query@^5.3.2:
version "5.3.2"
resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.2.tgz#93f81a43480e33a338f19163a3d10a50c01dcd59"
integrity sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==
@@ -3837,7 +3828,7 @@ broccoli-plugin@^4.0.7:
rimraf "^3.0.2"
symlink-or-copy "^1.3.1"
browserslist@^4.23.1, browserslist@^4.23.3, browserslist@^4.24.0, browserslist@^4.24.2:
browserslist@^4.23.1, browserslist@^4.23.3, browserslist@^4.24.0:
version "4.24.2"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.2.tgz#f5845bc91069dbd55ee89faf9822e1d885d16580"
integrity sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==
@@ -3903,16 +3894,11 @@ camelcase@^6.2.0:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
caniuse-lite@^1.0.30001646:
caniuse-lite@^1.0.30001646, caniuse-lite@^1.0.30001669:
version "1.0.30001680"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001680.tgz#5380ede637a33b9f9f1fc6045ea99bd142f3da5e"
integrity sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA==
caniuse-lite@^1.0.30001669:
version "1.0.30001684"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001684.tgz#0eca437bab7d5f03452ff0ef9de8299be6b08e16"
integrity sha512-G1LRwLIQjBQoyq0ZJGqGIJUXzJ8irpbjHLpVRXDvBEScFJ9b17sgK6vlx0GAJFE21okD7zXl08rRRUfq6HdoEQ==
caseless@~0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
@@ -3946,14 +3932,6 @@ chalk@^2.4.2:
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
chalk@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4"
integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==
dependencies:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
chalk@^5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385"
@@ -4016,9 +3994,9 @@ chokidar@^4.0.0:
readdirp "^4.0.1"
ci-info@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.1.0.tgz#92319d2fa29d2620180ea5afed31f589bc98cf83"
integrity sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A==
version "4.0.0"
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.0.0.tgz#65466f8b280fc019b9f50a5388115d17a63a44f2"
integrity sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==
classnames@^2.3.1, classnames@^2.5.1:
version "2.5.1"
@@ -4148,20 +4126,13 @@ copy-to-clipboard@^3.3.1:
dependencies:
toggle-selection "^1.0.6"
core-js-compat@^3.38.0:
core-js-compat@^3.38.0, core-js-compat@^3.38.1:
version "3.38.1"
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.38.1.tgz#2bc7a298746ca5a7bcb9c164bcb120f2ebc09a09"
integrity sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==
dependencies:
browserslist "^4.23.3"
core-js-compat@^3.38.1:
version "3.39.0"
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.39.0.tgz#b12dccb495f2601dc860bdbe7b4e3ffa8ba63f61"
integrity sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==
dependencies:
browserslist "^4.24.2"
core-util-is@~1.0.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
@@ -4232,11 +4203,6 @@ css-what@^6.1.0:
resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4"
integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==
css.escape@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb"
integrity sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==
cssdb@^8.2.1:
version "8.2.1"
resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-8.2.1.tgz#62a5d9a41e2c86f1d7c35981098fc5ce47c5766c"
@@ -4431,11 +4397,6 @@ dom-accessibility-api@^0.5.9:
resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453"
integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==
dom-accessibility-api@^0.6.3:
version "0.6.3"
resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz#993e925cc1d73f2c662e7d75dd5a5445259a8fd8"
integrity sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==
dom-serializer@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53"
@@ -4494,9 +4455,9 @@ easy-table@1.2.0:
wcwidth "^1.0.1"
electron-to-chromium@^1.5.41:
version "1.5.64"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.64.tgz#ac8c4c89075d35a1514b620f47dfe48a71ec3697"
integrity sha512-IXEuxU+5ClW2IGEYFC2T7szbyVgehupCWQe5GNh+H065CD6U6IFN0s4KeAMFGNmQolRU4IV7zGBWSYMmZ8uuqQ==
version "1.5.62"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.62.tgz#8289468414b0b0b3e9180ef619a763555debe612"
integrity sha512-t8c+zLmJHa9dJy96yBZRXGQYoiCEnHYgFwn1asvSPZSUdVxnB62A4RASd7k41ytG3ErFBA0TpHlKg9D9SQBmLg==
emoji-regex@^8.0.0:
version "8.0.0"
@@ -4877,9 +4838,9 @@ eslint-plugin-rxjs@^5.0.3:
tsutils-etc "^1.4.1"
eslint-plugin-unicorn@^56.0.0:
version "56.0.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-unicorn/-/eslint-plugin-unicorn-56.0.1.tgz#d10a3df69ba885939075bdc95a65a0c872e940d4"
integrity sha512-FwVV0Uwf8XPfVnKSGpMg7NtlZh0G0gBarCaFcMUOoqPxXryxdYxTRRv4kH6B9TFCVIrjRXG+emcxIk2ayZilog==
version "56.0.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-unicorn/-/eslint-plugin-unicorn-56.0.0.tgz#9fd3ebe6f478571734541fa745026b743175b59e"
integrity sha512-aXpddVz/PQMmd69uxO98PA4iidiVNvA0xOtbpUoz1WhBd4RxOQQYqN618v68drY0hmy5uU2jy1bheKEVWBjlPw==
dependencies:
"@babel/helper-validator-identifier" "^7.24.7"
"@eslint-community/eslint-utils" "^4.4.0"
@@ -4919,11 +4880,6 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800"
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
eslint-visitor-keys@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45"
integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==
eslint@^8.14.0:
version "8.57.1"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.1.tgz#7df109654aba7e3bbe5c8eae533c5e461d3c6ca9"
@@ -5337,9 +5293,9 @@ globals@^13.19.0:
type-fest "^0.20.2"
globals@^15.9.0:
version "15.12.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-15.12.0.tgz#1811872883ad8f41055b61457a130221297de5b5"
integrity sha512-1+gLErljJFhbOVyaetcwJiJ4+eLe45S2E7P5UiZ9xGfeq3ATQf5DOv9G7MH3gGbKQLkzmNh2DxfZwLdw+j6oTQ==
version "15.11.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-15.11.0.tgz#b96ed4c6998540c6fb824b24b5499216d2438d6e"
integrity sha512-yeyNSjdbyVaWurlwCpcA6XNBrHTMIeDdj0/hnvX/OLJ9ekOXYbLsLinH/MucQyGvNnXhidTdNhTtJaffL2sMfw==
globalthis@^1.0.3, globalthis@^1.0.4:
version "1.0.4"
@@ -5554,9 +5510,9 @@ i18next-parser@^9.0.0:
vinyl-fs "^4.0.0"
i18next@^23.0.0, i18next@^23.5.1:
version "23.16.8"
resolved "https://registry.yarnpkg.com/i18next/-/i18next-23.16.8.tgz#3ae1373d344c2393f465556f394aba5a9233b93a"
integrity sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg==
version "23.16.5"
resolved "https://registry.yarnpkg.com/i18next/-/i18next-23.16.5.tgz#53d48ae9f985fd73fc1fcb96e6c7d90ababf0831"
integrity sha512-KTlhE3EP9x6pPTAW7dy0WKIhoCpfOGhRQlO+jttQLgzVaoOjWwBWramu7Pp0i+8wDNduuzXfe3kkVbzrKyrbTA==
dependencies:
"@babel/runtime" "^7.23.2"
@@ -6054,9 +6010,9 @@ kleur@^3.0.3:
integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
knip@^5.27.2:
version "5.37.2"
resolved "https://registry.yarnpkg.com/knip/-/knip-5.37.2.tgz#e218afae3bf28ec10fa3be419cf6d89fd20fd63b"
integrity sha512-Rs9HHTgmUacyKxchP4kRwG8idi0tzVHVpSyo4EM9sNGDSrPq20lhKXOWMFmShGCV6CH2352393Ok/qG1NblCMw==
version "5.37.1"
resolved "https://registry.yarnpkg.com/knip/-/knip-5.37.1.tgz#3c3e91c425dfb35be68b4d12cc0b9ee3cde794e8"
integrity sha512-69gjKj5lLsLXcIPXlHyFfX5AOHgRdh/iXH8gUqvmsHtjqoWhOATeXZDjvvemmgw7KxbWbUzxBNbpjhtJWzgqGA==
dependencies:
"@nodelib/fs.walk" "1.2.8"
"@snyk/github-codeowners" "1.1.0"
@@ -6111,9 +6067,9 @@ lines-and-columns@^1.1.6:
integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
livekit-client@^2.5.7:
version "2.6.3"
resolved "https://registry.yarnpkg.com/livekit-client/-/livekit-client-2.6.3.tgz#0c7e16bcd8b30f61e867ba287257b60db69c7801"
integrity sha512-sUFjdERYdazGmYUCkxV46qKrL8Pg4Aw+9fs/DxV0EC/YtVd7zQh2QObip7IkyT8Ipj4gXhH8CkSinisZ1KpsJQ==
version "2.6.2"
resolved "https://registry.yarnpkg.com/livekit-client/-/livekit-client-2.6.2.tgz#7821cac8d293b7685a4272b8aa269685f0ae75a8"
integrity sha512-SqXNHLgk2ZZOZyeHLXFAVAl+FVdSI+NK39LvIYstqS5X6IE5aCPlK4FqXY4l3aHpSft/BC/TR1CFGOq20ONkMA==
dependencies:
"@livekit/mutex" "1.0.0"
"@livekit/protocol" "1.24.0"
@@ -6154,11 +6110,6 @@ lodash.merge@^4.6.2:
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
loglevel@1.9.1:
version "1.9.1"
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.9.1.tgz#d63976ac9bcd03c7c873116d41c2a85bafff1be7"
@@ -6258,9 +6209,9 @@ matrix-events-sdk@0.0.1:
resolved "https://registry.yarnpkg.com/matrix-events-sdk/-/matrix-events-sdk-0.0.1.tgz#c8c38911e2cb29023b0bbac8d6f32e0de2c957dd"
integrity sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA==
matrix-js-sdk@matrix-org/matrix-js-sdk#2210255d6ffc909c574fb8ef16f92140b2fb7797:
matrix-js-sdk@matrix-org/matrix-js-sdk#80582a734684b532585b52cde63ef34dc7d774fa:
version "34.12.0"
resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/2210255d6ffc909c574fb8ef16f92140b2fb7797"
resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/80582a734684b532585b52cde63ef34dc7d774fa"
dependencies:
"@babel/runtime" "^7.12.5"
"@matrix-org/matrix-sdk-crypto-wasm" "^9.0.0"
@@ -7310,14 +7261,6 @@ rechoir@^0.6.2:
dependencies:
resolve "^1.1.6"
redent@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f"
integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==
dependencies:
indent-string "^4.0.0"
strip-indent "^3.0.0"
reflect.getprototypeof@^1.0.4:
version "1.0.6"
resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz#3ab04c32a8390b770712b7a8633972702d278859"
@@ -7722,9 +7665,9 @@ slash@^3.0.0:
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
smol-toml@^1.3.0:
version "1.3.1"
resolved "https://registry.yarnpkg.com/smol-toml/-/smol-toml-1.3.1.tgz#d9084a9e212142e3cab27ef4e2b8e8ba620bfe15"
integrity sha512-tEYNll18pPKHroYSmLLrksq233j021G0giwW7P3D24jC54pQ5W5BXMsQ/Mvw1OJCmEYDgY+lrzT+3nNUtoNfXQ==
version "1.3.0"
resolved "https://registry.yarnpkg.com/smol-toml/-/smol-toml-1.3.0.tgz#5200e251fffadbb72570c84e9776d2a3eca48143"
integrity sha512-tWpi2TsODPScmi48b/OQZGi2lgUmBCHy6SZrhi/FdnnHiU1GwebbCfuQuxsC3nHaLwtYeJGPrDZDIeodDOc4pA==
snake-case@^3.0.4:
version "3.0.4"
@@ -7755,9 +7698,9 @@ spdx-correct@^3.0.0:
spdx-license-ids "^3.0.0"
spdx-exceptions@^2.1.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz#5d607d27fc806f66d7b64a766650fa890f04ed66"
integrity sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==
version "2.4.0"
resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.4.0.tgz#c07a4ede25b16e4f78e6707bbd84b15a45c19c1b"
integrity sha512-hcjppoJ68fhxA/cjbN4T8N6uCUejN8yFw69ttpqtBeCbF3u13n7mb31NB9jKwGTTWWnt9IbRA/mf1FprYS8wfw==
spdx-expression-parse@^3.0.0:
version "3.0.1"
@@ -7768,9 +7711,9 @@ spdx-expression-parse@^3.0.0:
spdx-license-ids "^3.0.0"
spdx-license-ids@^3.0.0:
version "3.0.20"
resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz#e44ed19ed318dd1e5888f93325cee800f0f51b89"
integrity sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==
version "3.0.16"
resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz#a14f64e0954f6e25cc6587bd4f392522db0d998f"
integrity sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==
sprintf-js@^1.1.1:
version "1.1.3"
@@ -8121,9 +8064,9 @@ tr46@~0.0.3:
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
ts-api-utils@^1.3.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.1.tgz#7c0a304cd446d9a497c24c960b8abbf0bc1611ae"
integrity sha512-5RU2/lxTA3YUZxju61HO2U6EoZLvBLtmV2mbTvqyu4a/7s7RmJPT+1YekhMVsQhznRWk/czIwDUg+V8Q9ZuG4w==
version "1.4.0"
resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.0.tgz#709c6f2076e511a81557f3d07a0cbd566ae8195c"
integrity sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==
ts-debounce@^4.0.0:
version "4.0.0"
@@ -8259,9 +8202,9 @@ typescript-eslint-language-service@^5.0.5:
integrity sha512-b7gWXpwSTqMVKpPX3WttNZEyVAMKs/2jsHKF79H+qaD6mjzCyU5jboJe/lOZgLJD+QRsXCr0GjIVxvl5kI1NMw==
typescript@^5.0.4, typescript@^5.1.6:
version "5.7.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.2.tgz#3169cf8c4c8a828cde53ba9ecb3d2b1d5dd67be6"
integrity sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==
version "5.6.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b"
integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==
unbox-primitive@^1.0.2:
version "1.0.2"