diff --git a/README.md b/README.md index e7f9a62d7..5f1ed6bd2 100644 --- a/README.md +++ b/README.md @@ -297,8 +297,8 @@ The package is not published yet. A host installs it as a git dependency on the ``` whose `prepare` script runs the build on install. That build needs pnpm (via -Corepack) on the host's machine, runs a full `pnpm install` of this repository -and is memory-hungry, since it inherits the `--max-old-space-size` setting of +Corepack) on the host's machine, runs a full `pnpm install` of this repository, +since it inherits the `--max-old-space-size` setting of the app build; the host's pnpm also has to allow it to run at all (`allowBuilds` in its `pnpm-workspace.yaml`). Note that `component/` is a pnpm project of its own for this reason, so pnpm commands run from inside that @@ -306,8 +306,10 @@ directory target it rather than the repository; run them from the repository root. The host imports the component from `@element-hq/element-call-component` and the stylesheet from `@element-hq/element-call-component/style.css`, and has to provide `react`, -`react-dom`, `matrix-js-sdk` and `livekit-client` itself, since the bundle leaves -them external. +`react-dom`, `matrix-js-sdk`, `livekit-client`, `@vector-im/compound-web` and +`@vector-im/compound-design-tokens` itself, since the bundle leaves them +external. Compound's stylesheet are the host's to load too: the component shares +the host's copy of Compound, (required for example for tooltips) The component is large, and a host will usually load it lazily, only once a call is shown. Everything a host needs in order to talk about a call before diff --git a/component/ElementCall.module.css b/component/ElementCall.module.css index 91213e422..8d5484042 100644 --- a/component/ElementCall.module.css +++ b/component/ElementCall.module.css @@ -34,10 +34,18 @@ component rather than a page makes. */ -webkit-tap-highlight-color: transparent; } -/* Compound's overlay container, which holds tooltips and popovers, has to fill -the container for the elements inside it to be positioned against it. The -standalone page does the same for the container under `#root`. */ -.root > [data-overlay-container] { - position: relative; +/* The call itself. It is a stacking context of its own, as the standalone +page's `#root` is, so that the layers the call view uses for its header, footer +and overlays stay below the modals and toasts, which are portalled into the +root and carry no layer of their own. + +What Compound floats — tooltips, menus — is not in here at all: it goes into +the host's body, as it would for any other component on the host's page, and is +styled and layered by the host's copy of Compound. */ +.content { + display: flex; + flex-direction: column; block-size: 100%; + position: relative; + isolation: isolate; } diff --git a/component/build/scopeStylesToRoot.ts b/component/build/scopeStylesToRoot.ts index 9e7e80376..e7d9e1d62 100644 --- a/component/build/scopeStylesToRoot.ts +++ b/component/build/scopeStylesToRoot.ts @@ -40,10 +40,10 @@ const IS_ROOT = `:where(${ROOT_SELECTOR})`; * Element Call as a component. * * As a page of its own, Element Call can style the document: normalize.css and - * Compound speak of `html`, `body` and bare elements, and the design tokens are - * declared on `:root`. As a component, all of that would land on the host's - * document too. This rewrites every selector so that it matches only the root - * or its descendants: + * its own base styles speak of `html`, `body` and bare elements, and its custom + * properties are declared on `:root`. As a component, all of that would land on + * the host's document too. This rewrites every selector so that it matches only + * the root or its descendants: * * - `html`, `body` and `:root` become the root element, which is what stands in * for the document inside a host. @@ -55,9 +55,10 @@ const IS_ROOT = `:where(${ROOT_SELECTOR})`; * CSS modules are scoped by their class names already, so only their selectors * that would match by element alone — `pre` rather than `.pre` — are touched. * - * The root's fonts and design tokens are still inherited by everything inside - * it, the way they were from `body` and `:root`, and `@font-face` declarations - * stay global, which they are by nature. + * The root's custom properties are still inherited by everything inside it, + * the way they were from `:root`. Compound's own stylesheet — the design + * tokens, the component styles, the fonts — is not in this build at all: the + * host supplies it, unscoped, along with its copy of Compound. */ export function scopeStylesToRoot(): Plugin { return { diff --git a/component/dev/main.tsx b/component/dev/main.tsx index 9150678d5..b9d12e2da 100644 --- a/component/dev/main.tsx +++ b/component/dev/main.tsx @@ -9,6 +9,16 @@ import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; import { logger } from "matrix-js-sdk/lib/logger"; +// What a host provides along with its copy of Compound: its stylesheet, the +// design tokens and the fonts they name. The component does not bundle them. +import "@fontsource/inter/400.css"; +import "@fontsource/inter/500.css"; +import "@fontsource/inter/600.css"; +import "@fontsource/inter/700.css"; +import "@fontsource/inconsolata/400.css"; +import "@fontsource/inconsolata/700.css"; +import "@vector-im/compound-design-tokens/assets/web/css/compound-design-tokens.css"; +import "@vector-im/compound-web/dist/style.css"; import { type ConfigOptions, initializeElementCall } from "../index"; import { Harness } from "./Harness"; // After Element Call's, so that the host has the last word on its own page diff --git a/component/index.tsx b/component/index.tsx index 128f4a55e..8f901c139 100644 --- a/component/index.tsx +++ b/component/index.tsx @@ -17,19 +17,25 @@ Please see LICENSE in the repository root for full details. * host instead, or is confined to the container it is mounted in. */ -// The design tokens, fonts and element defaults every Element Call stylesheet +// The element defaults and custom properties every Element Call stylesheet // builds on. Written for a page, they speak of `html`, `body` and bare // elements; the component build confines them, and every other stylesheet in // this bundle, to the root element below (see build/scopeStylesToRoot.ts), so // that the host's document is left as it was. // +// Compound is not in here. The component uses the host's copy of it (a peer +// dependency, left external by the build), and the host loads Compound's +// stylesheet, design tokens and fonts along with it: what Compound floats into +// the host's body — tooltips, menus — is then styled by the same stylesheet as +// everything else there, and Element Call's root takes the tokens and fonts of +// the theme class it carries, as any element on the host's page would. +// // Where these land relative to the component stylesheets is the bundler's // choice — the standalone app puts them first, this build puts them in the // middle — so nothing in base.css may depend on winning or losing against a // component's own rules at equal specificity. It currently does not: what it // declares unlayered is custom properties on Element Call's root, which -// components inherit rather than compete with, and everything from Compound -// sits in a `@layer`, which loses to unlayered rules either way. +// components inherit rather than compete with. import "../src/base.css"; import { @@ -231,34 +237,38 @@ export const ElementCall: FC = ({
{ready && ( - - {/* Whatever goes wrong in here is shown in here. Left to + // Stands in for the standalone page's `#root`: see `.content` + // in ElementCall.module.css +
+ + {/* Whatever goes wrong in here is shown in here. Left to propagate, an error would unmount the host's own tree. */} - } - // A broken call should not hold the host on screen - onError={() => void hostBridge.setAlwaysOnScreen(false)} - > - - - - - - - - - - - - - + } + // A broken call should not hold the host on screen + onError={() => void hostBridge.setAlwaysOnScreen(false)} + > + + + + + + + + + + + + + +
)}
diff --git a/component/package.json b/component/package.json index 79aa286c5..ee81cd51b 100644 --- a/component/package.json +++ b/component/package.json @@ -38,6 +38,8 @@ "prepare": "cd .. && pnpm install --frozen-lockfile && pnpm build:component" }, "peerDependencies": { + "@vector-im/compound-design-tokens": "^11.0.0", + "@vector-im/compound-web": "^10.0.0", "livekit-client": "^2.18.1", "matrix-js-sdk": "*", "react": "^19", diff --git a/docs/agents/architecture.md b/docs/agents/architecture.md index bd00bf325..fab783c19 100644 --- a/docs/agents/architecture.md +++ b/docs/agents/architecture.md @@ -76,7 +76,10 @@ Code that builds in only one is a bug. - `build:component` — `@element-hq/element-call-component`, sources in `component/` (its own pnpm project; run pnpm from the repo root). Host API is in the README; `pnpm lint:externals` rejects an import of a `react` / `react-dom` / - `matrix-js-sdk` / `livekit-client` subpath the externals list omits. Two entry + `matrix-js-sdk` / `livekit-client` / `@vector-im/compound-web` / + `@vector-im/compound-design-tokens` subpath the externals list omits. + Compound's stylesheets are loaded by `src/index.css` for the page and by the + host for the component, never by `src/base.css`. Two entry points: `index.tsx` (the component) and `api.ts` (types, enums and `configurationForIntent`, for a host that must not load the component). `api.ts` may only reach types and constants — `src/UrlConfiguration.ts`, not `UrlParams`. diff --git a/docs/matrix_rtc_modes.md b/docs/matrix_rtc_modes.md index a30248ba0..4f2e864c4 100644 --- a/docs/matrix_rtc_modes.md +++ b/docs/matrix_rtc_modes.md @@ -24,7 +24,7 @@ homeserver does not support sticky events. This is the default. MSC4354 and all clients on v0.17.0 or later. The local membership requests its token from the Matrix 2.0 JWT endpoint of the [MatrixRTC Authorization Service](https://github.com/element-hq/lk-jwt-service) -and identifies the room by a hashed identity instead of a `livekit_alias`. +and identifies the room by a hashed identity instead of the Matrix `room_id`. (Remote memberships always try the new endpoint first and fall back to the legacy one, so remote participants can be on either.) diff --git a/package.json b/package.json index c7642f57a..05c9312bd 100644 --- a/package.json +++ b/package.json @@ -103,7 +103,7 @@ "livekit-client": "^2.18.1", "lodash-es": "^4.17.21", "loglevel": "^1.9.1", - "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop", + "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#robin/remove-oldest-membership", "matrix-widget-api": "^1.18.0", "node-stdlib-browser": "^1.3.1", "normalize.css": "^8.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index be07cd290..a982f7af3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -191,8 +191,8 @@ importers: specifier: ^1.9.1 version: 1.9.2 matrix-js-sdk: - specifier: github:matrix-org/matrix-js-sdk#develop - version: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/24929be0e741be6a5d0a7226f1c682e245263b8a + specifier: github:matrix-org/matrix-js-sdk#robin/remove-oldest-membership + version: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/6defa47adae28feb7e3906dde25089e0dce42ece matrix-widget-api: specifier: ^1.18.0 version: 1.19.0 @@ -1194,8 +1194,8 @@ packages: '@types/dom-mediacapture-transform': ^0.1.9 livekit-client: ^1.12.0 || ^2.1.0 - '@matrix-org/matrix-sdk-crypto-wasm@18.5.0': - resolution: {integrity: sha512-E826Hy1rG26LanPjtSsOiVRcVoHfSgPgj2r2Xsb5RPScpaKi9XJADQ0u3dNjRCitZPX4oyLNl1FDd5AfDlmIwQ==} + '@matrix-org/matrix-sdk-crypto-wasm@18.9.0': + resolution: {integrity: sha512-NtLzNDTEfNASBjNCTGZHuk7N0jrQhS6Zx2CM857rCHZYYh/rpbKGQNrJ7azktm4S1P5Jy3WU3nwJTGohGw26qQ==} engines: {node: '>= 18'} '@mdx-js/react@3.1.1': @@ -3659,8 +3659,8 @@ packages: constants-browserify@1.0.0: resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==} - content-type@3.0.0: - resolution: {integrity: sha512-AIi5H6p0xk5uknXcN3/rmhP8jgp69OfSe/JuKiQAFprJ7UGw7mwj7m4XcmDzlrnJDG+cGpphAINGdU3g3g7kDw==} + content-type@3.1.1: + resolution: {integrity: sha512-GW4qUsfFo59d0HbUibDlWv5wPz+vAAcaTWbKIuKCf0JkC7wWkSyf8f13IpXn5JkeMlB2P8iTSSIjwXljorg2vA==} engines: {node: '>=22'} convert-source-map@2.0.0: @@ -4680,9 +4680,9 @@ 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/24929be0e741be6a5d0a7226f1c682e245263b8a: - resolution: {gitHosted: true, integrity: sha512-0EGwbXzvG88sBOxq4ZlCRwT4VpQj2xkizfyy0A3bTSK0ysb6/gXJ/o1WPFoqCjRGMDgPq4gAsk92xGtHA7EhPg==, tarball: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/24929be0e741be6a5d0a7226f1c682e245263b8a} - version: 42.2.0 + matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/6defa47adae28feb7e3906dde25089e0dce42ece: + resolution: {gitHosted: true, integrity: sha512-jHFmI2C29HJnzs/FIrqdmQD6ytyunyy627onhrxI+xxTGSfYrWhcTUFstNiCAaMt3fc0nNi9aVvLPD3IIglF2A==, tarball: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/6defa47adae28feb7e3906dde25089e0dce42ece} + version: 42.3.0 engines: {node: '>=22.0.0'} matrix-widget-api@1.19.0: @@ -4895,8 +4895,8 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} - p-retry@8.0.0: - resolution: {integrity: sha512-kFVqH1HxOHp8LupNsOys7bSV09VYTRLxarH/mokO4Rqhk6wGi70E0jh4VzvVGXfEVNggHoHLAMWsQqHyU1Ey9A==} + p-retry@8.0.1: + resolution: {integrity: sha512-NAigyu8hfvJe7MsS18mnc4is0MZtau3QMdBklaJKK23oBQb6occO3UPM3vHmTckW8KhfyjMZaUOqdTFEYliHgg==} engines: {node: '>=22'} p-try@2.2.0: @@ -7101,7 +7101,7 @@ snapshots: '@types/dom-mediacapture-transform': 0.1.11 livekit-client: 2.22.3(@types/dom-mediacapture-record@1.0.22) - '@matrix-org/matrix-sdk-crypto-wasm@18.5.0': {} + '@matrix-org/matrix-sdk-crypto-wasm@18.9.0': {} '@mdx-js/react@3.1.1(@types/react@19.2.18)(react@19.2.8)': dependencies: @@ -9163,7 +9163,7 @@ snapshots: constants-browserify@1.0.0: {} - content-type@3.0.0: {} + content-type@3.1.1: {} convert-source-map@2.0.0: {} @@ -10249,17 +10249,17 @@ snapshots: matrix-events-sdk@0.0.1: {} - matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/24929be0e741be6a5d0a7226f1c682e245263b8a: + matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/6defa47adae28feb7e3906dde25089e0dce42ece: dependencies: '@babel/runtime': 8.0.0 - '@matrix-org/matrix-sdk-crypto-wasm': 18.5.0 + '@matrix-org/matrix-sdk-crypto-wasm': 18.9.0 another-json: 0.2.0 bs58: 6.0.0 - content-type: 3.0.0 + content-type: 3.1.1 loglevel: 1.9.2 matrix-events-sdk: 0.0.1 matrix-widget-api: 1.19.0 - p-retry: 8.0.0 + p-retry: 8.0.1 sdp-transform: 3.0.0 unhomoglyph: 1.0.6 @@ -10584,7 +10584,7 @@ snapshots: dependencies: p-limit: 3.1.0 - p-retry@8.0.0: + p-retry@8.0.1: dependencies: is-network-error: 1.3.2 diff --git a/scripts/check-component-externals.mjs b/scripts/check-component-externals.mjs index ee7d57da0..42d9235df 100644 --- a/scripts/check-component-externals.mjs +++ b/scripts/check-component-externals.mjs @@ -9,9 +9,11 @@ Please see LICENSE in the repository root for full details. * Checks that the component build leaves the packages a host must supply to * the host. * - * A host application already has React, the Matrix SDK and LiveKit, and a - * second copy of any of them is worse than dead weight: React would hold two - * sets of hooks, and the Matrix client would run two sync loops. So the + * A host application already has React, the Matrix SDK, LiveKit and Compound, + * and a second copy of any of them is worse than dead weight: React would hold + * two sets of hooks, the Matrix client would run two sync loops, and a second + * Compound would style the tooltips it floats into the host's body with class + * names the host's stylesheet does not know. So the * component build lists them as external — but that list has to name every * subpath, since the bundler silently ignores the pattern and callback forms * of the option, and an import it does not cover is bundled with no warning at @@ -33,6 +35,9 @@ import { loadConfigFromFile } from "vite"; const CONFIG = "vite-component.config.ts"; const SOURCES = ["src", "component"]; +// The development harness is a host, not part of the component: it is the +// one that imports what a host supplies (Compound's stylesheets, say). +const EXCLUDED = ["component/dev"]; /** The packages whose duplication would break a host, rather than merely enlarge it. */ const MUST_BE_EXTERNAL = [ @@ -40,6 +45,8 @@ const MUST_BE_EXTERNAL = [ "react-dom", "matrix-js-sdk", "livekit-client", + "@vector-im/compound-web", + "@vector-im/compound-design-tokens", ]; const isTestFile = (name) => @@ -49,6 +56,7 @@ const isTestFile = (name) => async function* sourceFiles(dir) { for (const entry of await readdir(dir, { withFileTypes: true })) { const path = join(dir, entry.name); + if (EXCLUDED.includes(path)) continue; if (entry.isDirectory()) yield* sourceFiles(path); else if (/\.(ts|tsx)$/.test(entry.name) && !isTestFile(entry.name)) yield path; diff --git a/src/base.css b/src/base.css index 51751eb79..b8eaf67e8 100644 --- a/src/base.css +++ b/src/base.css @@ -5,19 +5,24 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE in the repository root for full details. */ -/* The styles Element Call needs wherever it is shown: the design tokens, fonts -and element defaults its own stylesheets build on top of. +/* The styles Element Call needs wherever it is shown: the element defaults and +custom properties its own stylesheets build on top of. Split out from index.css so that Element Call as a component can have these without also being given the standalone page's layout, which would style the -host's own document. What remains here still speaks of the -document — normalize.css and the typography below use bare element selectors, -and the custom properties are declared on `:root` — which is right for the -page, and is why the component build rewrites it: there every selector is -confined to Element Call's root element (see component/build/scopeStylesToRoot.ts), -with `html`, `body` and `:root` becoming that element. Nothing needs to be -written differently here for that to work, but nothing here may rely on -reaching the host's document either. +host's own document. Compound's stylesheets — the design tokens, the component +styles and the fonts they use — are not here either: as a component, Element +Call shares the host's copy of Compound (it is a peer dependency, so that what +Compound floats into the host's body, tooltips and menus, is styled by the +host's stylesheet), and index.css loads it for the page. + +What remains here still speaks of the document — normalize.css and the +typography below use bare element selectors, and the custom properties are +declared on `:root` — which is right for the page, and is why the component +build rewrites it: there every selector is confined to Element Call's root +element (see component/build/scopeStylesToRoot.ts), with `html`, `body` and +`:root` becoming that element. Nothing needs to be written differently here for +that to work, but nothing here may rely on reaching the host's document either. Nothing here should depend on where it lands relative to Element Call's component stylesheets: the bundler decides that, and it decides differently for @@ -25,16 +30,7 @@ the app and for the component build. */ @layer normalize, compound-legacy, compound; -@import url("@fontsource/inter/400.css"); -@import url("@fontsource/inter/500.css"); -@import url("@fontsource/inter/600.css"); -@import url("@fontsource/inter/700.css"); -@import url("@fontsource/inconsolata/400.css"); -@import url("@fontsource/inconsolata/700.css"); - @import url("normalize.css/normalize.css") layer(normalize); -@import url("@vector-im/compound-design-tokens/assets/web/css/compound-design-tokens.css") layer(compound); -@import url("@vector-im/compound-web/dist/style.css") layer(compound.components); :root { --font-scale: 1; diff --git a/src/index.css b/src/index.css index 7188f5e79..c52ae9e43 100644 --- a/src/index.css +++ b/src/index.css @@ -7,10 +7,24 @@ Please see LICENSE in the repository root for full details. /* Styles for Element Call as a page of its own. The parts that apply wherever Element Call is shown live in base.css; these are about owning the document, -and are not loaded when a host embeds Element Call as a component. */ +and are not loaded when a host embeds Element Call as a component. + +That includes Compound: the page brings the design tokens, the component styles +and their fonts, where a host embedding the component supplies its own. The +`compound` layers are ordered by base.css. */ @import url("./base.css"); +@import url("@fontsource/inter/400.css"); +@import url("@fontsource/inter/500.css"); +@import url("@fontsource/inter/600.css"); +@import url("@fontsource/inter/700.css"); +@import url("@fontsource/inconsolata/400.css"); +@import url("@fontsource/inconsolata/700.css"); + +@import url("@vector-im/compound-design-tokens/assets/web/css/compound-design-tokens.css") layer(compound); +@import url("@vector-im/compound-web/dist/style.css") layer(compound.components); + body { background-color: var(--cpd-color-bg-canvas-default); color: var(--cpd-color-text-primary); diff --git a/src/state/CallViewModel/CallViewModel.ts b/src/state/CallViewModel/CallViewModel.ts index 1ab4ea134..ea7f3cc78 100644 --- a/src/state/CallViewModel/CallViewModel.ts +++ b/src/state/CallViewModel/CallViewModel.ts @@ -43,7 +43,7 @@ import { import { type Logger, logger as rootLogger } from "matrix-js-sdk/lib/logger"; import { MembershipManagerEvent, - type LivekitTransportConfig, + type LivekitTransport, type MatrixRTCSession, type RTCCallIntent, type RTCNotificationType, @@ -624,7 +624,7 @@ export function createCallViewModel$( ), muteStates, joinMatrixRTC: ( - transport: LivekitTransportConfig, + transport: LivekitTransport, delayedLeaveTimings: ResolvedDelayedLeaveTimings, ) => { return enterRTCSession( diff --git a/src/state/CallViewModel/localMember/LocalMember.test.ts b/src/state/CallViewModel/localMember/LocalMember.test.ts index 973c3f6c0..8e4217719 100644 --- a/src/state/CallViewModel/localMember/LocalMember.test.ts +++ b/src/state/CallViewModel/localMember/LocalMember.test.ts @@ -8,7 +8,7 @@ Please see LICENSE in the repository root for full details. import { Status as RTCMemberStatus, - type LivekitTransportConfig, + type LivekitTransport, type MatrixRTCSession, } from "matrix-js-sdk/lib/matrixrtc"; import { @@ -121,8 +121,7 @@ const mockedClient = { }; describe("enterRTCSession", () => { - const transport: LivekitTransportConfig = { - livekit_alias: "roomId", + const transport: LivekitTransport = { livekit_service_url: "http://my-livekit-service-url.com", type: "livekit", }; @@ -157,8 +156,7 @@ describe("enterRTCSession", () => { memberId: "@alice:example.org:DEVICE", userId: "@alice:example.org", }, - [], - transport, + [transport], expect.objectContaining({ manageMediaKeys: true }), ); }); @@ -178,7 +176,6 @@ describe("enterRTCSession", () => { expect(mockedSession.joinRTCSession).toHaveBeenLastCalledWith( expect.any(Object), - [], expect.any(Object), expect.objectContaining({ keyRotationParticipantLimit: 50, @@ -190,7 +187,6 @@ describe("enterRTCSession", () => { enterRTCSession(mockedSession, ownMemberMock, transport, options); expect(mockedSession.joinRTCSession).toHaveBeenLastCalledWith( - expect.anything(), expect.anything(), expect.anything(), expect.objectContaining({ @@ -256,7 +252,7 @@ describe("LocalMembership", () => { it("throws error on missing RTC config error", () => { withTestScheduler(({ scope, hot, expectObservable }) => { - const localTransport$ = scope.behavior( + const localTransport$ = scope.behavior( hot("1ms #", {}, new MatrixRTCTransportMissingError("domain.com")), null, ); @@ -321,7 +317,7 @@ describe("LocalMembership", () => { const mockTransportConfig = { livekit_service_url: "a", - } as LivekitTransportConfig; + } as LivekitTransport; const mockTransport = { transport: mockTransportConfig, diff --git a/src/state/CallViewModel/localMember/LocalMember.ts b/src/state/CallViewModel/localMember/LocalMember.ts index 27a631d98..36469e7ad 100644 --- a/src/state/CallViewModel/localMember/LocalMember.ts +++ b/src/state/CallViewModel/localMember/LocalMember.ts @@ -19,7 +19,6 @@ import { type MatrixClient } from "matrix-js-sdk"; import { Status as RTCSessionStatus, type LivekitTransport, - type LivekitTransportConfig, type MatrixRTCSession, type RTCCallIntent, type RTCNotificationType, @@ -144,7 +143,7 @@ interface Props { connectionManager: IConnectionManager; createPublisherFactory: (connection: Connection) => Publisher; joinMatrixRTC: ( - transport: LivekitTransportConfig, + transport: LivekitTransport, delayedLeaveTimings: ResolvedDelayedLeaveTimings, ) => void; homeserverConnected: HomeserverConnected; @@ -966,7 +965,7 @@ interface EnterRTCSessionOptions { export function enterRTCSession( rtcSession: MatrixRTCSession, ownMembershipIdentity: CallMembershipIdentityParts, - transport: LivekitTransportConfig, + transport: LivekitTransport, { encryptMedia, matrixRTCMode, @@ -988,18 +987,6 @@ export function enterRTCSession( } = Config.get(); const retryInterval = sessionConfig.network_error_retry_ms; - // For backwards compatibility with Element Call versions that do not do Matrix 2.0, - // we add the livekit alias to the transport. - let backwardCompatibleTransport: LivekitTransport | LivekitTransportConfig; - if (matrixRTCMode === MatrixRTCMode.Matrix_2_0) { - backwardCompatibleTransport = transport; - } else { - backwardCompatibleTransport = { - livekit_alias: rtcSession.room.roomId, - ...transport, - }; - } - // Set maximumNetworkErrorRetryCount such that we will consider the client // disconnected as soon as either it fails to sync for longer than the grace // period, or it is likely that a delayed leave event has been sent. @@ -1011,24 +998,19 @@ export function enterRTCSession( // Multi-sfu does not need a preferred foci list. just the focus that is actually used. // TODO where/how do we track errors originating from the ongoing rtcSession? - rtcSession.joinRTCSession( - ownMembershipIdentity, - [], - backwardCompatibleTransport, - { - notificationType, - callIntent, - manageMediaKeys: encryptMedia, - delayedLeaveEventRestartMs: delayedLeaveTimings.restart_ms, - delayedLeaveEventDelayMs: delayedLeaveTimings.delay_ms, - delayedLeaveEventRestartLocalTimeoutMs: - delayedLeaveTimings.restart_timeout_ms, - networkErrorRetryMs: sessionConfig.network_error_retry_ms, - makeKeyDelay: sessionConfig.wait_for_key_rotation_ms, - membershipEventExpiryMs: sessionConfig.membership_event_expiry_ms, - keyRotationParticipantLimit: sessionConfig.key_rotation_participant_limit, - unstableSendStickyEvents: matrixRTCMode === MatrixRTCMode.Matrix_2_0, - maximumNetworkErrorRetryCount: maximumNetworkErrorRetryCount, - }, - ); + rtcSession.joinRTCSession(ownMembershipIdentity, [transport], { + notificationType, + callIntent, + manageMediaKeys: encryptMedia, + delayedLeaveEventRestartMs: delayedLeaveTimings.restart_ms, + delayedLeaveEventDelayMs: delayedLeaveTimings.delay_ms, + delayedLeaveEventRestartLocalTimeoutMs: + delayedLeaveTimings.restart_timeout_ms, + networkErrorRetryMs: sessionConfig.network_error_retry_ms, + makeKeyDelay: sessionConfig.wait_for_key_rotation_ms, + membershipEventExpiryMs: sessionConfig.membership_event_expiry_ms, + keyRotationParticipantLimit: sessionConfig.key_rotation_participant_limit, + unstableSendStickyEvents: matrixRTCMode === MatrixRTCMode.Matrix_2_0, + maximumNetworkErrorRetryCount: maximumNetworkErrorRetryCount, + }); } diff --git a/src/state/CallViewModel/localMember/LocalTransport.ts b/src/state/CallViewModel/localMember/LocalTransport.ts index 0aa150d5d..3534b3fec 100644 --- a/src/state/CallViewModel/localMember/LocalTransport.ts +++ b/src/state/CallViewModel/localMember/LocalTransport.ts @@ -5,7 +5,7 @@ SPDX-License-IdFentifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE in the repository root for full details. */ -import { type LivekitTransportConfig } from "matrix-js-sdk/lib/matrixrtc"; +import { type LivekitTransport } from "matrix-js-sdk/lib/matrixrtc"; import { type MatrixClient } from "matrix-js-sdk"; import { logger as rootLogger, type Logger } from "matrix-js-sdk/lib/logger"; import { type CallMembershipIdentityParts } from "matrix-js-sdk/lib/matrixrtc/EncryptionManager"; @@ -34,34 +34,13 @@ interface Props { matrixRTCMode: MatrixRTCMode; } -// TODO livekit_alias-cleanup -// 1. We need to move away from transports map to connections!!! -// -// 2. We need to stop sending livekit_alias all together -// -// -// 1. -// Transports are just the jwt service adress but do not contain the information which room on this transport to use. -// That requires slot and roomId. -// -// We need one connection per room on the transport. -// -// We need an object that contains: -// transport -// roomId -// slotId -// -// To map to the connections. Prosposal: `ConnectionIdentifier` -// -// 2. -// We need to make sure we do not sent livekit_alias in sticky events and that we drop all code for sending state events! export interface LocalTransport { - transport: LivekitTransportConfig; + transport: LivekitTransport; sfuConfig: SFUConfig; } export function isLocalTransport( - obj: LivekitTransportConfig | LocalTransport, + obj: LivekitTransport | LocalTransport, ): obj is LocalTransport { return "transport" in obj && "sfuConfig" in obj; } @@ -90,7 +69,7 @@ export async function getLocalTransport({ const customUrl = customLivekitUrl.value$.value; // Respect the user's custom URL, if set - const transport: LivekitTransportConfig | null = customUrl + const transport: LivekitTransport | null = customUrl ? { type: "livekit", livekit_service_url: customUrl } : await discovery.discoverPreferredTransport(); @@ -131,7 +110,7 @@ export async function getLocalTransport({ * @throws FailToGetOpenIdToken, NoMatrix2AuthorizationService */ async function doOpenIdAndJWTFromUrl( - transport: LivekitTransportConfig, + transport: LivekitTransport, matrixRTCMode: MatrixRTCMode, membership: CallMembershipIdentityParts, roomId: string, diff --git a/src/state/CallViewModel/localMember/RtcTransportAutoDiscovery.test.ts b/src/state/CallViewModel/localMember/RtcTransportAutoDiscovery.test.ts index f81223deb..61df88a73 100644 --- a/src/state/CallViewModel/localMember/RtcTransportAutoDiscovery.test.ts +++ b/src/state/CallViewModel/localMember/RtcTransportAutoDiscovery.test.ts @@ -16,7 +16,7 @@ import { import { MatrixError } from "matrix-js-sdk"; import { logger as rootLogger } from "matrix-js-sdk/lib/logger"; import { - type LivekitTransportConfig, + type LivekitTransport, type Transport, } from "matrix-js-sdk/lib/matrixrtc"; @@ -28,12 +28,12 @@ import { type DiscoveryClient = RtcTransportAutoDiscoveryProps["client"]; -const backendTransport: LivekitTransportConfig = { +const backendTransport: LivekitTransport = { type: "livekit", livekit_service_url: "https://backend.example.org", }; -const configTransport: LivekitTransportConfig = { +const configTransport: LivekitTransport = { type: "livekit", livekit_service_url: "https://config.example.org", }; diff --git a/src/state/CallViewModel/localMember/RtcTransportAutoDiscovery.ts b/src/state/CallViewModel/localMember/RtcTransportAutoDiscovery.ts index 1f295649f..aeb6b051a 100644 --- a/src/state/CallViewModel/localMember/RtcTransportAutoDiscovery.ts +++ b/src/state/CallViewModel/localMember/RtcTransportAutoDiscovery.ts @@ -5,8 +5,8 @@ SPDX-License-IdFentifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE in the repository root for full details. */ import { - isLivekitTransportConfig, - type LivekitTransportConfig, + isLivekitTransport, + type LivekitTransport, } from "matrix-js-sdk/lib/matrixrtc"; import { type MatrixClient } from "matrix-js-sdk"; import { type Logger } from "matrix-js-sdk/lib/logger"; @@ -40,7 +40,7 @@ export class RtcTransportAutoDiscovery { this.logger = logger.getChild("[RtcTransportAutoDiscovery]"); } - public async discoverPreferredTransport(): Promise { + public async discoverPreferredTransport(): Promise { // 1) backend transports const backendTransport = await this.tryBackendTransports(); if (backendTransport) { @@ -67,7 +67,7 @@ export class RtcTransportAutoDiscovery { * This will not throw errors, but instead just log them and return null if the expected config is not found or malformed. * @private */ - private async tryBackendTransports(): Promise { + private async tryBackendTransports(): Promise { const client = this.client; // MSC4143: Attempt to fetch transports from backend. this.logger.info("First try to use getRTCTransports end point ..."); @@ -75,7 +75,7 @@ export class RtcTransportAutoDiscovery { const transportList = await doNetworkOperationWithRetry(async () => client._unstable_getRTCTransports(), ); - const first = transportList.find(isLivekitTransportConfig); + const first = transportList.find(isLivekitTransport); if (first) { return first; } else { @@ -90,7 +90,7 @@ export class RtcTransportAutoDiscovery { return null; } - private tryConfigTransport(): LivekitTransportConfig | null { + private tryConfigTransport(): LivekitTransport | null { const url = this.resolvedConfig.livekit?.livekit_service_url; if (url) { return { diff --git a/src/state/CallViewModel/remoteMembers/Connection.test.ts b/src/state/CallViewModel/remoteMembers/Connection.test.ts index f226b7789..62dc17a31 100644 --- a/src/state/CallViewModel/remoteMembers/Connection.test.ts +++ b/src/state/CallViewModel/remoteMembers/Connection.test.ts @@ -30,7 +30,7 @@ import fetchMock from "fetch-mock"; import EventEmitter from "events"; import { type IOpenIDToken } from "matrix-js-sdk"; import { logger, type Logger } from "matrix-js-sdk/lib/logger"; -import { type LivekitTransportConfig } from "matrix-js-sdk/lib/matrixrtc"; +import { type LivekitTransport } from "matrix-js-sdk/lib/matrixrtc"; import { Connection, @@ -57,7 +57,7 @@ let fakeLocalParticipant: MockedObject; const ROOM_ID = "!roomID:example.org"; -const livekitFocus: LivekitTransportConfig = { +const livekitFocus: LivekitTransport = { livekit_service_url: "https://matrix-rtc.example.org/livekit/jwt", type: "livekit", }; @@ -228,9 +228,7 @@ describe("Start connection states", () => { capturedState = capturedStates.pop(); if (capturedState instanceof Error) { expect(capturedState.message).toEqual("Something went wrong"); - expect(connection.transport.livekit_alias).toEqual( - livekitFocus.livekit_alias, - ); + expect(connection.transport).toEqual(livekitFocus); } else { expect.fail("Expected FailedToStart state but got " + capturedState); } @@ -290,9 +288,7 @@ describe("Start connection states", () => { expect(capturedState.cause.message).toContain( "Failed to look up user info from homeserver", ); - expect(connection.transport.livekit_alias).toEqual( - livekitFocus.livekit_alias, - ); + expect(connection.transport).toEqual(livekitFocus); } else { expect.fail("Expected FailedToStart state but got " + capturedState); } @@ -357,9 +353,7 @@ describe("Start connection states", () => { expect(capturedState.cause.message).toContain( "Failed to connect to livekit", ); - expect(connection.transport.livekit_alias).toEqual( - livekitFocus.livekit_alias, - ); + expect(connection.transport).toEqual(livekitFocus); } else { expect.fail( "Expected FailedToStart state but got " + JSON.stringify(capturedState), diff --git a/src/state/CallViewModel/remoteMembers/Connection.ts b/src/state/CallViewModel/remoteMembers/Connection.ts index 7889f9004..cc84142d9 100644 --- a/src/state/CallViewModel/remoteMembers/Connection.ts +++ b/src/state/CallViewModel/remoteMembers/Connection.ts @@ -22,7 +22,7 @@ import { type Track, type TrackPublication, } from "livekit-client"; -import { type LivekitTransportConfig } from "matrix-js-sdk/lib/matrixrtc"; +import { type LivekitTransport } from "matrix-js-sdk/lib/matrixrtc"; import { BehaviorSubject, map } from "rxjs"; import { type Logger } from "matrix-js-sdk/lib/logger"; import { type CallMembershipIdentityParts } from "matrix-js-sdk/lib/matrixrtc/EncryptionManager"; @@ -52,7 +52,7 @@ export interface ConnectionOpts { /** The identity parts to use on this connection */ ownMembershipIdentity: CallMembershipIdentityParts; /** The media transport to connect to. */ - transport: LivekitTransportConfig; + transport: LivekitTransport; /** The Matrix client to use for OpenID and SFU config requests. */ client: OpenIDClientParts; /** The room ID this connection is associated with. */ @@ -107,7 +107,7 @@ export class Connection { /** * The media transport to connect to. */ - public readonly transport: LivekitTransportConfig; + public readonly transport: LivekitTransport; public readonly livekitRoom: LivekitRoom; diff --git a/src/state/CallViewModel/remoteMembers/ConnectionFactory.ts b/src/state/CallViewModel/remoteMembers/ConnectionFactory.ts index 30ff37f3e..96a28d5ad 100644 --- a/src/state/CallViewModel/remoteMembers/ConnectionFactory.ts +++ b/src/state/CallViewModel/remoteMembers/ConnectionFactory.ts @@ -16,7 +16,7 @@ import { logger, type Logger } from "matrix-js-sdk/lib/logger"; // imported as inline to support worker when loaded from a cdn (cross domain) import E2EEWorker from "livekit-client/e2ee-worker?worker&inline"; import { type CallMembershipIdentityParts } from "matrix-js-sdk/lib/matrixrtc/EncryptionManager"; -import { type LivekitTransportConfig } from "matrix-js-sdk/lib/matrixrtc"; +import { type LivekitTransport } from "matrix-js-sdk/lib/matrixrtc"; import { type ObservableScope } from "../../ObservableScope.ts"; import { Connection } from "./Connection.ts"; @@ -44,7 +44,7 @@ import { export interface ConnectionFactory { createConnection( scope: ObservableScope, - transport: LivekitTransportConfig, + transport: LivekitTransport, ownMembershipIdentity: CallMembershipIdentityParts, logger: Logger, sfuConfig?: SFUConfig, @@ -103,7 +103,7 @@ export class ECConnectionFactory implements ConnectionFactory { */ public createConnection( scope: ObservableScope, - transport: LivekitTransportConfig, + transport: LivekitTransport, ownMembershipIdentity: CallMembershipIdentityParts, logger: Logger, sfuConfig?: SFUConfig, diff --git a/src/state/CallViewModel/remoteMembers/ConnectionManager.test.ts b/src/state/CallViewModel/remoteMembers/ConnectionManager.test.ts index 0903702ff..e7d91c633 100644 --- a/src/state/CallViewModel/remoteMembers/ConnectionManager.test.ts +++ b/src/state/CallViewModel/remoteMembers/ConnectionManager.test.ts @@ -7,7 +7,7 @@ Please see LICENSE in the repository root for full details. import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"; import { BehaviorSubject, NEVER } from "rxjs"; -import { type LivekitTransportConfig } from "matrix-js-sdk/lib/matrixrtc"; +import { type LivekitTransport } from "matrix-js-sdk/lib/matrixrtc"; import { type RemoteParticipant } from "livekit-client"; import { logger } from "matrix-js-sdk/lib/logger"; @@ -24,12 +24,12 @@ import { type Behavior } from "../../Behavior.ts"; // Some test constants -const TRANSPORT_1: LivekitTransportConfig = { +const TRANSPORT_1: LivekitTransport = { type: "livekit", livekit_service_url: "https://lk.example.org", }; -const TRANSPORT_2: LivekitTransportConfig = { +const TRANSPORT_2: LivekitTransport = { type: "livekit", livekit_service_url: "https://lk.sample.com", }; @@ -47,7 +47,7 @@ beforeEach(() => { vi.mocked(fakeConnectionFactory).createConnection = vi .fn() .mockImplementation( - (scope: ObservableScope, transport: LivekitTransportConfig) => { + (scope: ObservableScope, transport: LivekitTransport) => { const mockConnection = { transport, remoteParticipants$: new BehaviorSubject([]), @@ -225,7 +225,7 @@ describe("connectionManagerData$ stream", () => { // Used in test to control fake connections' remoteParticipants$ streams let fakeRemoteParticipantsStreams: Map>; - function keyForTransport(transport: LivekitTransportConfig): string { + function keyForTransport(transport: LivekitTransport): string { return `${transport.livekit_service_url}`; } @@ -233,7 +233,7 @@ describe("connectionManagerData$ stream", () => { fakeRemoteParticipantsStreams = new Map(); function getRemoteParticipantsFor( - transport: LivekitTransportConfig, + transport: LivekitTransport, ): Behavior { return ( fakeRemoteParticipantsStreams.get(keyForTransport(transport)) ?? @@ -245,7 +245,7 @@ describe("connectionManagerData$ stream", () => { vi.mocked(fakeConnectionFactory).createConnection = vi .fn() .mockImplementation( - (scope: ObservableScope, transport: LivekitTransportConfig) => { + (scope: ObservableScope, transport: LivekitTransport) => { const fakeRemoteParticipants$ = new BehaviorSubject< RemoteParticipant[] >([]); diff --git a/src/state/CallViewModel/remoteMembers/ConnectionManager.ts b/src/state/CallViewModel/remoteMembers/ConnectionManager.ts index ea51c7ba0..18f54f021 100644 --- a/src/state/CallViewModel/remoteMembers/ConnectionManager.ts +++ b/src/state/CallViewModel/remoteMembers/ConnectionManager.ts @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE in the repository root for full details. */ -import { type LivekitTransportConfig } from "matrix-js-sdk/lib/matrixrtc"; +import { type LivekitTransport } from "matrix-js-sdk/lib/matrixrtc"; import { combineLatest, map, @@ -56,7 +56,7 @@ export class ConnectionManagerData { } } - private getKey(transport: LivekitTransportConfig): string { + private getKey(transport: LivekitTransport): string { // This is enough as a key because the ConnectionManager is already scoped by room. // We also do not need to consider the slotId at this point since each `MatrixRTCSession` is already scoped by `slotDescription: {id, application}`. return transport.livekit_service_url; @@ -67,13 +67,13 @@ export class ConnectionManagerData { } public getConnectionForTransport( - transport: LivekitTransportConfig, + transport: LivekitTransport, ): Connection | null { return this.store.get(this.getKey(transport))?.connection ?? null; } public getParticipantsForTransport( - transport: LivekitTransportConfig, + transport: LivekitTransport, ): RemoteParticipant[] { const key = this.getKey(transport); const existing = this.store.get(key); @@ -88,7 +88,7 @@ interface Props { scope: ObservableScope; connectionFactory: ConnectionFactory; localTransport$: Observable; - remoteTransports$: Behavior>; + remoteTransports$: Behavior>; logger: Logger; ownMembershipIdentity: CallMembershipIdentityParts; @@ -147,7 +147,7 @@ export function createConnectionManager$({ * externally this is modified via `registerTransports()`. */ const localAndRemoteTransports$: Behavior< - Epoch<(LivekitTransportConfig | LocalTransport)[]> + Epoch<(LivekitTransport | LocalTransport)[]> > = scope.behavior( combineLatest([localTransportAsArray$, remoteTransports$]).pipe( // Combine local and remote transports into one transport array @@ -271,7 +271,7 @@ export function createConnectionManager$({ return { connectionManagerData$ }; } -function removeDuplicateTransports( +function removeDuplicateTransports( transports: T[], ): T[] { return transports.reduce((acc, transport) => { diff --git a/src/state/CallViewModel/remoteMembers/MatrixLivekitMembers.test.ts b/src/state/CallViewModel/remoteMembers/MatrixLivekitMembers.test.ts index fe7621121..2e0051928 100644 --- a/src/state/CallViewModel/remoteMembers/MatrixLivekitMembers.test.ts +++ b/src/state/CallViewModel/remoteMembers/MatrixLivekitMembers.test.ts @@ -42,13 +42,11 @@ const fallbackMemberId = (userId: string, deviceId: string): string => const transportA: LivekitTransport = { type: "livekit", livekit_service_url: "https://lk.example.org", - livekit_alias: "!alias:example.org", }; const transportB: LivekitTransport = { type: "livekit", livekit_service_url: "https://lk.sample.com", - livekit_alias: "!alias:sample.com", }; const bobMembership = mockRtcMembership("@bob:example.org", "DEV000", { @@ -126,7 +124,7 @@ function createEpochedMemberships$(m$: Observable): { const membershipsWithTransport$ = memberships$.pipe( mapEpoch((members) => { return members.map((m) => { - const tr = m.getTransport(m); + const tr = m.getTransport(); return { membership: m, transport: @@ -152,7 +150,7 @@ test("should signal participant on a connection that is publishing", async () => ); const connection = { - transport: bobMembership.getTransport(bobMembership), + transport: bobMembership.getTransport(), } as unknown as Connection; const dataWithPublisher = new ConnectionManagerData(); dataWithPublisher.add(connection, [ @@ -195,7 +193,7 @@ test("should signal participant on a connection that is not publishing", async ( ); const connection = { - transport: bobMembership.getTransport(bobMembership), + transport: bobMembership.getTransport(), } as unknown as Connection; const dataWithPublisher = new ConnectionManagerData(); dataWithPublisher.add(connection, []); diff --git a/src/state/CallViewModel/remoteMembers/MatrixLivekitMembers.ts b/src/state/CallViewModel/remoteMembers/MatrixLivekitMembers.ts index 76f4670ec..6c9fff026 100644 --- a/src/state/CallViewModel/remoteMembers/MatrixLivekitMembers.ts +++ b/src/state/CallViewModel/remoteMembers/MatrixLivekitMembers.ts @@ -8,7 +8,7 @@ Please see LICENSE in the repository root for full details. import { type LocalParticipant, type RemoteParticipant } from "livekit-client"; import { type CallMembership, - type LivekitTransportConfig, + type LivekitTransport, } from "matrix-js-sdk/lib/matrixrtc"; import { combineLatest, filter, map } from "rxjs"; import { logger } from "matrix-js-sdk/lib/logger"; @@ -60,7 +60,7 @@ export interface RemoteMatrixLivekitMember extends MatrixLivekitMember { interface Props { scope: ObservableScope; membershipsWithTransport$: Behavior< - Epoch<{ membership: CallMembership; transport?: LivekitTransportConfig }[]> + Epoch<{ membership: CallMembership; transport?: LivekitTransport }[]> >; connectionManager: IConnectionManager; localUser: { deviceId: string; userId: string }; @@ -165,7 +165,7 @@ export function createRemoteMatrixLivekitMembers$({ // TODO add back in the callviewmodel pauseWhen(this.pretendToBeDisconnected$) // TODO add this to the JS-SDK -export function areLivekitTransportsEqual( +export function areLivekitTransportsEqual( t1: T | null, t2: T | null, ): boolean { diff --git a/src/state/CallViewModel/remoteMembers/integration.test.ts b/src/state/CallViewModel/remoteMembers/integration.test.ts index 0db25c3b5..4c0f4fea7 100644 --- a/src/state/CallViewModel/remoteMembers/integration.test.ts +++ b/src/state/CallViewModel/remoteMembers/integration.test.ts @@ -10,7 +10,7 @@ import { BehaviorSubject, NEVER } from "rxjs"; import { type Room as LivekitRoom } from "livekit-client"; import EventEmitter from "events"; import fetchMock from "fetch-mock"; -import { type LivekitTransportConfig } from "matrix-js-sdk/lib/matrixrtc"; +import { type LivekitTransport } from "matrix-js-sdk/lib/matrixrtc"; import { logger } from "matrix-js-sdk/lib/logger"; import { @@ -149,7 +149,7 @@ test("bob, carl, then bob joining no tracks yet", () => { a: expect.toSatisfy((co) => areLivekitTransportsEqual( co.transport, - bobMembership.transports[0]! as LivekitTransportConfig, + bobMembership.transports[0]! as LivekitTransport, ), ), }); @@ -186,7 +186,7 @@ test("bob, carl, then bob joining no tracks yet", () => { expect( areLivekitTransportsEqual( connection.transport, - carlMembership.transports[0]! as LivekitTransportConfig, + carlMembership.transports[0]! as LivekitTransport, ), ).toBe(true); return true; @@ -216,7 +216,7 @@ test("bob, carl, then bob joining no tracks yet", () => { expect( areLivekitTransportsEqual( connection.transport, - daveMembership.transports[0]! as LivekitTransportConfig, + daveMembership.transports[0]! as LivekitTransport, ), ).toBe(true); return true; diff --git a/src/state/SessionBehaviors.ts b/src/state/SessionBehaviors.ts index 8d3986b68..4ca1c5861 100644 --- a/src/state/SessionBehaviors.ts +++ b/src/state/SessionBehaviors.ts @@ -7,10 +7,10 @@ Please see LICENSE in the repository root for full details. import { type CallMembership, - type LivekitTransportConfig, + type LivekitTransport, type MatrixRTCSession, MatrixRTCSessionEvent, - isLivekitTransportConfig, + isLivekitTransport, } from "matrix-js-sdk/lib/matrixrtc"; import { fromEvent } from "rxjs"; @@ -31,35 +31,32 @@ export const membershipsAndTransports$ = ( memberships$: Behavior>, ): { membershipsWithTransport$: Behavior< - Epoch<{ membership: CallMembership; transport?: LivekitTransportConfig }[]> + Epoch<{ membership: CallMembership; transport?: LivekitTransport }[]> >; - transports$: Behavior>; + transports$: Behavior>; } => { const membershipsWithTransport$: Behavior< Epoch< { membership: CallMembership; - transport: LivekitTransportConfig | undefined; + transport: LivekitTransport | undefined; }[] > > = scope.behavior( memberships$.pipe( mapEpoch((memberships) => { return memberships.map((membership) => { - const oldestMembership = memberships[0] ?? membership; - const transport = membership.getTransport(oldestMembership); + const transport = membership.getTransport(); return { membership, - transport: isLivekitTransportConfig(transport) - ? transport - : undefined, + transport: isLivekitTransport(transport) ? transport : undefined, }; }); }), ), ); - const transports$: Behavior> = scope.behavior( + const transports$: Behavior> = scope.behavior( membershipsWithTransport$.pipe( mapEpoch((mts) => mts.flatMap(({ transport: t }) => (t ? [t] : []))), ), diff --git a/src/utils/test.ts b/src/utils/test.ts index 405e5947b..1c49c0da4 100644 --- a/src/utils/test.ts +++ b/src/utils/test.ts @@ -212,7 +212,6 @@ export function mockEmitter(): EmitterMock { export const exampleTransport: LivekitTransport = { type: "livekit", livekit_service_url: "https://lk.example.org", - livekit_alias: "!alias:example.org", }; export const exampleSfuConfig: SFUConfig = { diff --git a/vite-component.config.ts b/vite-component.config.ts index 09baac23c..93e661acd 100644 --- a/vite-component.config.ts +++ b/vite-component.config.ts @@ -65,8 +65,12 @@ export default defineConfig(({ mode }) => { }, rollupOptions: { // The host already has these, and a second copy of any of them does not - // merely bloat the bundle: React would hold two sets of hooks, and the - // Matrix client would run two sync loops. + // merely bloat the bundle: React would hold two sets of hooks, the + // Matrix client would run two sync loops, and a second Compound would + // bring a second stylesheet with class names of its own — so that what + // it floats into the host's body, tooltips and menus, would match the + // host's Compound stylesheet only when the two happened to be the same + // version. // // Every subpath has to be named. Element Call reaches most of the Matrix // SDK as `matrix-js-sdk/lib/…`, and a bare "matrix-js-sdk" would not @@ -103,6 +107,8 @@ export default defineConfig(({ mode }) => { "matrix-js-sdk/lib/sync", "matrix-js-sdk/lib/types", "matrix-js-sdk/lib/utils", + "@vector-im/compound-web", + "@vector-im/compound-design-tokens/assets/web/icons", ], }, }, diff --git a/vitest.config.ts b/vitest.config.ts index d6f67f89a..0373c4af7 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -25,6 +25,7 @@ export default defineConfig((configEnv) => "src/**/*.test.ts", "src/**/*.test.tsx", "component/**/*.test.ts", + "component/**/*.test.tsx", ], }, },