mirror of
https://github.com/vector-im/element-call.git
synced 2026-05-07 10:14:36 +00:00
Merge branch 'livekit' into toger5/back-button-press-control-on-esc
This commit is contained in:
@@ -50,6 +50,9 @@ max_event_delay_duration: 24h
|
||||
enable_registration: true
|
||||
enable_registration_without_verification: true
|
||||
|
||||
# Shared secret for admin user registration via API (for testing only!)
|
||||
registration_shared_secret: "test_shared_secret_for_local_dev_only"
|
||||
|
||||
report_stats: false
|
||||
serve_server_wellknown: true
|
||||
|
||||
|
||||
@@ -50,6 +50,9 @@ max_event_delay_duration: 24h
|
||||
enable_registration: true
|
||||
enable_registration_without_verification: true
|
||||
|
||||
# Shared secret for admin user registration via API (for testing only!)
|
||||
registration_shared_secret: "test_shared_secret_for_local_dev_only"
|
||||
|
||||
report_stats: false
|
||||
serve_server_wellknown: true
|
||||
|
||||
|
||||
@@ -18,3 +18,7 @@ keys:
|
||||
devkey: secret
|
||||
room:
|
||||
auto_create: false
|
||||
webhook:
|
||||
api_key: devkey
|
||||
urls:
|
||||
- https://matrix-rtc.othersite.m.localhost/livekit/jwt/sfu_webhook
|
||||
|
||||
@@ -18,3 +18,7 @@ keys:
|
||||
devkey: secret
|
||||
room:
|
||||
auto_create: false
|
||||
webhook:
|
||||
api_key: devkey
|
||||
urls:
|
||||
- https://matrix-rtc.m.localhost/livekit/jwt/sfu_webhook
|
||||
|
||||
@@ -34,6 +34,13 @@ server {
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
location ~ ^(/_matrix|/_synapse/admin) {
|
||||
proxy_pass "http://homeserver:8008";
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
|
||||
@@ -76,6 +83,14 @@ server {
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
|
||||
location ~ ^(/_matrix|/_synapse/admin) {
|
||||
proxy_pass "http://homeserver-1:18008";
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
|
||||
}
|
||||
|
||||
@@ -50,6 +50,9 @@ max_event_delay_duration: 24h
|
||||
enable_registration: true
|
||||
enable_registration_without_verification: true
|
||||
|
||||
# Shared secret for admin user registration via API (for testing only!)
|
||||
registration_shared_secret: "test_shared_secret_for_local_dev_only"
|
||||
|
||||
report_stats: false
|
||||
serve_server_wellknown: true
|
||||
|
||||
|
||||
@@ -50,6 +50,9 @@ max_event_delay_duration: 24h
|
||||
enable_registration: true
|
||||
enable_registration_without_verification: true
|
||||
|
||||
# Shared secret for admin user registration via API (for testing only!)
|
||||
registration_shared_secret: "test_shared_secret_for_local_dev_only"
|
||||
|
||||
report_stats: false
|
||||
serve_server_wellknown: true
|
||||
|
||||
|
||||
@@ -62,7 +62,10 @@ services:
|
||||
- 7882:7882/tcp
|
||||
- 50100-50200:50100-50200/udp
|
||||
volumes:
|
||||
- ./backend/dev_tls_m.localhost.crt:/local_cert.pem:Z
|
||||
- ./backend/dev_livekit.yaml:/etc/livekit.yaml:Z
|
||||
environment:
|
||||
- SSL_CERT_FILE=/local_cert.pem
|
||||
networks:
|
||||
- ecbackend
|
||||
|
||||
@@ -82,7 +85,10 @@ services:
|
||||
- 17882:17882/tcp
|
||||
- 50300-50400:50300-50400/udp
|
||||
volumes:
|
||||
- ./backend/dev_tls_m.localhost.crt:/local_cert.pem:Z
|
||||
- ./backend/dev_livekit-othersite.yaml:/etc/livekit.yaml:Z
|
||||
environment:
|
||||
- SSL_CERT_FILE=/local_cert.pem
|
||||
networks:
|
||||
- ecbackend
|
||||
|
||||
@@ -164,6 +170,8 @@ services:
|
||||
- "8448:8448"
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
- "auth-server:127.0.0.1"
|
||||
- "auth-server-1:127.0.0.1"
|
||||
depends_on:
|
||||
- synapse
|
||||
networks:
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
"@typescript-eslint/parser": "^8.31.0",
|
||||
"@use-gesture/react": "^10.2.11",
|
||||
"@vector-im/compound-design-tokens": "^10.0.0",
|
||||
"@vector-im/compound-web": "^9.0.0",
|
||||
"@vector-im/compound-web": "^9.3.0",
|
||||
"@vitejs/plugin-react": "^4.0.1",
|
||||
"@vitest/coverage-v8": "^4.0.18",
|
||||
"babel-plugin-transform-vite-meta-env": "^1.0.3",
|
||||
@@ -132,7 +132,7 @@
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint-language-service": "^5.0.5",
|
||||
"unique-names-generator": "^4.6.0",
|
||||
"uuid": "^13.0.0",
|
||||
"uuid": "^14.0.0",
|
||||
"vaul": "^1.0.0",
|
||||
"vite": "^8.0.0",
|
||||
"vite-plugin-generate-file": "^0.3.0",
|
||||
|
||||
@@ -7,11 +7,22 @@ Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { defineConfig, devices } from "@playwright/test";
|
||||
import { join } from "path";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const baseURL = process.env.USE_DOCKER
|
||||
? "http://localhost:8080"
|
||||
: "https://localhost:3000";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
// Needed by the synapse admin API called in fixtures
|
||||
process.env.NODE_EXTRA_CA_CERTS = join(
|
||||
__dirname,
|
||||
"backend/dev_tls_local-ca.crt",
|
||||
);
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
|
||||
@@ -75,9 +75,7 @@ test("Should automatically retry non fatal JWT errors", async ({
|
||||
await expect(page.getByTestId("video").first()).toBeVisible();
|
||||
});
|
||||
|
||||
// We skip this test for now as it appears the livekit does not let us
|
||||
// detect and handle NotAllowed errors anymore. https://github.com/livekit/client-sdk-js/issues/1883
|
||||
test.skip("Should show error screen if call creation is restricted", async ({
|
||||
test("Should show error screen if call creation is restricted", async ({
|
||||
page,
|
||||
browserName,
|
||||
}) => {
|
||||
|
||||
@@ -91,7 +91,9 @@ export const widgetTest = test.extend<MyFixtures>({
|
||||
|
||||
await ewPage1
|
||||
.getByRole("button", { name: "Invite to this room", exact: true })
|
||||
.click();
|
||||
.click({
|
||||
timeout: 10000,
|
||||
});
|
||||
await expect(
|
||||
ewPage1.getByRole("heading", { name: "Invite to Welcome Room" }),
|
||||
).toBeVisible();
|
||||
@@ -104,6 +106,7 @@ export const widgetTest = test.extend<MyFixtures>({
|
||||
await ewPage1.getByRole("dialog").getByRole("textbox").fill(whistlerMxId);
|
||||
await ewPage1.getByRole("dialog").getByRole("textbox").click();
|
||||
await ewPage1.getByRole("button", { name: "Invite" }).click();
|
||||
await TestHelpers.dismissInviteUnknownUserModal(ewPage1);
|
||||
|
||||
// Accept the invite
|
||||
await expect(
|
||||
@@ -126,6 +129,7 @@ export const widgetTest = test.extend<MyFixtures>({
|
||||
await ewPage1.getByRole("textbox", { name: "Search" }).click();
|
||||
await ewPage1.getByRole("textbox", { name: "Search" }).fill(whistlerMxId);
|
||||
await ewPage1.getByRole("button", { name: "Go" }).click();
|
||||
await TestHelpers.dismissInviteUnknownUserModalDM(ewPage1);
|
||||
|
||||
// Wait and send the first message to create the DM
|
||||
await expect(
|
||||
|
||||
@@ -53,7 +53,6 @@ test("@mobile Start a new call then leave and show the feedback screen", async (
|
||||
mobileTest(
|
||||
"Test earpiece overlay in controlledAudioDevices mode",
|
||||
async ({ asMobile, browser }) => {
|
||||
test.slow(); // Triples the timeout
|
||||
const { creatorPage, inviteLink } = asMobile;
|
||||
|
||||
// ========
|
||||
|
||||
@@ -9,7 +9,9 @@ import { expect, test } from "@playwright/test";
|
||||
|
||||
test("When creator left, avoid reconnect to the same SFU", async ({
|
||||
browser,
|
||||
browserName,
|
||||
}) => {
|
||||
test.skip(browserName === "firefox", "Browser independent");
|
||||
// Use reduce motion to disable animations that are making the tests a bit flaky
|
||||
const creatorContext = await browser.newContext({ reducedMotion: "reduce" });
|
||||
const creatorPage = await creatorContext.newPage();
|
||||
@@ -91,8 +93,10 @@ test("When creator left, avoid reconnect to the same SFU", async ({
|
||||
// the creator leaves the call
|
||||
await creatorPage.getByTestId("incall_leave").click();
|
||||
|
||||
await guestCPage.waitForTimeout(2000);
|
||||
// https://github.com/element-hq/element-call/issues/3344
|
||||
// The app used to request a new jwt token then to reconnect to the SFU
|
||||
expect(wsConnectionCount).toBe(1);
|
||||
// Wait a bit to be sure that if there was a reconnect, it would have happened by now
|
||||
await guestCPage.waitForTimeout(6000);
|
||||
expect(wsConnectionCount).toBe(1);
|
||||
});
|
||||
|
||||
@@ -115,8 +115,12 @@ test("One to One rejoin after improper leave does not crash EC", async ({
|
||||
await guestPage.getByTestId("lobby_joinCall").click();
|
||||
|
||||
// We cannot use the `expectVideoTilesCount` helper here since one of them is expected to show waiting for media
|
||||
await expect(page.getByTestId("videoTile")).toHaveCount(3);
|
||||
await expect(guestPage.getByTestId("videoTile")).toHaveCount(2);
|
||||
await expect(page.getByTestId("videoTile")).toHaveCount(3, {
|
||||
timeout: 10000,
|
||||
});
|
||||
await expect(guestPage.getByTestId("videoTile")).toHaveCount(2, {
|
||||
timeout: 10000,
|
||||
});
|
||||
});
|
||||
|
||||
function isStickySend(url: string): boolean {
|
||||
|
||||
@@ -119,25 +119,27 @@ async function setRtcModeFromSettings(
|
||||
async function expectVideoTilesCount(page: Page, count: number): Promise<void> {
|
||||
await expect(page.getByTestId("videoTile")).toHaveCount(2);
|
||||
|
||||
// There are no other options than to wait for all media to be ready?
|
||||
// Or it is too flaky :/
|
||||
await page.waitForTimeout(3000);
|
||||
// No one should be waiting for media
|
||||
await expect(page.getByText("Waiting for media...")).not.toBeVisible();
|
||||
await expect(page.getByText("Waiting for media...")).not.toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
// There should be 5 video elements, visible and autoplaying
|
||||
const videoElements = await page.locator("video").all();
|
||||
expect(videoElements.length).toBe(count);
|
||||
// There should be `count` video elements, visible and autoplaying
|
||||
await expect(page.locator("video")).toHaveCount(count);
|
||||
|
||||
const blockDisplayCount = await page
|
||||
.locator("video")
|
||||
.evaluateAll(
|
||||
(videos: Element[]) =>
|
||||
videos.filter(
|
||||
(v: Element) => window.getComputedStyle(v).display === "block",
|
||||
).length,
|
||||
);
|
||||
expect(blockDisplayCount).toBe(count);
|
||||
await expect(async () => {
|
||||
const videoBlockCount = await page
|
||||
.locator("video")
|
||||
.evaluateAll(
|
||||
(videos: Element[]) =>
|
||||
videos.filter(
|
||||
(v: Element) => window.getComputedStyle(v).display === "block",
|
||||
).length,
|
||||
);
|
||||
expect(videoBlockCount).toBe(count);
|
||||
}).toPass({
|
||||
timeout: 10000,
|
||||
});
|
||||
}
|
||||
|
||||
export const SpaHelpers = {
|
||||
|
||||
142
playwright/utils/synapse-admin.ts
Normal file
142
playwright/utils/synapse-admin.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
Copyright 2026 Element Creations Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { createHmac } from "crypto";
|
||||
|
||||
/**
|
||||
* Response from Synapse registration API
|
||||
*/
|
||||
export interface SynapseRegistrationResponse {
|
||||
access_token: string;
|
||||
user_id: string;
|
||||
home_server: string;
|
||||
device_id: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility class for interacting with Synapse Admin API
|
||||
* This provides fast user registration without going through the UI
|
||||
*
|
||||
* @see https://matrix-org.github.io/synapse/latest/admin_api/register_api.html
|
||||
*/
|
||||
export class SynapseAdmin {
|
||||
public constructor(
|
||||
private baseUrl: string = "https://synapse.m.localhost",
|
||||
private sharedSecret: string = "test_shared_secret_for_local_dev_only",
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Register a user using the Synapse Admin API
|
||||
* This is much faster than going through the UI registration flow
|
||||
*
|
||||
* @param username - The username (localpart) for the new user
|
||||
* @param password - The password for the new user
|
||||
* @param displayName - Optional display name (defaults to username)
|
||||
* @param admin - Whether the user should be an admin (defaults to false)
|
||||
* @returns Registration response containing access token and user ID
|
||||
*/
|
||||
public async registerUser(
|
||||
username: string,
|
||||
password: string,
|
||||
displayName?: string,
|
||||
admin: boolean = false,
|
||||
): Promise<SynapseRegistrationResponse> {
|
||||
// Get a nonce first
|
||||
const nonce = await this.getNonce();
|
||||
|
||||
// Generate the HMAC
|
||||
const mac = this.generateMac(username, password, admin, nonce);
|
||||
|
||||
// Make the registration request
|
||||
const response = await fetch(`${this.baseUrl}/_synapse/admin/v1/register`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
nonce,
|
||||
username,
|
||||
password,
|
||||
displayname: displayName || username,
|
||||
admin,
|
||||
mac,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.text();
|
||||
throw new Error(
|
||||
`Failed to register user ${username}: ${response.status} ${error}`,
|
||||
);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a nonce for registration
|
||||
* The nonce is required for the HMAC calculation
|
||||
*
|
||||
* @returns A nonce string
|
||||
*/
|
||||
private async getNonce(): Promise<string> {
|
||||
const response = await fetch(`${this.baseUrl}/_synapse/admin/v1/register`, {
|
||||
method: "GET",
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Failed to get nonce: ${response.status} ${await response.text()}`,
|
||||
);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data.nonce;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate HMAC for shared secret registration
|
||||
* This is the authentication mechanism for the admin API
|
||||
*
|
||||
* @param username - The username
|
||||
* @param password - The password
|
||||
* @param admin - Whether the user is an admin
|
||||
* @param nonce - The nonce from the server
|
||||
* @returns The HMAC hex string
|
||||
*/
|
||||
private generateMac(
|
||||
username: string,
|
||||
password: string,
|
||||
admin: boolean,
|
||||
nonce: string,
|
||||
): string {
|
||||
const mac = createHmac("sha1", this.sharedSecret);
|
||||
mac.update(nonce);
|
||||
mac.update("\x00");
|
||||
mac.update(username);
|
||||
mac.update("\x00");
|
||||
mac.update(password);
|
||||
mac.update("\x00");
|
||||
mac.update(admin ? "admin" : "notadmin");
|
||||
|
||||
return mac.digest("hex");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new SynapseAdmin instance for a different homeserver
|
||||
*
|
||||
* @param baseUrl - The base URL of the homeserver
|
||||
* @param sharedSecret - The shared secret (defaults to test secret)
|
||||
* @returns A new SynapseAdmin instance
|
||||
*/
|
||||
public static forHomeserver(
|
||||
baseUrl: string,
|
||||
sharedSecret: string = "test_shared_secret_for_local_dev_only",
|
||||
): SynapseAdmin {
|
||||
return new SynapseAdmin(baseUrl, sharedSecret);
|
||||
}
|
||||
}
|
||||
@@ -26,8 +26,12 @@ modePairs.forEach(([rtcMode1, rtcMode2]) => {
|
||||
"The is test is not working on firefox CI environment. No mic/audio device inputs so cam/mic are disabled",
|
||||
);
|
||||
|
||||
const florian = await addUser("floriant", HOST1);
|
||||
const timo = await addUser("timo", HOST2);
|
||||
test.slow();
|
||||
|
||||
const [florian, timo] = await Promise.all([
|
||||
addUser("florian", HOST1),
|
||||
addUser("timo", HOST2),
|
||||
]);
|
||||
|
||||
const roomName = "Call Room";
|
||||
|
||||
@@ -57,27 +61,20 @@ modePairs.forEach(([rtcMode1, rtcMode2]) => {
|
||||
const frame = user.page
|
||||
.locator('iframe[title="Element Call"]')
|
||||
.contentFrame();
|
||||
await expect(frame.getByTestId("videoTile")).toHaveCount(2);
|
||||
await expect(frame.getByTestId("videoTile")).toHaveCount(2, {
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
// There are no other options than to wait for all media to be ready?
|
||||
// Or it is too flaky :/
|
||||
await user.page.waitForTimeout(3000);
|
||||
// No one should be waiting for media
|
||||
await expect(frame.getByText("Waiting for media...")).not.toBeVisible();
|
||||
await expect(frame.getByText("Waiting for media...")).not.toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
// There should be 2 video elements, visible and autoplaying
|
||||
const videoElements = await frame.locator("video").all();
|
||||
expect(videoElements.length).toBe(2);
|
||||
|
||||
const blockDisplayCount = await frame
|
||||
.locator("video")
|
||||
.evaluateAll(
|
||||
(videos: Element[]) =>
|
||||
videos.filter(
|
||||
(v: Element) => window.getComputedStyle(v).display === "block",
|
||||
).length,
|
||||
);
|
||||
expect(blockDisplayCount).toBe(2);
|
||||
await TestHelpers.expectVisibleVideoCount(frame, 2);
|
||||
}
|
||||
|
||||
// await florian.page.pause();
|
||||
|
||||
@@ -75,18 +75,11 @@ widgetTest(
|
||||
await expect(frame.getByText("Waiting for media...")).not.toBeVisible();
|
||||
|
||||
// There should be 2 video elements, visible and autoplaying
|
||||
const videoElements = await frame.locator("video").all();
|
||||
expect(videoElements.length).toBe(2);
|
||||
await expect(frame.locator("video")).toHaveCount(2, {
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
const blockDisplayCount = await frame
|
||||
.locator("video")
|
||||
.evaluateAll(
|
||||
(videos: Element[]) =>
|
||||
videos.filter(
|
||||
(v: Element) => window.getComputedStyle(v).display === "block",
|
||||
).length,
|
||||
);
|
||||
expect(blockDisplayCount).toBe(2);
|
||||
await TestHelpers.expectVisibleVideoCount(frame, 2);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@@ -27,6 +27,7 @@ import { HOST1, HOST2, TestHelpers } from "./test-helpers";
|
||||
widgetTest(
|
||||
`Test swapping publisher from ${HOST1} to ${HOST2}`,
|
||||
async ({ addUser, browserName }) => {
|
||||
test.slow();
|
||||
test.skip(
|
||||
browserName === "firefox",
|
||||
"The is test is not working on firefox CI environment. No mic/audio device inputs so cam/mic are disabled",
|
||||
@@ -65,40 +66,26 @@ widgetTest(
|
||||
.contentFrame();
|
||||
await expect(frame.getByTestId("videoTile")).toHaveCount(2);
|
||||
|
||||
// There are no other options than to wait for all media to be ready?
|
||||
// Or it is too flaky :/
|
||||
await user.page.waitForTimeout(3000);
|
||||
// No one should be waiting for media
|
||||
await expect(frame.getByText("Waiting for media...")).not.toBeVisible();
|
||||
// Wait for "Waiting for media..." to disappear (with timeout)
|
||||
await expect(frame.getByText("Waiting for media...")).not.toBeVisible({
|
||||
timeout: 10000, // Maximum time to wait
|
||||
});
|
||||
|
||||
// There should be 2 video elements, visible and autoplaying
|
||||
const videoElements = await frame.locator("video").all();
|
||||
expect(videoElements.length).toBe(2);
|
||||
await expect(frame.locator("video")).toHaveCount(2, {
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
const blockDisplayCount = await frame
|
||||
.locator("video")
|
||||
.evaluateAll(
|
||||
(videos: Element[]) =>
|
||||
videos.filter(
|
||||
(v: Element) => window.getComputedStyle(v).display === "block",
|
||||
).length,
|
||||
);
|
||||
expect(blockDisplayCount).toBe(2);
|
||||
await TestHelpers.expectVisibleVideoCount(frame, 2);
|
||||
}
|
||||
|
||||
// now we switch the mode for timo (second joiner on multi-sfu HOST2 but currently HOST1)
|
||||
await TestHelpers.setEmbeddedElementCallRtcMode(timo.page, "compat");
|
||||
await timo.page.waitForTimeout(3000);
|
||||
const blockDisplayCount = await timo.page
|
||||
.locator('iframe[title="Element Call"]')
|
||||
.contentFrame()
|
||||
.locator("video")
|
||||
.evaluateAll(
|
||||
(videos: Element[]) =>
|
||||
videos.filter(
|
||||
(v: Element) => window.getComputedStyle(v).display === "block",
|
||||
).length,
|
||||
);
|
||||
expect(blockDisplayCount).toBe(2);
|
||||
|
||||
await TestHelpers.expectVisibleVideoCount(
|
||||
timo.page.locator('iframe[title="Element Call"]').contentFrame(),
|
||||
2,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -11,18 +11,21 @@ import { widgetTest } from "../fixtures/widget-user.ts";
|
||||
import { HOST1, TestHelpers } from "./test-helpers.ts";
|
||||
|
||||
widgetTest("Create and join a group call", async ({ addUser, browserName }) => {
|
||||
// increase the timeouts, it is a long test and it is annoying to retry from the beginning for a single timeout.
|
||||
test.slow();
|
||||
|
||||
test.skip(
|
||||
browserName === "firefox",
|
||||
"The is test is not working on firefox CI environment. No mic/audio device inputs so cam/mic are disabled",
|
||||
);
|
||||
|
||||
test.slow(); // We are registering multiple users here, give it more time
|
||||
|
||||
const valere = await addUser("Valere", HOST1);
|
||||
const timo = await addUser("Timo", HOST1);
|
||||
const robin = await addUser("Robin", HOST1);
|
||||
const halfshot = await addUser("Halfshot", HOST1);
|
||||
const florian = await addUser("florian", HOST1);
|
||||
const [valere, timo, robin, halfshot, florian] = await Promise.all([
|
||||
addUser("Valere", HOST1),
|
||||
addUser("Timo", HOST1),
|
||||
addUser("Robin", HOST1),
|
||||
addUser("Halfshot", HOST1),
|
||||
addUser("florian", HOST1),
|
||||
]);
|
||||
|
||||
const roomName = "Group Call Room";
|
||||
await TestHelpers.createRoom(roomName, valere.page, [
|
||||
@@ -47,52 +50,55 @@ widgetTest("Create and join a group call", async ({ addUser, browserName }) => {
|
||||
|
||||
await TestHelpers.joinCallFromLobby(valere.page);
|
||||
|
||||
for (const user of [timo, robin, halfshot, florian]) {
|
||||
await TestHelpers.joinCallInCurrentRoom(user.page);
|
||||
}
|
||||
await Promise.all(
|
||||
[timo, robin, halfshot, florian].map(async (user) => {
|
||||
await TestHelpers.joinCallInCurrentRoom(user.page);
|
||||
}),
|
||||
);
|
||||
|
||||
for (const user of [timo, robin, halfshot, florian]) {
|
||||
const frame = user.page
|
||||
.locator('iframe[title="Element Call"]')
|
||||
.contentFrame();
|
||||
// No lobby, should start with video on
|
||||
await expect(
|
||||
frame.getByRole("switch", { name: "Stop video", checked: true }),
|
||||
).toBeVisible();
|
||||
}
|
||||
await Promise.all(
|
||||
[timo, robin, halfshot, florian].map(async (user) => {
|
||||
const frame = user.page
|
||||
.locator('iframe[title="Element Call"]')
|
||||
.contentFrame();
|
||||
await expect(
|
||||
frame.getByRole("switch", { name: "Stop video", checked: true }),
|
||||
).toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
// We should see 5 video tiles everywhere now
|
||||
for (const user of [valere, timo, robin, halfshot, florian]) {
|
||||
const frame = user.page
|
||||
.locator('iframe[title="Element Call"]')
|
||||
.contentFrame();
|
||||
await expect(frame.getByTestId("videoTile")).toHaveCount(5);
|
||||
for (const participant of [valere, timo, robin, halfshot, florian]) {
|
||||
// Check the names are correct
|
||||
await expect(frame.getByText(participant.displayName)).toBeVisible();
|
||||
}
|
||||
await Promise.all(
|
||||
[valere, timo, robin, halfshot, florian].map(async (user) => {
|
||||
const frame = user.page
|
||||
.locator('iframe[title="Element Call"]')
|
||||
.contentFrame();
|
||||
await expect(frame.getByTestId("videoTile")).toHaveCount(5, {
|
||||
timeout: 15000,
|
||||
});
|
||||
|
||||
// There is no other options than to wait for all media to be ready?
|
||||
// Or it is too flaky :/
|
||||
await user.page.waitForTimeout(5000);
|
||||
// No one should be waiting for media
|
||||
await expect(frame.getByText("Waiting for media...")).not.toBeVisible();
|
||||
|
||||
// There should be 5 video elements, visible and autoplaying
|
||||
const videoElements = await frame.locator("video").all();
|
||||
expect(videoElements.length).toBe(5);
|
||||
await expect(frame.locator("video[autoplay]")).toHaveCount(5);
|
||||
|
||||
const blockDisplayCount = await frame
|
||||
.locator("video")
|
||||
.evaluateAll(
|
||||
(videos: Element[]) =>
|
||||
videos.filter(
|
||||
(v: Element) => window.getComputedStyle(v).display === "block",
|
||||
).length,
|
||||
await Promise.all(
|
||||
[valere, timo, robin, halfshot, florian].map(async (user) => {
|
||||
// Check the names are correct
|
||||
await expect(frame.getByText(user.displayName)).toBeVisible();
|
||||
}),
|
||||
);
|
||||
expect(blockDisplayCount).toBe(5);
|
||||
}
|
||||
|
||||
// No one should be waiting for media
|
||||
await expect(frame.getByText("Waiting for media...")).not.toBeVisible({
|
||||
// Use a bigger timeout here
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
// There should be 5 video elements, visible and autoplaying
|
||||
await expect(frame.locator("video")).toHaveCount(5);
|
||||
await expect(frame.locator("video[autoplay]")).toHaveCount(5);
|
||||
|
||||
await TestHelpers.expectVisibleVideoCount(frame, 5);
|
||||
}),
|
||||
);
|
||||
|
||||
// Quickly test muting one participant to see it reflects and that our asserts works
|
||||
const florianFrame = florian.page
|
||||
@@ -108,28 +114,16 @@ widgetTest("Create and join a group call", async ({ addUser, browserName }) => {
|
||||
await expect(florianVideoButton).toHaveAccessibleName("Start video");
|
||||
await expect(florianVideoButton).not.toBeChecked();
|
||||
|
||||
// wait a bit for the state to propagate
|
||||
await valere.page.waitForTimeout(3000);
|
||||
{
|
||||
const frame = valere.page
|
||||
.locator('iframe[title="Element Call"]')
|
||||
.contentFrame();
|
||||
|
||||
const videoElements = await frame.locator("video").all();
|
||||
expect(videoElements.length).toBe(5);
|
||||
|
||||
const blockDisplayCount = await frame
|
||||
.locator("video")
|
||||
.evaluateAll(
|
||||
(videos: Element[]) =>
|
||||
videos.filter(
|
||||
(v: Element) => window.getComputedStyle(v).display === "block",
|
||||
).length,
|
||||
);
|
||||
await expect(frame.locator("video")).toHaveCount(5, {
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
// out of 5 ONLY 4 are visible (display:block) !!
|
||||
// XXX we need to be better at our HTML markup and accessibility, it would make
|
||||
// this kind of stuff way easier to test if we could look out for aria attributes.
|
||||
expect(blockDisplayCount).toBe(4);
|
||||
await TestHelpers.expectVisibleVideoCount(frame, 4);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -16,8 +16,6 @@ widgetTest("Footer interaction in PiP", async ({ addUser, browserName }) => {
|
||||
"The is test is not working on firefox CI environment. No mic/audio device inputs so cam/mic are disabled",
|
||||
);
|
||||
|
||||
test.slow();
|
||||
|
||||
const valere = await addUser("Valere", HOST1);
|
||||
|
||||
const callRoom = "CallRoom";
|
||||
|
||||
@@ -47,7 +47,10 @@ widgetTest("Put call in PIP", async ({ addUser, browserName }) => {
|
||||
// check that the video is on
|
||||
await expect(
|
||||
frame.getByRole("switch", { name: "Stop video", checked: true }),
|
||||
).toBeVisible();
|
||||
).toBeVisible({
|
||||
// Increase timeout, as this expect was flaky
|
||||
timeout: 15000,
|
||||
});
|
||||
|
||||
// Switch to the other room, the call should go to PIP
|
||||
await TestHelpers.switchToRoomNamed(valere.page, "DoubleTask");
|
||||
@@ -63,8 +66,10 @@ widgetTest("Put call in PIP", async ({ addUser, browserName }) => {
|
||||
const frame = valere.page
|
||||
.locator('iframe[title="Element Call"]')
|
||||
.contentFrame();
|
||||
|
||||
await expect(frame.locator("video")).toHaveCount(1, { timeout: 10000 });
|
||||
|
||||
const videoElements = await frame.locator("video").all();
|
||||
expect(videoElements.length).toBe(1);
|
||||
|
||||
const pipVideo = videoElements[0];
|
||||
await expect(pipVideo).toHaveCSS("object-fit", "cover");
|
||||
|
||||
@@ -18,9 +18,11 @@ widgetTest("Sharing screen in group call", async ({ addUser, browserName }) => {
|
||||
|
||||
test.slow(); // We are registering multiple users here, give it more time
|
||||
|
||||
const alice = await addUser("Alice", HOST1);
|
||||
const bob = await addUser("Bob", HOST1);
|
||||
const carol = await addUser("Carol", HOST1);
|
||||
const [alice, bob, carol] = await Promise.all([
|
||||
addUser("Alice", HOST1),
|
||||
addUser("Bob", HOST1),
|
||||
addUser("Carol", HOST1),
|
||||
]);
|
||||
|
||||
const roomName = "Meeting Room";
|
||||
await TestHelpers.createRoom(roomName, alice.page, [bob.mxId, carol.mxId]);
|
||||
@@ -50,7 +52,7 @@ widgetTest("Sharing screen in group call", async ({ addUser, browserName }) => {
|
||||
|
||||
// Expect 3 video tiles
|
||||
await expect(frame.locator("video")).toHaveCount(3, {
|
||||
timeout: 5000,
|
||||
timeout: 10000,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ widgetTest.skip(
|
||||
);
|
||||
|
||||
widgetTest("Start a new call as widget", async ({ asWidget, browserName }) => {
|
||||
test.slow(); // Triples the timeout
|
||||
test.slow();
|
||||
|
||||
const { brooks, whistler } = asWidget;
|
||||
|
||||
@@ -83,8 +83,12 @@ widgetTest("Start a new call as widget", async ({ asWidget, browserName }) => {
|
||||
.locator('iframe[title="Element Call"]')
|
||||
.contentFrame()
|
||||
.getByTestId("incall_leave")
|
||||
.click();
|
||||
.click({ timeout: 15000 });
|
||||
|
||||
await expect(whistler.page.locator(".mx_BasicMessageComposer")).toBeVisible();
|
||||
await expect(brooks.page.locator(".mx_BasicMessageComposer")).toBeVisible();
|
||||
await expect(whistler.page.locator(".mx_BasicMessageComposer")).toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
await expect(brooks.page.locator(".mx_BasicMessageComposer")).toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,9 +10,12 @@ import {
|
||||
expect,
|
||||
type JSHandle,
|
||||
type Page,
|
||||
type FrameLocator,
|
||||
} from "@playwright/test";
|
||||
import { type MatrixClient } from "matrix-js-sdk";
|
||||
|
||||
import { SynapseAdmin } from "../utils/synapse-admin.ts";
|
||||
|
||||
const PASSWORD = "foobarbaz1!";
|
||||
|
||||
export const HOST1 = "https://app.m.localhost/#/welcome";
|
||||
@@ -26,14 +29,14 @@ export class TestHelpers {
|
||||
voice: boolean = false,
|
||||
): Promise<void> {
|
||||
const buttonName = voice ? "Voice call" : "Video call";
|
||||
await expect(page.getByRole("button", { name: buttonName })).toBeVisible();
|
||||
await page.getByRole("button", { name: buttonName }).click();
|
||||
|
||||
await expect(
|
||||
page.getByRole("menuitem", { name: "Element Call" }),
|
||||
).toBeVisible();
|
||||
await page.getByRole("button", { name: buttonName }).click({
|
||||
timeout: 5000,
|
||||
});
|
||||
|
||||
await page.getByRole("menuitem", { name: "Element Call" }).click();
|
||||
await page.getByRole("menuitem", { name: "Element Call" }).click({
|
||||
timeout: 10000,
|
||||
});
|
||||
}
|
||||
|
||||
public static async joinCallFromLobby(page: Page): Promise<void> {
|
||||
@@ -57,9 +60,12 @@ export class TestHelpers {
|
||||
): Promise<void> {
|
||||
// This is the header button that notifies about an ongoing call
|
||||
const label = audioOnly ? "Voice call started" : "Video call started";
|
||||
await expect(page.getByText(label)).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: "Join" })).toBeVisible();
|
||||
await page.getByRole("button", { name: "Join" }).click();
|
||||
await expect(page.getByText(label)).toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
await page.getByRole("button", { name: "Join" }).click({
|
||||
timeout: 5000,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,43 +80,56 @@ export class TestHelpers {
|
||||
clientHandle: JSHandle<MatrixClient>;
|
||||
mxId: string;
|
||||
}> {
|
||||
// Determine which homeserver to use based on the host
|
||||
const synapseBaseUrl =
|
||||
host === HOST2
|
||||
? "https://synapse.othersite.m.localhost"
|
||||
: "https://synapse.m.localhost";
|
||||
|
||||
// Register user via Synapse Admin API to speed things up
|
||||
const synapseAdmin = SynapseAdmin.forHomeserver(synapseBaseUrl);
|
||||
const credentials = await synapseAdmin.registerUser(
|
||||
username,
|
||||
PASSWORD,
|
||||
username,
|
||||
);
|
||||
|
||||
// STEP 2: Open browser and login
|
||||
const userContext = await browser.newContext({
|
||||
reducedMotion: "reduce",
|
||||
});
|
||||
const page = await userContext.newPage();
|
||||
await page.goto(host);
|
||||
await page.getByRole("link", { name: "Create Account" }).click();
|
||||
await page.getByRole("textbox", { name: "Username" }).fill(username);
|
||||
await page.getByRole("textbox", { name: "Password", exact: true }).click();
|
||||
await page
|
||||
.getByRole("textbox", { name: "Password", exact: true })
|
||||
.fill(PASSWORD);
|
||||
await page.getByRole("textbox", { name: "Confirm password" }).click();
|
||||
await page
|
||||
.getByRole("textbox", { name: "Confirm password" })
|
||||
.fill(PASSWORD);
|
||||
await page.getByRole("button", { name: "Register" }).click();
|
||||
|
||||
await page.getByRole("link", { name: "Sign in" }).click({
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
await page.getByRole("textbox", { name: "Username" }).fill(username, {
|
||||
timeout: 10000,
|
||||
});
|
||||
await page.getByRole("textbox", { name: "Password" }).fill(PASSWORD, {
|
||||
timeout: 10000,
|
||||
});
|
||||
await page.getByRole("button", { name: "Sign in" }).click();
|
||||
|
||||
await expect(
|
||||
page.getByRole("heading", { name: `Welcome ${username}` }),
|
||||
).toBeVisible({
|
||||
// Increase timeout as registration can be slow :/
|
||||
timeout: 15_000,
|
||||
// Increase timeout here :/ flaky
|
||||
timeout: 15000,
|
||||
});
|
||||
|
||||
await this.maybeDismissBrowserNotSupportedToast(page);
|
||||
await this.maybeDismissServiceWorkerWarningToast(page);
|
||||
await this.maybeDismissBackupChat(page);
|
||||
|
||||
await TestHelpers.setDevToolElementCallDevUrl(page);
|
||||
|
||||
const clientHandle = await page.evaluateHandle(() =>
|
||||
window.mxMatrixClientPeg.get(),
|
||||
);
|
||||
const mxId = (await clientHandle.evaluate(
|
||||
(cli: MatrixClient) => cli.getUserId(),
|
||||
clientHandle,
|
||||
))!;
|
||||
|
||||
const mxId = credentials.user_id;
|
||||
return { page, clientHandle, mxId };
|
||||
}
|
||||
|
||||
@@ -152,6 +171,22 @@ export class TestHelpers {
|
||||
}
|
||||
}
|
||||
|
||||
private static async maybeDismissBackupChat(page: Page): Promise<void> {
|
||||
const toast = page
|
||||
.locator(".mx_Toast_toast")
|
||||
.getByText("Back up your chats");
|
||||
|
||||
try {
|
||||
await expect(toast).toBeVisible({ timeout: 700 });
|
||||
await page
|
||||
.locator(".mx_Toast_toast")
|
||||
.getByRole("button", { name: "Dismiss" })
|
||||
.click();
|
||||
} catch {
|
||||
// toast not visible, continue as normal
|
||||
}
|
||||
}
|
||||
|
||||
public static async maybeDismissKeyBackupToast(page: Page): Promise<void> {
|
||||
const toast = page
|
||||
.locator(".mx_Toast_toast")
|
||||
@@ -178,10 +213,14 @@ export class TestHelpers {
|
||||
.getByRole("button", { name: "New conversation" })
|
||||
.click();
|
||||
|
||||
await page.getByRole("menuitem", { name: "New Room" }).click();
|
||||
await page.getByRole("menuitem", { name: "New Room" }).click({
|
||||
timeout: 5000,
|
||||
});
|
||||
await page.getByRole("textbox", { name: "Name" }).fill(name);
|
||||
await page.getByRole("button", { name: "Create room" }).click();
|
||||
await expect(page.getByText("You created this room.")).toBeVisible();
|
||||
await expect(page.getByText("You created this room.")).toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
await expect(page.getByText("Encryption enabled")).toBeVisible();
|
||||
await TestHelpers.maybeDismissKeyBackupToast(page);
|
||||
|
||||
@@ -199,6 +238,7 @@ export class TestHelpers {
|
||||
}
|
||||
|
||||
await page.getByRole("button", { name: "Invite" }).click();
|
||||
await TestHelpers.dismissInviteUnknownUserModal(page);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,9 +251,12 @@ export class TestHelpers {
|
||||
roomName: string,
|
||||
page: Page,
|
||||
): Promise<void> {
|
||||
await expect(page.getByRole("option", { name: roomName })).toBeVisible();
|
||||
await page.getByRole("option", { name: roomName }).click();
|
||||
await page.getByRole("button", { name: "Accept" }).click();
|
||||
await page.getByRole("option", { name: roomName }).click({
|
||||
timeout: 10000,
|
||||
});
|
||||
await page.getByRole("button", { name: "Accept" }).click({
|
||||
timeout: 5000,
|
||||
});
|
||||
|
||||
await expect(
|
||||
page.getByRole("main").getByRole("heading", { name: roomName }),
|
||||
@@ -233,8 +276,12 @@ export class TestHelpers {
|
||||
page: Page,
|
||||
mode: RtcMode,
|
||||
): Promise<void> {
|
||||
await page.getByRole("button", { name: "Video call" }).click();
|
||||
await page.getByRole("menuitem", { name: "Element Call" }).click();
|
||||
await page.getByRole("button", { name: "Video call" }).click({
|
||||
timeout: 5000,
|
||||
});
|
||||
await page.getByRole("menuitem", { name: "Element Call" }).click({
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
await TestHelpers.setEmbeddedElementCallRtcMode(page, mode);
|
||||
await page.getByRole("button", { name: "Close lobby" }).click();
|
||||
@@ -308,4 +355,52 @@ export class TestHelpers {
|
||||
): Promise<void> {
|
||||
await page.getByRole("option", { name: `Open room ${roomName}` }).click();
|
||||
}
|
||||
|
||||
public static async dismissInviteUnknownUserModal(page: Page): Promise<void> {
|
||||
await expect(
|
||||
page.getByRole("heading", { name: "Invite new contacts to this" }),
|
||||
).toBeVisible();
|
||||
await page.getByRole("button", { name: "Invite" }).click({
|
||||
timeout: 5000,
|
||||
});
|
||||
}
|
||||
|
||||
public static async dismissInviteUnknownUserModalDM(
|
||||
page: Page,
|
||||
): Promise<void> {
|
||||
await expect(
|
||||
page.getByRole("heading", {
|
||||
name: "Start a chat with this new contact?",
|
||||
}),
|
||||
).toBeVisible();
|
||||
await page.getByRole("button", { name: "Continue" }).click({
|
||||
timeout: 5000,
|
||||
});
|
||||
}
|
||||
|
||||
public static async expectVisibleVideoCount(
|
||||
frame: FrameLocator,
|
||||
count: number,
|
||||
): Promise<void> {
|
||||
// XXX we need to be better at our HTML markup and accessibility, it would make
|
||||
// this kind of stuff way easier to test if we could look out for aria attributes.
|
||||
await expect
|
||||
.poll(
|
||||
async () => {
|
||||
return await frame
|
||||
.locator("video")
|
||||
.evaluateAll(
|
||||
(videos: Element[]) =>
|
||||
videos.filter(
|
||||
(v: Element) =>
|
||||
window.getComputedStyle(v).display === "block",
|
||||
).length,
|
||||
);
|
||||
},
|
||||
{
|
||||
timeout: 10000,
|
||||
},
|
||||
)
|
||||
.toBe(count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,6 @@ widgetTest(
|
||||
"The is test is not working on firefox CI environment. No mic/audio device inputs so cam/mic are disabled",
|
||||
);
|
||||
|
||||
test.slow(); // Triples the timeout
|
||||
|
||||
const { brooks, whistler } = asWidget;
|
||||
|
||||
await TestHelpers.startCallInCurrentRoom(brooks.page, true);
|
||||
@@ -113,8 +111,6 @@ widgetTest(
|
||||
"The is test is not working on firefox CI environment. No mic/audio device inputs so cam/mic are disabled",
|
||||
);
|
||||
|
||||
test.slow(); // Triples the timeout
|
||||
|
||||
const { brooks, whistler } = asWidget;
|
||||
|
||||
await TestHelpers.startCallInCurrentRoom(brooks.page, false);
|
||||
@@ -200,8 +196,6 @@ widgetTest(
|
||||
"The is test is not working on firefox CI environment. No mic/audio device inputs so cam/mic are disabled",
|
||||
);
|
||||
|
||||
test.slow(); // Triples the timeout
|
||||
|
||||
const { brooks, whistler } = asWidget;
|
||||
|
||||
await TestHelpers.startCallInCurrentRoom(brooks.page, false);
|
||||
|
||||
163
pnpm-lock.yaml
generated
163
pnpm-lock.yaml
generated
@@ -47,16 +47,16 @@ importers:
|
||||
version: 11.7.12
|
||||
'@livekit/components-core':
|
||||
specifier: ^0.12.0
|
||||
version: 0.12.13(livekit-client@2.18.3(@types/dom-mediacapture-record@1.0.22))(tslib@2.8.1)
|
||||
version: 0.12.13(livekit-client@2.18.6(@types/dom-mediacapture-record@1.0.22))(tslib@2.8.1)
|
||||
'@livekit/components-react':
|
||||
specifier: ^2.0.0
|
||||
version: 2.9.20(livekit-client@2.18.3(@types/dom-mediacapture-record@1.0.22))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tslib@2.8.1)
|
||||
version: 2.9.20(livekit-client@2.18.6(@types/dom-mediacapture-record@1.0.22))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tslib@2.8.1)
|
||||
'@livekit/protocol':
|
||||
specifier: ^1.42.2
|
||||
version: 1.45.3
|
||||
'@livekit/track-processors':
|
||||
specifier: ^0.7.1
|
||||
version: 0.7.2(@types/dom-mediacapture-transform@0.1.11)(livekit-client@2.18.3(@types/dom-mediacapture-record@1.0.22))
|
||||
version: 0.7.2(@types/dom-mediacapture-transform@0.1.11)(livekit-client@2.18.6(@types/dom-mediacapture-record@1.0.22))
|
||||
'@mediapipe/tasks-vision':
|
||||
specifier: ^0.10.18
|
||||
version: 0.10.34
|
||||
@@ -148,8 +148,8 @@ importers:
|
||||
specifier: ^10.0.0
|
||||
version: 10.1.0(@types/react@19.2.14)(react@19.2.5)
|
||||
'@vector-im/compound-web':
|
||||
specifier: ^9.0.0
|
||||
version: 9.2.0(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.2.8)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@vector-im/compound-design-tokens@10.1.0(@types/react@19.2.14)(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
||||
specifier: ^9.3.0
|
||||
version: 9.3.0(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.2.8)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@vector-im/compound-design-tokens@10.1.0(@types/react@19.2.14)(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
||||
'@vitejs/plugin-react':
|
||||
specifier: ^4.0.1
|
||||
version: 4.7.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.7)(jiti@2.6.1)(sass@1.99.0)(terser@5.46.1)(yaml@2.8.3))
|
||||
@@ -227,7 +227,7 @@ importers:
|
||||
version: 5.88.1(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@types/node@24.12.2)(typescript@5.9.3)
|
||||
livekit-client:
|
||||
specifier: ^2.18.1
|
||||
version: 2.18.3(@types/dom-mediacapture-record@1.0.22)
|
||||
version: 2.18.6(@types/dom-mediacapture-record@1.0.22)
|
||||
lodash-es:
|
||||
specifier: ^4.17.21
|
||||
version: 4.18.1
|
||||
@@ -236,7 +236,7 @@ importers:
|
||||
version: 1.9.2
|
||||
matrix-js-sdk:
|
||||
specifier: matrix-org/matrix-js-sdk#develop
|
||||
version: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/4b33892d48017b3733a00a48cf5d30182be4a6fb
|
||||
version: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/bec985b783c666de24d6db92a4307a10416742d7
|
||||
matrix-widget-api:
|
||||
specifier: ^1.16.1
|
||||
version: 1.17.0
|
||||
@@ -301,8 +301,8 @@ importers:
|
||||
specifier: ^4.6.0
|
||||
version: 4.7.1
|
||||
uuid:
|
||||
specifier: ^13.0.0
|
||||
version: 13.0.0
|
||||
specifier: ^14.0.0
|
||||
version: 14.0.0
|
||||
vaul:
|
||||
specifier: ^1.0.0
|
||||
version: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
||||
@@ -1576,8 +1576,8 @@ packages:
|
||||
'@types/dom-mediacapture-transform': ^0.1.9
|
||||
livekit-client: ^1.12.0 || ^2.1.0
|
||||
|
||||
'@matrix-org/matrix-sdk-crypto-wasm@18.1.0':
|
||||
resolution: {integrity: sha512-GxXK2U39+2qWNvR3fXJY7nxdikvpiT17RaS0/Dktk6R8FMKDk3vm79Hq65yrCWLBmT7pJZoerfILNZqhrcUHrg==}
|
||||
'@matrix-org/matrix-sdk-crypto-wasm@18.2.0':
|
||||
resolution: {integrity: sha512-puyZefvq6sHfqlmkri8umhA44724H2JL0YtX8wlvhGuNl8awX/Q1tZyW2Iekm9ZJP7BtuOqlNdg9oQd6iaGbNw==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@mdx-js/react@3.1.1':
|
||||
@@ -2981,6 +2981,12 @@ packages:
|
||||
peerDependencies:
|
||||
typescript: '>=4.8.4 <6.1.0'
|
||||
|
||||
'@typescript-eslint/project-service@8.59.0':
|
||||
resolution: {integrity: sha512-Lw5ITrR5s5TbC19YSvlr63ZfLaJoU6vtKTHyB0GQOpX0W7d5/Ir6vUahWi/8Sps/nOukZQ0IB3SmlxZnjaKVnw==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
typescript: '>=4.8.4 <6.1.0'
|
||||
|
||||
'@typescript-eslint/scope-manager@5.62.0':
|
||||
resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
@@ -2989,12 +2995,22 @@ packages:
|
||||
resolution: {integrity: sha512-SgmyvDPexWETQek+qzZnrG6844IaO02UVyOLhI4wpo82dpZJY9+6YZCKAMFzXb7qhx37mFK1QcPQ18tud+vo6Q==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@typescript-eslint/scope-manager@8.59.0':
|
||||
resolution: {integrity: sha512-UzR16Ut8IpA3Mc4DbgAShlPPkVm8xXMWafXxB0BocaVRHs8ZGakAxGRskF7FId3sdk9lgGD73GSFaWmWFDE4dg==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@typescript-eslint/tsconfig-utils@8.58.2':
|
||||
resolution: {integrity: sha512-3SR+RukipDvkkKp/d0jP0dyzuls3DbGmwDpVEc5wqk5f38KFThakqAAO0XMirWAE+kT00oTauTbzMFGPoAzB0A==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
typescript: '>=4.8.4 <6.1.0'
|
||||
|
||||
'@typescript-eslint/tsconfig-utils@8.59.0':
|
||||
resolution: {integrity: sha512-91Sbl3s4Kb3SybliIY6muFBmHVv+pYXfybC4Oolp3dvk8BvIE3wOPc+403CWIT7mJNkfQRGtdqghzs2+Z91Tqg==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
typescript: '>=4.8.4 <6.1.0'
|
||||
|
||||
'@typescript-eslint/type-utils@8.58.2':
|
||||
resolution: {integrity: sha512-Z7EloNR/B389FvabdGeTo2XMs4W9TjtPiO9DAsmT0yom0bwlPyRjkJ1uCdW1DvrrrYP50AJZ9Xc3sByZA9+dcg==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
@@ -3010,6 +3026,10 @@ packages:
|
||||
resolution: {integrity: sha512-9TukXyATBQf/Jq9AMQXfvurk+G5R2MwfqQGDR2GzGz28HvY/lXNKGhkY+6IOubwcquikWk5cjlgPvD2uAA7htQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@typescript-eslint/types@8.59.0':
|
||||
resolution: {integrity: sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@typescript-eslint/typescript-estree@5.62.0':
|
||||
resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
@@ -3025,6 +3045,12 @@ packages:
|
||||
peerDependencies:
|
||||
typescript: '>=4.8.4 <6.1.0'
|
||||
|
||||
'@typescript-eslint/typescript-estree@8.59.0':
|
||||
resolution: {integrity: sha512-O9Re9P1BmBLFJyikRbQpLku/QA3/AueZNO9WePLBwQrvkixTmDe8u76B6CYUAITRl/rHawggEqUGn5QIkVRLMw==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
typescript: '>=4.8.4 <6.1.0'
|
||||
|
||||
'@typescript-eslint/utils@5.62.0':
|
||||
resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
@@ -3038,6 +3064,13 @@ packages:
|
||||
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
|
||||
typescript: '>=4.8.4 <6.1.0'
|
||||
|
||||
'@typescript-eslint/utils@8.59.0':
|
||||
resolution: {integrity: sha512-I1R/K7V07XsMJ12Oaxg/O9GfrysGTmCRhvZJBv0RE0NcULMzjqVpR5kRRQjHsz3J/bElU7HwCO7zkqL+MSUz+g==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
|
||||
typescript: '>=4.8.4 <6.1.0'
|
||||
|
||||
'@typescript-eslint/visitor-keys@5.62.0':
|
||||
resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
@@ -3046,6 +3079,10 @@ packages:
|
||||
resolution: {integrity: sha512-f1WO2Lx8a9t8DARmcWAUPJbu0G20bJlj8L4z72K00TMeJAoyLr/tHhI/pzYBLrR4dXWkcxO1cWYZEOX8DKHTqA==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@typescript-eslint/visitor-keys@8.59.0':
|
||||
resolution: {integrity: sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@ungap/structured-clone@1.3.0':
|
||||
resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
|
||||
|
||||
@@ -3068,8 +3105,8 @@ packages:
|
||||
react:
|
||||
optional: true
|
||||
|
||||
'@vector-im/compound-web@9.2.0':
|
||||
resolution: {integrity: sha512-jHbABGEQ2yqNtm5xRIkklQs198VEfSk9AJQolI+e4WSJ0xg8Ozyv9t9KIuKQAmjdSV9aow5G6hDE861XB6DQgw==}
|
||||
'@vector-im/compound-web@9.3.0':
|
||||
resolution: {integrity: sha512-Elu4Uw8RbfP6JaudQYkVibALYT6qpwubqfKhteTxIPWBWzSYM+P5T+B1uX+ra+grNcXwXUt2xfMxpqYQsAHgYA==}
|
||||
peerDependencies:
|
||||
'@fontsource/inconsolata': ^5
|
||||
'@fontsource/inter': ^5
|
||||
@@ -4936,8 +4973,8 @@ packages:
|
||||
lines-and-columns@1.2.4:
|
||||
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
|
||||
|
||||
livekit-client@2.18.3:
|
||||
resolution: {integrity: sha512-A8QDaVPo+Ye35bJFyKe6PjMOtY33dmdRXGKP/3+BG48ynEES3YwFzHbsPHJiScgI4OZouNef3Ew/BPazXKwo8Q==}
|
||||
livekit-client@2.18.6:
|
||||
resolution: {integrity: sha512-JTOSWkRrFC9KayPvasbnXpAmt+J/ILk5c8f3xUmjqazZk7j9QTyj0qhDHIgdyy/5KFqjqaRmtPu/InMB+WlkPA==}
|
||||
peerDependencies:
|
||||
'@types/dom-mediacapture-record': ^1
|
||||
|
||||
@@ -5011,8 +5048,8 @@ packages:
|
||||
matrix-events-sdk@0.0.1:
|
||||
resolution: {integrity: sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA==}
|
||||
|
||||
matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/4b33892d48017b3733a00a48cf5d30182be4a6fb:
|
||||
resolution: {tarball: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/4b33892d48017b3733a00a48cf5d30182be4a6fb}
|
||||
matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/bec985b783c666de24d6db92a4307a10416742d7:
|
||||
resolution: {tarball: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/bec985b783c666de24d6db92a4307a10416742d7}
|
||||
version: 41.3.0
|
||||
engines: {node: '>=22.0.0'}
|
||||
|
||||
@@ -6421,8 +6458,8 @@ packages:
|
||||
util@0.12.5:
|
||||
resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==}
|
||||
|
||||
uuid@13.0.0:
|
||||
resolution: {integrity: sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==}
|
||||
uuid@14.0.0:
|
||||
resolution: {integrity: sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==}
|
||||
hasBin: true
|
||||
|
||||
validate-npm-package-license@3.0.4:
|
||||
@@ -6611,8 +6648,8 @@ packages:
|
||||
webpack-virtual-modules@0.6.2:
|
||||
resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==}
|
||||
|
||||
webrtc-adapter@9.0.4:
|
||||
resolution: {integrity: sha512-5ZZY1+lGq8LEKuDlg9M2RPJHlH3R7OVwyHqMcUsLKCgd9Wvf+QrFTCItkXXYPmrJn8H6gRLXbSgxLLdexiqHxw==}
|
||||
webrtc-adapter@9.0.5:
|
||||
resolution: {integrity: sha512-U9vjByy/sK2OMXu5mmfuZFKTMIUQe34c0JXRO+oDrxJTsntdYT2iIFwYMOV7HhMTuktcZLGf2W1N/OcSf9ssWg==}
|
||||
engines: {node: '>=6.0.0', npm: '>=3.10.0'}
|
||||
|
||||
whatwg-encoding@3.1.1:
|
||||
@@ -8115,21 +8152,21 @@ snapshots:
|
||||
'@jridgewell/resolve-uri': 3.1.2
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
'@livekit/components-core@0.12.13(livekit-client@2.18.3(@types/dom-mediacapture-record@1.0.22))(tslib@2.8.1)':
|
||||
'@livekit/components-core@0.12.13(livekit-client@2.18.6(@types/dom-mediacapture-record@1.0.22))(tslib@2.8.1)':
|
||||
dependencies:
|
||||
'@floating-ui/dom': 1.7.4
|
||||
livekit-client: 2.18.3(@types/dom-mediacapture-record@1.0.22)
|
||||
livekit-client: 2.18.6(@types/dom-mediacapture-record@1.0.22)
|
||||
loglevel: 1.9.1
|
||||
rxjs: 7.8.2
|
||||
tslib: 2.8.1
|
||||
|
||||
'@livekit/components-react@2.9.20(livekit-client@2.18.3(@types/dom-mediacapture-record@1.0.22))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tslib@2.8.1)':
|
||||
'@livekit/components-react@2.9.20(livekit-client@2.18.6(@types/dom-mediacapture-record@1.0.22))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tslib@2.8.1)':
|
||||
dependencies:
|
||||
'@livekit/components-core': 0.12.13(livekit-client@2.18.3(@types/dom-mediacapture-record@1.0.22))(tslib@2.8.1)
|
||||
'@livekit/components-core': 0.12.13(livekit-client@2.18.6(@types/dom-mediacapture-record@1.0.22))(tslib@2.8.1)
|
||||
clsx: 2.1.1
|
||||
events: 3.3.0
|
||||
jose: 6.2.2
|
||||
livekit-client: 2.18.3(@types/dom-mediacapture-record@1.0.22)
|
||||
livekit-client: 2.18.6(@types/dom-mediacapture-record@1.0.22)
|
||||
react: 19.2.5
|
||||
react-dom: 19.2.5(react@19.2.5)
|
||||
tslib: 2.8.1
|
||||
@@ -8141,13 +8178,13 @@ snapshots:
|
||||
dependencies:
|
||||
'@bufbuild/protobuf': 1.10.1
|
||||
|
||||
'@livekit/track-processors@0.7.2(@types/dom-mediacapture-transform@0.1.11)(livekit-client@2.18.3(@types/dom-mediacapture-record@1.0.22))':
|
||||
'@livekit/track-processors@0.7.2(@types/dom-mediacapture-transform@0.1.11)(livekit-client@2.18.6(@types/dom-mediacapture-record@1.0.22))':
|
||||
dependencies:
|
||||
'@mediapipe/tasks-vision': 0.10.34
|
||||
'@types/dom-mediacapture-transform': 0.1.11
|
||||
livekit-client: 2.18.3(@types/dom-mediacapture-record@1.0.22)
|
||||
livekit-client: 2.18.6(@types/dom-mediacapture-record@1.0.22)
|
||||
|
||||
'@matrix-org/matrix-sdk-crypto-wasm@18.1.0': {}
|
||||
'@matrix-org/matrix-sdk-crypto-wasm@18.2.0': {}
|
||||
|
||||
'@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.5)':
|
||||
dependencies:
|
||||
@@ -9399,6 +9436,15 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/project-service@8.59.0(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/tsconfig-utils': 8.59.0(typescript@5.9.3)
|
||||
'@typescript-eslint/types': 8.59.0
|
||||
debug: 4.4.3
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/scope-manager@5.62.0':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 5.62.0
|
||||
@@ -9409,10 +9455,19 @@ snapshots:
|
||||
'@typescript-eslint/types': 8.58.2
|
||||
'@typescript-eslint/visitor-keys': 8.58.2
|
||||
|
||||
'@typescript-eslint/scope-manager@8.59.0':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.59.0
|
||||
'@typescript-eslint/visitor-keys': 8.59.0
|
||||
|
||||
'@typescript-eslint/tsconfig-utils@8.58.2(typescript@5.9.3)':
|
||||
dependencies:
|
||||
typescript: 5.9.3
|
||||
|
||||
'@typescript-eslint/tsconfig-utils@8.59.0(typescript@5.9.3)':
|
||||
dependencies:
|
||||
typescript: 5.9.3
|
||||
|
||||
'@typescript-eslint/type-utils@8.58.2(eslint@8.57.1)(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.58.2
|
||||
@@ -9429,6 +9484,8 @@ snapshots:
|
||||
|
||||
'@typescript-eslint/types@8.58.2': {}
|
||||
|
||||
'@typescript-eslint/types@8.59.0': {}
|
||||
|
||||
'@typescript-eslint/typescript-estree@5.62.0(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 5.62.0
|
||||
@@ -9458,6 +9515,21 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/typescript-estree@8.59.0(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/project-service': 8.59.0(typescript@5.9.3)
|
||||
'@typescript-eslint/tsconfig-utils': 8.59.0(typescript@5.9.3)
|
||||
'@typescript-eslint/types': 8.59.0
|
||||
'@typescript-eslint/visitor-keys': 8.59.0
|
||||
debug: 4.4.3
|
||||
minimatch: 10.2.5
|
||||
semver: 7.7.4
|
||||
tinyglobby: 0.2.16
|
||||
ts-api-utils: 2.5.0(typescript@5.9.3)
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/utils@5.62.0(eslint@8.57.1)(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1)
|
||||
@@ -9484,6 +9556,17 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/utils@8.59.0(eslint@8.57.1)(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1)
|
||||
'@typescript-eslint/scope-manager': 8.59.0
|
||||
'@typescript-eslint/types': 8.59.0
|
||||
'@typescript-eslint/typescript-estree': 8.59.0(typescript@5.9.3)
|
||||
eslint: 8.57.1
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/visitor-keys@5.62.0':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 5.62.0
|
||||
@@ -9494,6 +9577,11 @@ snapshots:
|
||||
'@typescript-eslint/types': 8.58.2
|
||||
eslint-visitor-keys: 5.0.1
|
||||
|
||||
'@typescript-eslint/visitor-keys@8.59.0':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.59.0
|
||||
eslint-visitor-keys: 5.0.1
|
||||
|
||||
'@ungap/structured-clone@1.3.0': {}
|
||||
|
||||
'@use-gesture/core@10.3.1': {}
|
||||
@@ -9508,7 +9596,7 @@ snapshots:
|
||||
'@types/react': 19.2.14
|
||||
react: 19.2.5
|
||||
|
||||
'@vector-im/compound-web@9.2.0(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.2.8)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@vector-im/compound-design-tokens@10.1.0(@types/react@19.2.14)(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
|
||||
'@vector-im/compound-web@9.3.0(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.2.8)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@vector-im/compound-design-tokens@10.1.0(@types/react@19.2.14)(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
|
||||
dependencies:
|
||||
'@floating-ui/react': 0.27.19(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
||||
'@fontsource/inconsolata': 5.2.8
|
||||
@@ -10652,7 +10740,7 @@ snapshots:
|
||||
|
||||
eslint-plugin-jest@29.15.2(@typescript-eslint/eslint-plugin@8.58.2(@typescript-eslint/parser@8.58.2(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3):
|
||||
dependencies:
|
||||
'@typescript-eslint/utils': 8.58.2(eslint@8.57.1)(typescript@5.9.3)
|
||||
'@typescript-eslint/utils': 8.59.0(eslint@8.57.1)(typescript@5.9.3)
|
||||
eslint: 8.57.1
|
||||
optionalDependencies:
|
||||
'@typescript-eslint/eslint-plugin': 8.58.2(@typescript-eslint/parser@8.58.2(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)
|
||||
@@ -11710,7 +11798,7 @@ snapshots:
|
||||
|
||||
lines-and-columns@1.2.4: {}
|
||||
|
||||
livekit-client@2.18.3(@types/dom-mediacapture-record@1.0.22):
|
||||
livekit-client@2.18.6(@types/dom-mediacapture-record@1.0.22):
|
||||
dependencies:
|
||||
'@livekit/mutex': 1.1.1
|
||||
'@livekit/protocol': 1.45.3
|
||||
@@ -11721,7 +11809,7 @@ snapshots:
|
||||
sdp-transform: 2.15.0
|
||||
tslib: 2.8.1
|
||||
typed-emitter: 2.1.0
|
||||
webrtc-adapter: 9.0.4
|
||||
webrtc-adapter: 9.0.5
|
||||
|
||||
locate-path@5.0.0:
|
||||
dependencies:
|
||||
@@ -11786,10 +11874,10 @@ snapshots:
|
||||
|
||||
matrix-events-sdk@0.0.1: {}
|
||||
|
||||
matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/4b33892d48017b3733a00a48cf5d30182be4a6fb:
|
||||
matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/bec985b783c666de24d6db92a4307a10416742d7:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.29.2
|
||||
'@matrix-org/matrix-sdk-crypto-wasm': 18.1.0
|
||||
'@matrix-org/matrix-sdk-crypto-wasm': 18.2.0
|
||||
another-json: 0.2.0
|
||||
bs58: 6.0.0
|
||||
content-type: 1.0.5
|
||||
@@ -11801,7 +11889,6 @@ snapshots:
|
||||
p-retry: 8.0.0
|
||||
sdp-transform: 3.0.0
|
||||
unhomoglyph: 1.0.6
|
||||
uuid: 13.0.0
|
||||
|
||||
matrix-widget-api@1.17.0:
|
||||
dependencies:
|
||||
@@ -13439,7 +13526,7 @@ snapshots:
|
||||
is-typed-array: 1.1.15
|
||||
which-typed-array: 1.1.20
|
||||
|
||||
uuid@13.0.0: {}
|
||||
uuid@14.0.0: {}
|
||||
|
||||
validate-npm-package-license@3.0.4:
|
||||
dependencies:
|
||||
@@ -13643,7 +13730,7 @@ snapshots:
|
||||
|
||||
webpack-virtual-modules@0.6.2: {}
|
||||
|
||||
webrtc-adapter@9.0.4:
|
||||
webrtc-adapter@9.0.5:
|
||||
dependencies:
|
||||
sdp: 3.2.2
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ export const RTCConnectionStats: FC<Props> = ({
|
||||
<div>
|
||||
<Button
|
||||
onClick={() => showFullModal("audio")}
|
||||
size="sm"
|
||||
size="md"
|
||||
kind="tertiary"
|
||||
Icon={MicOnSolidIcon}
|
||||
>
|
||||
@@ -103,7 +103,7 @@ export const RTCConnectionStats: FC<Props> = ({
|
||||
<div>
|
||||
<Button
|
||||
onClick={() => showFullModal("video")}
|
||||
size="sm"
|
||||
size="md"
|
||||
kind="tertiary"
|
||||
Icon={VideoCallSolidIcon}
|
||||
>
|
||||
|
||||
@@ -32,7 +32,7 @@ import { platform } from "../Platform";
|
||||
|
||||
interface MicButtonProps extends ComponentPropsWithoutRef<"button"> {
|
||||
enabled: boolean;
|
||||
size?: "sm" | "lg";
|
||||
size?: "md" | "lg";
|
||||
}
|
||||
|
||||
export const MicButton: FC<MicButtonProps> = ({ enabled, ...props }) => {
|
||||
@@ -58,7 +58,7 @@ export const MicButton: FC<MicButtonProps> = ({ enabled, ...props }) => {
|
||||
|
||||
interface VideoButtonProps extends ComponentPropsWithoutRef<"button"> {
|
||||
enabled: boolean;
|
||||
size?: "sm" | "lg";
|
||||
size?: "md" | "lg";
|
||||
}
|
||||
|
||||
export const VideoButton: FC<VideoButtonProps> = ({ enabled, ...props }) => {
|
||||
@@ -84,7 +84,7 @@ export const VideoButton: FC<VideoButtonProps> = ({ enabled, ...props }) => {
|
||||
|
||||
interface ShareScreenButtonProps extends ComponentPropsWithoutRef<"button"> {
|
||||
enabled: boolean;
|
||||
size: "sm" | "lg";
|
||||
size: "md" | "lg";
|
||||
}
|
||||
|
||||
export const ShareScreenButton: FC<ShareScreenButtonProps> = ({
|
||||
@@ -111,7 +111,7 @@ export const ShareScreenButton: FC<ShareScreenButtonProps> = ({
|
||||
};
|
||||
|
||||
interface EndCallButtonProps extends ComponentPropsWithoutRef<"button"> {
|
||||
size?: "sm" | "lg";
|
||||
size?: "md" | "lg";
|
||||
}
|
||||
|
||||
export const EndCallButton: FC<EndCallButtonProps> = ({
|
||||
@@ -134,7 +134,7 @@ export const EndCallButton: FC<EndCallButtonProps> = ({
|
||||
};
|
||||
|
||||
interface LoudspeakerButtonProps extends ComponentPropsWithoutRef<"button"> {
|
||||
size?: "sm" | "lg";
|
||||
size?: "md" | "lg";
|
||||
loudspeakerModeEnabled: boolean;
|
||||
}
|
||||
export const LoudspeakerButton: FC<LoudspeakerButtonProps> = ({
|
||||
@@ -195,7 +195,7 @@ export const SettingsIconButton: FC<SettingsIconButtonProps> = ({
|
||||
};
|
||||
|
||||
interface SettingsButtonProps extends ComponentPropsWithoutRef<"button"> {
|
||||
size?: "sm" | "lg";
|
||||
size?: "md" | "lg";
|
||||
/** If this buttons should be setup to be used in the app bar */
|
||||
showForScreenWidth?: "wide" | "narrow";
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ export const InviteButton: FC<
|
||||
> = (props) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Button kind="secondary" size="sm" Icon={UserAddIcon} {...props}>
|
||||
<Button kind="secondary" size="md" Icon={UserAddIcon} {...props}>
|
||||
{t("action.invite")}
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -173,7 +173,7 @@ export interface ReactionData {
|
||||
interface ReactionToggleButtonProps extends ComponentPropsWithoutRef<"button"> {
|
||||
reactionData: ReactionData;
|
||||
identifier: string;
|
||||
size?: "sm" | "lg";
|
||||
size?: "md" | "lg";
|
||||
/** List of participants raising their hand */
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ exports[`Can close reaction dialog 1`] = `
|
||||
aria-expanded="true"
|
||||
aria-haspopup="true"
|
||||
aria-labelledby="_r_bb_"
|
||||
class="_button_13vu4_8 _has-icon_13vu4_60 _icon-only_13vu4_53"
|
||||
class="_button_1nw83_8 _has-icon_1nw83_60 _icon-only_1nw83_53"
|
||||
data-kind="primary"
|
||||
data-size="lg"
|
||||
role="button"
|
||||
@@ -44,7 +44,7 @@ exports[`Can fully expand emoji picker 1`] = `
|
||||
aria-expanded="true"
|
||||
aria-haspopup="true"
|
||||
aria-labelledby="_r_7m_"
|
||||
class="_button_13vu4_8 _has-icon_13vu4_60 _icon-only_13vu4_53"
|
||||
class="_button_1nw83_8 _has-icon_1nw83_60 _icon-only_1nw83_53"
|
||||
data-kind="primary"
|
||||
data-size="lg"
|
||||
role="button"
|
||||
@@ -75,7 +75,7 @@ exports[`Can lower hand 1`] = `
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
aria-labelledby="_r_36_"
|
||||
class="_button_13vu4_8 _has-icon_13vu4_60 _icon-only_13vu4_53"
|
||||
class="_button_1nw83_8 _has-icon_1nw83_60 _icon-only_1nw83_53"
|
||||
data-kind="secondary"
|
||||
data-size="lg"
|
||||
role="button"
|
||||
@@ -109,7 +109,7 @@ exports[`Can open menu 1`] = `
|
||||
aria-expanded="true"
|
||||
aria-haspopup="true"
|
||||
aria-labelledby="_r_0_"
|
||||
class="_button_13vu4_8 _has-icon_13vu4_60 _icon-only_13vu4_53"
|
||||
class="_button_1nw83_8 _has-icon_1nw83_60 _icon-only_1nw83_53"
|
||||
data-kind="primary"
|
||||
data-size="lg"
|
||||
role="button"
|
||||
@@ -140,7 +140,7 @@ exports[`Can raise hand 1`] = `
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
aria-labelledby="_r_1j_"
|
||||
class="_button_13vu4_8 raisedButton _has-icon_13vu4_60 _icon-only_13vu4_53"
|
||||
class="_button_1nw83_8 raisedButton _has-icon_1nw83_60 _icon-only_1nw83_53"
|
||||
data-kind="primary"
|
||||
data-size="lg"
|
||||
role="button"
|
||||
|
||||
@@ -101,7 +101,7 @@ export const CallFooter: FC<FooterProps> = ({
|
||||
tileStoreGeneration,
|
||||
}) => {
|
||||
const buttons: JSX.Element[] = [];
|
||||
const buttonSize = asPip ? "sm" : "lg";
|
||||
const buttonSize = asPip ? "md" : "lg";
|
||||
const showSettingsButton =
|
||||
openSettings !== undefined && !asPip && !hideControls;
|
||||
const showLayoutSwitcher = !asPip && !hideControls;
|
||||
|
||||
@@ -113,7 +113,7 @@ export const AvatarInputField: FC<Props> = ({
|
||||
iconOnly
|
||||
Icon={EditIcon}
|
||||
kind="tertiary"
|
||||
size="sm"
|
||||
size="md"
|
||||
aria-label={t("action.edit")}
|
||||
/>
|
||||
}
|
||||
@@ -136,7 +136,7 @@ export const AvatarInputField: FC<Props> = ({
|
||||
iconOnly
|
||||
Icon={EditIcon}
|
||||
kind="tertiary"
|
||||
size="sm"
|
||||
size="md"
|
||||
aria-label={t("action.edit")}
|
||||
onClick={onSelectUpload}
|
||||
/>
|
||||
|
||||
@@ -30,7 +30,7 @@ export const EarpieceOverlay: FC<Props> = ({ show, onBackToVideoPressed }) => {
|
||||
<Text>{t("handset.overlay_description")}</Text>
|
||||
<Button
|
||||
kind="primary"
|
||||
size="sm"
|
||||
size="md"
|
||||
onClick={() => {
|
||||
onBackToVideoPressed?.();
|
||||
}}
|
||||
|
||||
@@ -215,7 +215,7 @@ export const LobbyView: FC<Props> = ({
|
||||
className={classNames(styles.join, {
|
||||
[styles.wait]: waitingForInvite,
|
||||
})}
|
||||
size={waitingForInvite ? "sm" : "lg"}
|
||||
size={waitingForInvite ? "md" : "lg"}
|
||||
disabled={waitingForInvite}
|
||||
onClick={() => {
|
||||
if (!waitingForInvite) onEnter();
|
||||
|
||||
@@ -134,7 +134,7 @@ exports[`ConnectionLostError: Action handling should reset error state 1`] = `
|
||||
You were disconnected from the call.
|
||||
</p>
|
||||
<button
|
||||
class="_button_13vu4_8"
|
||||
class="_button_1nw83_8"
|
||||
data-kind="secondary"
|
||||
data-size="lg"
|
||||
role="button"
|
||||
@@ -143,7 +143,7 @@ exports[`ConnectionLostError: Action handling should reset error state 1`] = `
|
||||
Reconnect
|
||||
</button>
|
||||
<button
|
||||
class="_button_13vu4_8 homeLink"
|
||||
class="_button_1nw83_8 homeLink"
|
||||
data-kind="tertiary"
|
||||
data-size="lg"
|
||||
role="button"
|
||||
@@ -297,7 +297,7 @@ exports[`should have a close button in widget mode 1`] = `
|
||||
The server is not configured to work with Element Call. Please contact your server admin (Domain: example.com, Error Code: MISSING_MATRIX_RTC_TRANSPORT).
|
||||
</p>
|
||||
<button
|
||||
class="_button_13vu4_8"
|
||||
class="_button_1nw83_8"
|
||||
data-kind="primary"
|
||||
data-size="lg"
|
||||
role="button"
|
||||
@@ -451,7 +451,7 @@ exports[`should render the error page with link back to home 1`] = `
|
||||
The server is not configured to work with Element Call. Please contact your server admin (Domain: example.com, Error Code: MISSING_MATRIX_RTC_TRANSPORT).
|
||||
</p>
|
||||
<button
|
||||
class="_button_13vu4_8 homeLink"
|
||||
class="_button_1nw83_8 homeLink"
|
||||
data-kind="tertiary"
|
||||
data-size="lg"
|
||||
role="button"
|
||||
@@ -605,7 +605,7 @@ exports[`should report correct error for 'Call is not supported' 1`] = `
|
||||
The server is not configured to work with Element Call. Please contact your server admin (Domain: example.com, Error Code: MISSING_MATRIX_RTC_TRANSPORT).
|
||||
</p>
|
||||
<button
|
||||
class="_button_13vu4_8 homeLink"
|
||||
class="_button_1nw83_8 homeLink"
|
||||
data-kind="tertiary"
|
||||
data-size="lg"
|
||||
role="button"
|
||||
@@ -754,7 +754,7 @@ exports[`should report correct error for 'Connection lost' 1`] = `
|
||||
You were disconnected from the call.
|
||||
</p>
|
||||
<button
|
||||
class="_button_13vu4_8"
|
||||
class="_button_1nw83_8"
|
||||
data-kind="secondary"
|
||||
data-size="lg"
|
||||
role="button"
|
||||
@@ -763,7 +763,7 @@ exports[`should report correct error for 'Connection lost' 1`] = `
|
||||
Reconnect
|
||||
</button>
|
||||
<button
|
||||
class="_button_13vu4_8 homeLink"
|
||||
class="_button_1nw83_8 homeLink"
|
||||
data-kind="tertiary"
|
||||
data-size="lg"
|
||||
role="button"
|
||||
@@ -912,7 +912,7 @@ exports[`should report correct error for 'Incompatible browser' 1`] = `
|
||||
Your web browser does not support encrypted calls. Supported browsers include Chrome, Safari, and Firefox 117+.
|
||||
</p>
|
||||
<button
|
||||
class="_button_13vu4_8 homeLink"
|
||||
class="_button_1nw83_8 homeLink"
|
||||
data-kind="tertiary"
|
||||
data-size="lg"
|
||||
role="button"
|
||||
@@ -1061,7 +1061,7 @@ exports[`should report correct error for 'Insufficient capacity' 1`] = `
|
||||
The server has reached its maximum capacity and you cannot join the call at this time. Try again later, or contact your server admin if the problem persists.
|
||||
</p>
|
||||
<button
|
||||
class="_button_13vu4_8 homeLink"
|
||||
class="_button_1nw83_8 homeLink"
|
||||
data-kind="tertiary"
|
||||
data-size="lg"
|
||||
role="button"
|
||||
|
||||
@@ -147,9 +147,9 @@ exports[`InCallView > rendering > renders 1`] = `
|
||||
Only works while using app
|
||||
</p>
|
||||
<button
|
||||
class="_button_13vu4_8"
|
||||
class="_button_1nw83_8"
|
||||
data-kind="primary"
|
||||
data-size="sm"
|
||||
data-size="md"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
@@ -305,7 +305,7 @@ exports[`InCallView > rendering > renders 1`] = `
|
||||
>
|
||||
<button
|
||||
aria-labelledby="_r_d_"
|
||||
class="_button_13vu4_8 settingsOnlyShowNarrow _has-icon_13vu4_60 _icon-only_13vu4_53"
|
||||
class="_button_1nw83_8 settingsOnlyShowNarrow _has-icon_1nw83_60 _icon-only_1nw83_53"
|
||||
data-kind="secondary"
|
||||
data-size="lg"
|
||||
data-testid="settings-bottom-center"
|
||||
@@ -329,7 +329,7 @@ exports[`InCallView > rendering > renders 1`] = `
|
||||
aria-checked="false"
|
||||
aria-disabled="true"
|
||||
aria-labelledby="_r_i_"
|
||||
class="_button_13vu4_8 _has-icon_13vu4_60 _icon-only_13vu4_53"
|
||||
class="_button_1nw83_8 _has-icon_1nw83_60 _icon-only_1nw83_53"
|
||||
data-kind="primary"
|
||||
data-size="lg"
|
||||
data-testid="incall_mute"
|
||||
@@ -353,7 +353,7 @@ exports[`InCallView > rendering > renders 1`] = `
|
||||
aria-checked="false"
|
||||
aria-disabled="true"
|
||||
aria-labelledby="_r_n_"
|
||||
class="_button_13vu4_8 _has-icon_13vu4_60 _icon-only_13vu4_53"
|
||||
class="_button_1nw83_8 _has-icon_1nw83_60 _icon-only_1nw83_53"
|
||||
data-kind="primary"
|
||||
data-size="lg"
|
||||
data-testid="incall_videomute"
|
||||
@@ -375,7 +375,7 @@ exports[`InCallView > rendering > renders 1`] = `
|
||||
</button>
|
||||
<button
|
||||
aria-labelledby="_r_s_"
|
||||
class="_button_13vu4_8 endCall _has-icon_13vu4_60 _icon-only_13vu4_53 _destructive_13vu4_110"
|
||||
class="_button_1nw83_8 endCall _has-icon_1nw83_60 _icon-only_1nw83_53 _destructive_1nw83_110"
|
||||
data-kind="primary"
|
||||
data-size="lg"
|
||||
data-testid="incall_leave"
|
||||
|
||||
Reference in New Issue
Block a user