mirror of
https://github.com/vector-im/element-call.git
synced 2026-10-10 23:25:52 +00:00
Merge branch 'main' into toger5/fix-component-sounds
This commit is contained in:
@@ -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
|
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
|
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
|
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
|
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
|
(`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
|
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
|
root. The host imports the component from
|
||||||
`@element-hq/element-call-component` and the stylesheet from
|
`@element-hq/element-call-component` and the stylesheet from
|
||||||
`@element-hq/element-call-component/style.css`, and has to provide `react`,
|
`@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
|
`react-dom`, `matrix-js-sdk`, `livekit-client`, `@vector-im/compound-web` and
|
||||||
them external.
|
`@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
|
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
|
call is shown. Everything a host needs in order to talk about a call before
|
||||||
|
|||||||
@@ -34,10 +34,18 @@ component rather than a page makes. */
|
|||||||
-webkit-tap-highlight-color: transparent;
|
-webkit-tap-highlight-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Compound's overlay container, which holds tooltips and popovers, has to fill
|
/* The call itself. It is a stacking context of its own, as the standalone
|
||||||
the container for the elements inside it to be positioned against it. The
|
page's `#root` is, so that the layers the call view uses for its header, footer
|
||||||
standalone page does the same for the container under `#root`. */
|
and overlays stay below the modals and toasts, which are portalled into the
|
||||||
.root > [data-overlay-container] {
|
root and carry no layer of their own.
|
||||||
position: relative;
|
|
||||||
|
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%;
|
block-size: 100%;
|
||||||
|
position: relative;
|
||||||
|
isolation: isolate;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,10 +40,10 @@ const IS_ROOT = `:where(${ROOT_SELECTOR})`;
|
|||||||
* Element Call as a component.
|
* Element Call as a component.
|
||||||
*
|
*
|
||||||
* As a page of its own, Element Call can style the document: normalize.css and
|
* 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
|
* its own base styles speak of `html`, `body` and bare elements, and its custom
|
||||||
* declared on `:root`. As a component, all of that would land on the host's
|
* properties are declared on `:root`. As a component, all of that would land on
|
||||||
* document too. This rewrites every selector so that it matches only the root
|
* the host's document too. This rewrites every selector so that it matches only
|
||||||
* or its descendants:
|
* the root or its descendants:
|
||||||
*
|
*
|
||||||
* - `html`, `body` and `:root` become the root element, which is what stands in
|
* - `html`, `body` and `:root` become the root element, which is what stands in
|
||||||
* for the document inside a host.
|
* 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
|
* 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.
|
* 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
|
* The root's custom properties are still inherited by everything inside it,
|
||||||
* it, the way they were from `body` and `:root`, and `@font-face` declarations
|
* the way they were from `:root`. Compound's own stylesheet — the design
|
||||||
* stay global, which they are by nature.
|
* 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 {
|
export function scopeStylesToRoot(): Plugin {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -9,6 +9,16 @@ import { StrictMode } from "react";
|
|||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
import { logger } from "matrix-js-sdk/lib/logger";
|
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 { type ConfigOptions, initializeElementCall } from "../index";
|
||||||
import { Harness } from "./Harness";
|
import { Harness } from "./Harness";
|
||||||
// After Element Call's, so that the host has the last word on its own page
|
// After Element Call's, so that the host has the last word on its own page
|
||||||
|
|||||||
+13
-3
@@ -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.
|
* 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
|
// builds on. Written for a page, they speak of `html`, `body` and bare
|
||||||
// elements; the component build confines them, and every other stylesheet in
|
// elements; the component build confines them, and every other stylesheet in
|
||||||
// this bundle, to the root element below (see build/scopeStylesToRoot.ts), so
|
// this bundle, to the root element below (see build/scopeStylesToRoot.ts), so
|
||||||
// that the host's document is left as it was.
|
// 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
|
// 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
|
// 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
|
// 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
|
// component's own rules at equal specificity. It currently does not: what it
|
||||||
// declares unlayered is custom properties on Element Call's root, which
|
// declares unlayered is custom properties on Element Call's root, which
|
||||||
// components inherit rather than compete with, and everything from Compound
|
// components inherit rather than compete with.
|
||||||
// sits in a `@layer`, which loses to unlayered rules either way.
|
|
||||||
import "../src/base.css";
|
import "../src/base.css";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -231,6 +237,9 @@ export const ElementCall: FC<ElementCallProps> = ({
|
|||||||
<UrlParamsProvider value={params}>
|
<UrlParamsProvider value={params}>
|
||||||
<div ref={setContainer} className={styles.root}>
|
<div ref={setContainer} className={styles.root}>
|
||||||
{ready && (
|
{ready && (
|
||||||
|
// Stands in for the standalone page's `#root`: see `.content`
|
||||||
|
// in ElementCall.module.css
|
||||||
|
<div className={styles.content}>
|
||||||
<RootElementProvider value={container}>
|
<RootElementProvider value={container}>
|
||||||
{/* Whatever goes wrong in here is shown in here. Left to
|
{/* Whatever goes wrong in here is shown in here. Left to
|
||||||
propagate, an error would unmount the host's own tree. */}
|
propagate, an error would unmount the host's own tree. */}
|
||||||
@@ -259,6 +268,7 @@ export const ElementCall: FC<ElementCallProps> = ({
|
|||||||
</Decoration>
|
</Decoration>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</RootElementProvider>
|
</RootElementProvider>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</UrlParamsProvider>
|
</UrlParamsProvider>
|
||||||
|
|||||||
@@ -38,6 +38,8 @@
|
|||||||
"prepare": "cd .. && pnpm install --frozen-lockfile && pnpm build:component"
|
"prepare": "cd .. && pnpm install --frozen-lockfile && pnpm build:component"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
"@vector-im/compound-design-tokens": "^11.0.0",
|
||||||
|
"@vector-im/compound-web": "^10.0.0",
|
||||||
"livekit-client": "^2.18.1",
|
"livekit-client": "^2.18.1",
|
||||||
"matrix-js-sdk": "*",
|
"matrix-js-sdk": "*",
|
||||||
"react": "^19",
|
"react": "^19",
|
||||||
|
|||||||
@@ -76,7 +76,10 @@ Code that builds in only one is a bug.
|
|||||||
- `build:component` — `@element-hq/element-call-component`, sources in `component/`
|
- `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;
|
(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` /
|
`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
|
points: `index.tsx` (the component) and `api.ts` (types, enums and
|
||||||
`configurationForIntent`, for a host that must not load the component). `api.ts`
|
`configurationForIntent`, for a host that must not load the component). `api.ts`
|
||||||
may only reach types and constants — `src/UrlConfiguration.ts`, not `UrlParams`.
|
may only reach types and constants — `src/UrlConfiguration.ts`, not `UrlParams`.
|
||||||
|
|||||||
@@ -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
|
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
|
token from the Matrix 2.0 JWT endpoint of the
|
||||||
[MatrixRTC Authorization Service](https://github.com/element-hq/lk-jwt-service)
|
[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
|
(Remote memberships always try the new endpoint first and fall back to the
|
||||||
legacy one, so remote participants can be on either.)
|
legacy one, so remote participants can be on either.)
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -103,7 +103,7 @@
|
|||||||
"livekit-client": "^2.18.1",
|
"livekit-client": "^2.18.1",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"loglevel": "^1.9.1",
|
"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",
|
"matrix-widget-api": "^1.18.0",
|
||||||
"node-stdlib-browser": "^1.3.1",
|
"node-stdlib-browser": "^1.3.1",
|
||||||
"normalize.css": "^8.0.1",
|
"normalize.css": "^8.0.1",
|
||||||
|
|||||||
Generated
+18
-18
@@ -191,8 +191,8 @@ importers:
|
|||||||
specifier: ^1.9.1
|
specifier: ^1.9.1
|
||||||
version: 1.9.2
|
version: 1.9.2
|
||||||
matrix-js-sdk:
|
matrix-js-sdk:
|
||||||
specifier: github:matrix-org/matrix-js-sdk#develop
|
specifier: github:matrix-org/matrix-js-sdk#robin/remove-oldest-membership
|
||||||
version: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/24929be0e741be6a5d0a7226f1c682e245263b8a
|
version: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/6defa47adae28feb7e3906dde25089e0dce42ece
|
||||||
matrix-widget-api:
|
matrix-widget-api:
|
||||||
specifier: ^1.18.0
|
specifier: ^1.18.0
|
||||||
version: 1.19.0
|
version: 1.19.0
|
||||||
@@ -1194,8 +1194,8 @@ packages:
|
|||||||
'@types/dom-mediacapture-transform': ^0.1.9
|
'@types/dom-mediacapture-transform': ^0.1.9
|
||||||
livekit-client: ^1.12.0 || ^2.1.0
|
livekit-client: ^1.12.0 || ^2.1.0
|
||||||
|
|
||||||
'@matrix-org/matrix-sdk-crypto-wasm@18.5.0':
|
'@matrix-org/matrix-sdk-crypto-wasm@18.9.0':
|
||||||
resolution: {integrity: sha512-E826Hy1rG26LanPjtSsOiVRcVoHfSgPgj2r2Xsb5RPScpaKi9XJADQ0u3dNjRCitZPX4oyLNl1FDd5AfDlmIwQ==}
|
resolution: {integrity: sha512-NtLzNDTEfNASBjNCTGZHuk7N0jrQhS6Zx2CM857rCHZYYh/rpbKGQNrJ7azktm4S1P5Jy3WU3nwJTGohGw26qQ==}
|
||||||
engines: {node: '>= 18'}
|
engines: {node: '>= 18'}
|
||||||
|
|
||||||
'@mdx-js/react@3.1.1':
|
'@mdx-js/react@3.1.1':
|
||||||
@@ -3659,8 +3659,8 @@ packages:
|
|||||||
constants-browserify@1.0.0:
|
constants-browserify@1.0.0:
|
||||||
resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==}
|
resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==}
|
||||||
|
|
||||||
content-type@3.0.0:
|
content-type@3.1.1:
|
||||||
resolution: {integrity: sha512-AIi5H6p0xk5uknXcN3/rmhP8jgp69OfSe/JuKiQAFprJ7UGw7mwj7m4XcmDzlrnJDG+cGpphAINGdU3g3g7kDw==}
|
resolution: {integrity: sha512-GW4qUsfFo59d0HbUibDlWv5wPz+vAAcaTWbKIuKCf0JkC7wWkSyf8f13IpXn5JkeMlB2P8iTSSIjwXljorg2vA==}
|
||||||
engines: {node: '>=22'}
|
engines: {node: '>=22'}
|
||||||
|
|
||||||
convert-source-map@2.0.0:
|
convert-source-map@2.0.0:
|
||||||
@@ -4680,9 +4680,9 @@ packages:
|
|||||||
matrix-events-sdk@0.0.1:
|
matrix-events-sdk@0.0.1:
|
||||||
resolution: {integrity: sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA==}
|
resolution: {integrity: sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA==}
|
||||||
|
|
||||||
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:
|
||||||
resolution: {gitHosted: true, integrity: sha512-0EGwbXzvG88sBOxq4ZlCRwT4VpQj2xkizfyy0A3bTSK0ysb6/gXJ/o1WPFoqCjRGMDgPq4gAsk92xGtHA7EhPg==, tarball: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/24929be0e741be6a5d0a7226f1c682e245263b8a}
|
resolution: {gitHosted: true, integrity: sha512-jHFmI2C29HJnzs/FIrqdmQD6ytyunyy627onhrxI+xxTGSfYrWhcTUFstNiCAaMt3fc0nNi9aVvLPD3IIglF2A==, tarball: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/6defa47adae28feb7e3906dde25089e0dce42ece}
|
||||||
version: 42.2.0
|
version: 42.3.0
|
||||||
engines: {node: '>=22.0.0'}
|
engines: {node: '>=22.0.0'}
|
||||||
|
|
||||||
matrix-widget-api@1.19.0:
|
matrix-widget-api@1.19.0:
|
||||||
@@ -4895,8 +4895,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
|
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
p-retry@8.0.0:
|
p-retry@8.0.1:
|
||||||
resolution: {integrity: sha512-kFVqH1HxOHp8LupNsOys7bSV09VYTRLxarH/mokO4Rqhk6wGi70E0jh4VzvVGXfEVNggHoHLAMWsQqHyU1Ey9A==}
|
resolution: {integrity: sha512-NAigyu8hfvJe7MsS18mnc4is0MZtau3QMdBklaJKK23oBQb6occO3UPM3vHmTckW8KhfyjMZaUOqdTFEYliHgg==}
|
||||||
engines: {node: '>=22'}
|
engines: {node: '>=22'}
|
||||||
|
|
||||||
p-try@2.2.0:
|
p-try@2.2.0:
|
||||||
@@ -7101,7 +7101,7 @@ snapshots:
|
|||||||
'@types/dom-mediacapture-transform': 0.1.11
|
'@types/dom-mediacapture-transform': 0.1.11
|
||||||
livekit-client: 2.22.3(@types/dom-mediacapture-record@1.0.22)
|
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)':
|
'@mdx-js/react@3.1.1(@types/react@19.2.18)(react@19.2.8)':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -9163,7 +9163,7 @@ snapshots:
|
|||||||
|
|
||||||
constants-browserify@1.0.0: {}
|
constants-browserify@1.0.0: {}
|
||||||
|
|
||||||
content-type@3.0.0: {}
|
content-type@3.1.1: {}
|
||||||
|
|
||||||
convert-source-map@2.0.0: {}
|
convert-source-map@2.0.0: {}
|
||||||
|
|
||||||
@@ -10249,17 +10249,17 @@ snapshots:
|
|||||||
|
|
||||||
matrix-events-sdk@0.0.1: {}
|
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:
|
dependencies:
|
||||||
'@babel/runtime': 8.0.0
|
'@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
|
another-json: 0.2.0
|
||||||
bs58: 6.0.0
|
bs58: 6.0.0
|
||||||
content-type: 3.0.0
|
content-type: 3.1.1
|
||||||
loglevel: 1.9.2
|
loglevel: 1.9.2
|
||||||
matrix-events-sdk: 0.0.1
|
matrix-events-sdk: 0.0.1
|
||||||
matrix-widget-api: 1.19.0
|
matrix-widget-api: 1.19.0
|
||||||
p-retry: 8.0.0
|
p-retry: 8.0.1
|
||||||
sdp-transform: 3.0.0
|
sdp-transform: 3.0.0
|
||||||
unhomoglyph: 1.0.6
|
unhomoglyph: 1.0.6
|
||||||
|
|
||||||
@@ -10584,7 +10584,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
p-limit: 3.1.0
|
p-limit: 3.1.0
|
||||||
|
|
||||||
p-retry@8.0.0:
|
p-retry@8.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-network-error: 1.3.2
|
is-network-error: 1.3.2
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
* Checks that the component build leaves the packages a host must supply to
|
||||||
* the host.
|
* the host.
|
||||||
*
|
*
|
||||||
* A host application already has React, the Matrix SDK and LiveKit, and a
|
* A host application already has React, the Matrix SDK, LiveKit and Compound,
|
||||||
* second copy of any of them is worse than dead weight: React would hold two
|
* and a second copy of any of them is worse than dead weight: React would hold
|
||||||
* sets of hooks, and the Matrix client would run two sync loops. So the
|
* 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
|
* component build lists them as external — but that list has to name every
|
||||||
* subpath, since the bundler silently ignores the pattern and callback forms
|
* 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
|
* 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 CONFIG = "vite-component.config.ts";
|
||||||
const SOURCES = ["src", "component"];
|
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. */
|
/** The packages whose duplication would break a host, rather than merely enlarge it. */
|
||||||
const MUST_BE_EXTERNAL = [
|
const MUST_BE_EXTERNAL = [
|
||||||
@@ -40,6 +45,8 @@ const MUST_BE_EXTERNAL = [
|
|||||||
"react-dom",
|
"react-dom",
|
||||||
"matrix-js-sdk",
|
"matrix-js-sdk",
|
||||||
"livekit-client",
|
"livekit-client",
|
||||||
|
"@vector-im/compound-web",
|
||||||
|
"@vector-im/compound-design-tokens",
|
||||||
];
|
];
|
||||||
|
|
||||||
const isTestFile = (name) =>
|
const isTestFile = (name) =>
|
||||||
@@ -49,6 +56,7 @@ const isTestFile = (name) =>
|
|||||||
async function* sourceFiles(dir) {
|
async function* sourceFiles(dir) {
|
||||||
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
||||||
const path = join(dir, entry.name);
|
const path = join(dir, entry.name);
|
||||||
|
if (EXCLUDED.includes(path)) continue;
|
||||||
if (entry.isDirectory()) yield* sourceFiles(path);
|
if (entry.isDirectory()) yield* sourceFiles(path);
|
||||||
else if (/\.(ts|tsx)$/.test(entry.name) && !isTestFile(entry.name))
|
else if (/\.(ts|tsx)$/.test(entry.name) && !isTestFile(entry.name))
|
||||||
yield path;
|
yield path;
|
||||||
|
|||||||
+15
-19
@@ -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.
|
Please see LICENSE in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* The styles Element Call needs wherever it is shown: the design tokens, fonts
|
/* The styles Element Call needs wherever it is shown: the element defaults and
|
||||||
and element defaults its own stylesheets build on top of.
|
custom properties its own stylesheets build on top of.
|
||||||
|
|
||||||
Split out from index.css so that Element Call as a component can have these
|
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
|
without also being given the standalone page's layout, which would style the
|
||||||
host's own document. What remains here still speaks of the
|
host's own document. Compound's stylesheets — the design tokens, the component
|
||||||
document — normalize.css and the typography below use bare element selectors,
|
styles and the fonts they use — are not here either: as a component, Element
|
||||||
and the custom properties are declared on `:root` — which is right for the
|
Call shares the host's copy of Compound (it is a peer dependency, so that what
|
||||||
page, and is why the component build rewrites it: there every selector is
|
Compound floats into the host's body, tooltips and menus, is styled by the
|
||||||
confined to Element Call's root element (see component/build/scopeStylesToRoot.ts),
|
host's stylesheet), and index.css loads it for the page.
|
||||||
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
|
What remains here still speaks of the document — normalize.css and the
|
||||||
reaching the host's document either.
|
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
|
Nothing here should depend on where it lands relative to Element Call's
|
||||||
component stylesheets: the bundler decides that, and it decides differently for
|
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;
|
@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("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 {
|
:root {
|
||||||
--font-scale: 1;
|
--font-scale: 1;
|
||||||
|
|||||||
+15
-1
@@ -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
|
/* 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,
|
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("./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 {
|
body {
|
||||||
background-color: var(--cpd-color-bg-canvas-default);
|
background-color: var(--cpd-color-bg-canvas-default);
|
||||||
color: var(--cpd-color-text-primary);
|
color: var(--cpd-color-text-primary);
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ import {
|
|||||||
import { type Logger, logger as rootLogger } from "matrix-js-sdk/lib/logger";
|
import { type Logger, logger as rootLogger } from "matrix-js-sdk/lib/logger";
|
||||||
import {
|
import {
|
||||||
MembershipManagerEvent,
|
MembershipManagerEvent,
|
||||||
type LivekitTransportConfig,
|
type LivekitTransport,
|
||||||
type MatrixRTCSession,
|
type MatrixRTCSession,
|
||||||
type RTCCallIntent,
|
type RTCCallIntent,
|
||||||
type RTCNotificationType,
|
type RTCNotificationType,
|
||||||
@@ -624,7 +624,7 @@ export function createCallViewModel$(
|
|||||||
),
|
),
|
||||||
muteStates,
|
muteStates,
|
||||||
joinMatrixRTC: (
|
joinMatrixRTC: (
|
||||||
transport: LivekitTransportConfig,
|
transport: LivekitTransport,
|
||||||
delayedLeaveTimings: ResolvedDelayedLeaveTimings,
|
delayedLeaveTimings: ResolvedDelayedLeaveTimings,
|
||||||
) => {
|
) => {
|
||||||
return enterRTCSession(
|
return enterRTCSession(
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ Please see LICENSE in the repository root for full details.
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
Status as RTCMemberStatus,
|
Status as RTCMemberStatus,
|
||||||
type LivekitTransportConfig,
|
type LivekitTransport,
|
||||||
type MatrixRTCSession,
|
type MatrixRTCSession,
|
||||||
} from "matrix-js-sdk/lib/matrixrtc";
|
} from "matrix-js-sdk/lib/matrixrtc";
|
||||||
import {
|
import {
|
||||||
@@ -121,8 +121,7 @@ const mockedClient = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
describe("enterRTCSession", () => {
|
describe("enterRTCSession", () => {
|
||||||
const transport: LivekitTransportConfig = {
|
const transport: LivekitTransport = {
|
||||||
livekit_alias: "roomId",
|
|
||||||
livekit_service_url: "http://my-livekit-service-url.com",
|
livekit_service_url: "http://my-livekit-service-url.com",
|
||||||
type: "livekit",
|
type: "livekit",
|
||||||
};
|
};
|
||||||
@@ -157,8 +156,7 @@ describe("enterRTCSession", () => {
|
|||||||
memberId: "@alice:example.org:DEVICE",
|
memberId: "@alice:example.org:DEVICE",
|
||||||
userId: "@alice:example.org",
|
userId: "@alice:example.org",
|
||||||
},
|
},
|
||||||
[],
|
[transport],
|
||||||
transport,
|
|
||||||
expect.objectContaining({ manageMediaKeys: true }),
|
expect.objectContaining({ manageMediaKeys: true }),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -178,7 +176,6 @@ describe("enterRTCSession", () => {
|
|||||||
|
|
||||||
expect(mockedSession.joinRTCSession).toHaveBeenLastCalledWith(
|
expect(mockedSession.joinRTCSession).toHaveBeenLastCalledWith(
|
||||||
expect.any(Object),
|
expect.any(Object),
|
||||||
[],
|
|
||||||
expect.any(Object),
|
expect.any(Object),
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
keyRotationParticipantLimit: 50,
|
keyRotationParticipantLimit: 50,
|
||||||
@@ -190,7 +187,6 @@ describe("enterRTCSession", () => {
|
|||||||
enterRTCSession(mockedSession, ownMemberMock, transport, options);
|
enterRTCSession(mockedSession, ownMemberMock, transport, options);
|
||||||
|
|
||||||
expect(mockedSession.joinRTCSession).toHaveBeenLastCalledWith(
|
expect(mockedSession.joinRTCSession).toHaveBeenLastCalledWith(
|
||||||
expect.anything(),
|
|
||||||
expect.anything(),
|
expect.anything(),
|
||||||
expect.anything(),
|
expect.anything(),
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
@@ -256,7 +252,7 @@ describe("LocalMembership", () => {
|
|||||||
|
|
||||||
it("throws error on missing RTC config error", () => {
|
it("throws error on missing RTC config error", () => {
|
||||||
withTestScheduler(({ scope, hot, expectObservable }) => {
|
withTestScheduler(({ scope, hot, expectObservable }) => {
|
||||||
const localTransport$ = scope.behavior<null | LivekitTransportConfig>(
|
const localTransport$ = scope.behavior<null | LivekitTransport>(
|
||||||
hot("1ms #", {}, new MatrixRTCTransportMissingError("domain.com")),
|
hot("1ms #", {}, new MatrixRTCTransportMissingError("domain.com")),
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
@@ -321,7 +317,7 @@ describe("LocalMembership", () => {
|
|||||||
|
|
||||||
const mockTransportConfig = {
|
const mockTransportConfig = {
|
||||||
livekit_service_url: "a",
|
livekit_service_url: "a",
|
||||||
} as LivekitTransportConfig;
|
} as LivekitTransport;
|
||||||
|
|
||||||
const mockTransport = {
|
const mockTransport = {
|
||||||
transport: mockTransportConfig,
|
transport: mockTransportConfig,
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import { type MatrixClient } from "matrix-js-sdk";
|
|||||||
import {
|
import {
|
||||||
Status as RTCSessionStatus,
|
Status as RTCSessionStatus,
|
||||||
type LivekitTransport,
|
type LivekitTransport,
|
||||||
type LivekitTransportConfig,
|
|
||||||
type MatrixRTCSession,
|
type MatrixRTCSession,
|
||||||
type RTCCallIntent,
|
type RTCCallIntent,
|
||||||
type RTCNotificationType,
|
type RTCNotificationType,
|
||||||
@@ -144,7 +143,7 @@ interface Props {
|
|||||||
connectionManager: IConnectionManager;
|
connectionManager: IConnectionManager;
|
||||||
createPublisherFactory: (connection: Connection) => Publisher;
|
createPublisherFactory: (connection: Connection) => Publisher;
|
||||||
joinMatrixRTC: (
|
joinMatrixRTC: (
|
||||||
transport: LivekitTransportConfig,
|
transport: LivekitTransport,
|
||||||
delayedLeaveTimings: ResolvedDelayedLeaveTimings,
|
delayedLeaveTimings: ResolvedDelayedLeaveTimings,
|
||||||
) => void;
|
) => void;
|
||||||
homeserverConnected: HomeserverConnected;
|
homeserverConnected: HomeserverConnected;
|
||||||
@@ -966,7 +965,7 @@ interface EnterRTCSessionOptions {
|
|||||||
export function enterRTCSession(
|
export function enterRTCSession(
|
||||||
rtcSession: MatrixRTCSession,
|
rtcSession: MatrixRTCSession,
|
||||||
ownMembershipIdentity: CallMembershipIdentityParts,
|
ownMembershipIdentity: CallMembershipIdentityParts,
|
||||||
transport: LivekitTransportConfig,
|
transport: LivekitTransport,
|
||||||
{
|
{
|
||||||
encryptMedia,
|
encryptMedia,
|
||||||
matrixRTCMode,
|
matrixRTCMode,
|
||||||
@@ -988,18 +987,6 @@ export function enterRTCSession(
|
|||||||
} = Config.get();
|
} = Config.get();
|
||||||
const retryInterval = sessionConfig.network_error_retry_ms;
|
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
|
// Set maximumNetworkErrorRetryCount such that we will consider the client
|
||||||
// disconnected as soon as either it fails to sync for longer than the grace
|
// 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.
|
// period, or it is likely that a delayed leave event has been sent.
|
||||||
@@ -1011,11 +998,7 @@ export function enterRTCSession(
|
|||||||
// Multi-sfu does not need a preferred foci list. just the focus that is actually used.
|
// 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?
|
// TODO where/how do we track errors originating from the ongoing rtcSession?
|
||||||
|
|
||||||
rtcSession.joinRTCSession(
|
rtcSession.joinRTCSession(ownMembershipIdentity, [transport], {
|
||||||
ownMembershipIdentity,
|
|
||||||
[],
|
|
||||||
backwardCompatibleTransport,
|
|
||||||
{
|
|
||||||
notificationType,
|
notificationType,
|
||||||
callIntent,
|
callIntent,
|
||||||
manageMediaKeys: encryptMedia,
|
manageMediaKeys: encryptMedia,
|
||||||
@@ -1029,6 +1012,5 @@ export function enterRTCSession(
|
|||||||
keyRotationParticipantLimit: sessionConfig.key_rotation_participant_limit,
|
keyRotationParticipantLimit: sessionConfig.key_rotation_participant_limit,
|
||||||
unstableSendStickyEvents: matrixRTCMode === MatrixRTCMode.Matrix_2_0,
|
unstableSendStickyEvents: matrixRTCMode === MatrixRTCMode.Matrix_2_0,
|
||||||
maximumNetworkErrorRetryCount: maximumNetworkErrorRetryCount,
|
maximumNetworkErrorRetryCount: maximumNetworkErrorRetryCount,
|
||||||
},
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
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 { type MatrixClient } from "matrix-js-sdk";
|
||||||
import { logger as rootLogger, type Logger } from "matrix-js-sdk/lib/logger";
|
import { logger as rootLogger, type Logger } from "matrix-js-sdk/lib/logger";
|
||||||
import { type CallMembershipIdentityParts } from "matrix-js-sdk/lib/matrixrtc/EncryptionManager";
|
import { type CallMembershipIdentityParts } from "matrix-js-sdk/lib/matrixrtc/EncryptionManager";
|
||||||
@@ -34,34 +34,13 @@ interface Props {
|
|||||||
matrixRTCMode: MatrixRTCMode;
|
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 {
|
export interface LocalTransport {
|
||||||
transport: LivekitTransportConfig;
|
transport: LivekitTransport;
|
||||||
sfuConfig: SFUConfig;
|
sfuConfig: SFUConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isLocalTransport(
|
export function isLocalTransport(
|
||||||
obj: LivekitTransportConfig | LocalTransport,
|
obj: LivekitTransport | LocalTransport,
|
||||||
): obj is LocalTransport {
|
): obj is LocalTransport {
|
||||||
return "transport" in obj && "sfuConfig" in obj;
|
return "transport" in obj && "sfuConfig" in obj;
|
||||||
}
|
}
|
||||||
@@ -90,7 +69,7 @@ export async function getLocalTransport({
|
|||||||
const customUrl = customLivekitUrl.value$.value;
|
const customUrl = customLivekitUrl.value$.value;
|
||||||
|
|
||||||
// Respect the user's custom URL, if set
|
// Respect the user's custom URL, if set
|
||||||
const transport: LivekitTransportConfig | null = customUrl
|
const transport: LivekitTransport | null = customUrl
|
||||||
? { type: "livekit", livekit_service_url: customUrl }
|
? { type: "livekit", livekit_service_url: customUrl }
|
||||||
: await discovery.discoverPreferredTransport();
|
: await discovery.discoverPreferredTransport();
|
||||||
|
|
||||||
@@ -131,7 +110,7 @@ export async function getLocalTransport({
|
|||||||
* @throws FailToGetOpenIdToken, NoMatrix2AuthorizationService
|
* @throws FailToGetOpenIdToken, NoMatrix2AuthorizationService
|
||||||
*/
|
*/
|
||||||
async function doOpenIdAndJWTFromUrl(
|
async function doOpenIdAndJWTFromUrl(
|
||||||
transport: LivekitTransportConfig,
|
transport: LivekitTransport,
|
||||||
matrixRTCMode: MatrixRTCMode,
|
matrixRTCMode: MatrixRTCMode,
|
||||||
membership: CallMembershipIdentityParts,
|
membership: CallMembershipIdentityParts,
|
||||||
roomId: string,
|
roomId: string,
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import {
|
|||||||
import { MatrixError } from "matrix-js-sdk";
|
import { MatrixError } from "matrix-js-sdk";
|
||||||
import { logger as rootLogger } from "matrix-js-sdk/lib/logger";
|
import { logger as rootLogger } from "matrix-js-sdk/lib/logger";
|
||||||
import {
|
import {
|
||||||
type LivekitTransportConfig,
|
type LivekitTransport,
|
||||||
type Transport,
|
type Transport,
|
||||||
} from "matrix-js-sdk/lib/matrixrtc";
|
} from "matrix-js-sdk/lib/matrixrtc";
|
||||||
|
|
||||||
@@ -28,12 +28,12 @@ import {
|
|||||||
|
|
||||||
type DiscoveryClient = RtcTransportAutoDiscoveryProps["client"];
|
type DiscoveryClient = RtcTransportAutoDiscoveryProps["client"];
|
||||||
|
|
||||||
const backendTransport: LivekitTransportConfig = {
|
const backendTransport: LivekitTransport = {
|
||||||
type: "livekit",
|
type: "livekit",
|
||||||
livekit_service_url: "https://backend.example.org",
|
livekit_service_url: "https://backend.example.org",
|
||||||
};
|
};
|
||||||
|
|
||||||
const configTransport: LivekitTransportConfig = {
|
const configTransport: LivekitTransport = {
|
||||||
type: "livekit",
|
type: "livekit",
|
||||||
livekit_service_url: "https://config.example.org",
|
livekit_service_url: "https://config.example.org",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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.
|
Please see LICENSE in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
import {
|
import {
|
||||||
isLivekitTransportConfig,
|
isLivekitTransport,
|
||||||
type LivekitTransportConfig,
|
type LivekitTransport,
|
||||||
} from "matrix-js-sdk/lib/matrixrtc";
|
} from "matrix-js-sdk/lib/matrixrtc";
|
||||||
import { type MatrixClient } from "matrix-js-sdk";
|
import { type MatrixClient } from "matrix-js-sdk";
|
||||||
import { type Logger } from "matrix-js-sdk/lib/logger";
|
import { type Logger } from "matrix-js-sdk/lib/logger";
|
||||||
@@ -40,7 +40,7 @@ export class RtcTransportAutoDiscovery {
|
|||||||
this.logger = logger.getChild("[RtcTransportAutoDiscovery]");
|
this.logger = logger.getChild("[RtcTransportAutoDiscovery]");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async discoverPreferredTransport(): Promise<LivekitTransportConfig | null> {
|
public async discoverPreferredTransport(): Promise<LivekitTransport | null> {
|
||||||
// 1) backend transports
|
// 1) backend transports
|
||||||
const backendTransport = await this.tryBackendTransports();
|
const backendTransport = await this.tryBackendTransports();
|
||||||
if (backendTransport) {
|
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.
|
* This will not throw errors, but instead just log them and return null if the expected config is not found or malformed.
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
private async tryBackendTransports(): Promise<LivekitTransportConfig | null> {
|
private async tryBackendTransports(): Promise<LivekitTransport | null> {
|
||||||
const client = this.client;
|
const client = this.client;
|
||||||
// MSC4143: Attempt to fetch transports from backend.
|
// MSC4143: Attempt to fetch transports from backend.
|
||||||
this.logger.info("First try to use getRTCTransports end point ...");
|
this.logger.info("First try to use getRTCTransports end point ...");
|
||||||
@@ -75,7 +75,7 @@ export class RtcTransportAutoDiscovery {
|
|||||||
const transportList = await doNetworkOperationWithRetry(async () =>
|
const transportList = await doNetworkOperationWithRetry(async () =>
|
||||||
client._unstable_getRTCTransports(),
|
client._unstable_getRTCTransports(),
|
||||||
);
|
);
|
||||||
const first = transportList.find(isLivekitTransportConfig);
|
const first = transportList.find(isLivekitTransport);
|
||||||
if (first) {
|
if (first) {
|
||||||
return first;
|
return first;
|
||||||
} else {
|
} else {
|
||||||
@@ -90,7 +90,7 @@ export class RtcTransportAutoDiscovery {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private tryConfigTransport(): LivekitTransportConfig | null {
|
private tryConfigTransport(): LivekitTransport | null {
|
||||||
const url = this.resolvedConfig.livekit?.livekit_service_url;
|
const url = this.resolvedConfig.livekit?.livekit_service_url;
|
||||||
if (url) {
|
if (url) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import fetchMock from "fetch-mock";
|
|||||||
import EventEmitter from "events";
|
import EventEmitter from "events";
|
||||||
import { type IOpenIDToken } from "matrix-js-sdk";
|
import { type IOpenIDToken } from "matrix-js-sdk";
|
||||||
import { logger, type Logger } from "matrix-js-sdk/lib/logger";
|
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 {
|
import {
|
||||||
Connection,
|
Connection,
|
||||||
@@ -57,7 +57,7 @@ let fakeLocalParticipant: MockedObject<LocalParticipant>;
|
|||||||
|
|
||||||
const ROOM_ID = "!roomID:example.org";
|
const ROOM_ID = "!roomID:example.org";
|
||||||
|
|
||||||
const livekitFocus: LivekitTransportConfig = {
|
const livekitFocus: LivekitTransport = {
|
||||||
livekit_service_url: "https://matrix-rtc.example.org/livekit/jwt",
|
livekit_service_url: "https://matrix-rtc.example.org/livekit/jwt",
|
||||||
type: "livekit",
|
type: "livekit",
|
||||||
};
|
};
|
||||||
@@ -228,9 +228,7 @@ describe("Start connection states", () => {
|
|||||||
capturedState = capturedStates.pop();
|
capturedState = capturedStates.pop();
|
||||||
if (capturedState instanceof Error) {
|
if (capturedState instanceof Error) {
|
||||||
expect(capturedState.message).toEqual("Something went wrong");
|
expect(capturedState.message).toEqual("Something went wrong");
|
||||||
expect(connection.transport.livekit_alias).toEqual(
|
expect(connection.transport).toEqual(livekitFocus);
|
||||||
livekitFocus.livekit_alias,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
expect.fail("Expected FailedToStart state but got " + capturedState);
|
expect.fail("Expected FailedToStart state but got " + capturedState);
|
||||||
}
|
}
|
||||||
@@ -290,9 +288,7 @@ describe("Start connection states", () => {
|
|||||||
expect(capturedState.cause.message).toContain(
|
expect(capturedState.cause.message).toContain(
|
||||||
"Failed to look up user info from homeserver",
|
"Failed to look up user info from homeserver",
|
||||||
);
|
);
|
||||||
expect(connection.transport.livekit_alias).toEqual(
|
expect(connection.transport).toEqual(livekitFocus);
|
||||||
livekitFocus.livekit_alias,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
expect.fail("Expected FailedToStart state but got " + capturedState);
|
expect.fail("Expected FailedToStart state but got " + capturedState);
|
||||||
}
|
}
|
||||||
@@ -357,9 +353,7 @@ describe("Start connection states", () => {
|
|||||||
expect(capturedState.cause.message).toContain(
|
expect(capturedState.cause.message).toContain(
|
||||||
"Failed to connect to livekit",
|
"Failed to connect to livekit",
|
||||||
);
|
);
|
||||||
expect(connection.transport.livekit_alias).toEqual(
|
expect(connection.transport).toEqual(livekitFocus);
|
||||||
livekitFocus.livekit_alias,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
expect.fail(
|
expect.fail(
|
||||||
"Expected FailedToStart state but got " + JSON.stringify(capturedState),
|
"Expected FailedToStart state but got " + JSON.stringify(capturedState),
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import {
|
|||||||
type Track,
|
type Track,
|
||||||
type TrackPublication,
|
type TrackPublication,
|
||||||
} from "livekit-client";
|
} 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 { BehaviorSubject, map } from "rxjs";
|
||||||
import { type Logger } from "matrix-js-sdk/lib/logger";
|
import { type Logger } from "matrix-js-sdk/lib/logger";
|
||||||
import { type CallMembershipIdentityParts } from "matrix-js-sdk/lib/matrixrtc/EncryptionManager";
|
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 */
|
/** The identity parts to use on this connection */
|
||||||
ownMembershipIdentity: CallMembershipIdentityParts;
|
ownMembershipIdentity: CallMembershipIdentityParts;
|
||||||
/** The media transport to connect to. */
|
/** The media transport to connect to. */
|
||||||
transport: LivekitTransportConfig;
|
transport: LivekitTransport;
|
||||||
/** The Matrix client to use for OpenID and SFU config requests. */
|
/** The Matrix client to use for OpenID and SFU config requests. */
|
||||||
client: OpenIDClientParts;
|
client: OpenIDClientParts;
|
||||||
/** The room ID this connection is associated with. */
|
/** The room ID this connection is associated with. */
|
||||||
@@ -107,7 +107,7 @@ export class Connection {
|
|||||||
/**
|
/**
|
||||||
* The media transport to connect to.
|
* The media transport to connect to.
|
||||||
*/
|
*/
|
||||||
public readonly transport: LivekitTransportConfig;
|
public readonly transport: LivekitTransport;
|
||||||
|
|
||||||
public readonly livekitRoom: LivekitRoom;
|
public readonly livekitRoom: LivekitRoom;
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
// imported as inline to support worker when loaded from a cdn (cross domain)
|
||||||
import E2EEWorker from "livekit-client/e2ee-worker?worker&inline";
|
import E2EEWorker from "livekit-client/e2ee-worker?worker&inline";
|
||||||
import { type CallMembershipIdentityParts } from "matrix-js-sdk/lib/matrixrtc/EncryptionManager";
|
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 { type ObservableScope } from "../../ObservableScope.ts";
|
||||||
import { Connection } from "./Connection.ts";
|
import { Connection } from "./Connection.ts";
|
||||||
@@ -44,7 +44,7 @@ import {
|
|||||||
export interface ConnectionFactory {
|
export interface ConnectionFactory {
|
||||||
createConnection(
|
createConnection(
|
||||||
scope: ObservableScope,
|
scope: ObservableScope,
|
||||||
transport: LivekitTransportConfig,
|
transport: LivekitTransport,
|
||||||
ownMembershipIdentity: CallMembershipIdentityParts,
|
ownMembershipIdentity: CallMembershipIdentityParts,
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
sfuConfig?: SFUConfig,
|
sfuConfig?: SFUConfig,
|
||||||
@@ -103,7 +103,7 @@ export class ECConnectionFactory implements ConnectionFactory {
|
|||||||
*/
|
*/
|
||||||
public createConnection(
|
public createConnection(
|
||||||
scope: ObservableScope,
|
scope: ObservableScope,
|
||||||
transport: LivekitTransportConfig,
|
transport: LivekitTransport,
|
||||||
ownMembershipIdentity: CallMembershipIdentityParts,
|
ownMembershipIdentity: CallMembershipIdentityParts,
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
sfuConfig?: SFUConfig,
|
sfuConfig?: SFUConfig,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ Please see LICENSE in the repository root for full details.
|
|||||||
|
|
||||||
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
||||||
import { BehaviorSubject, NEVER } from "rxjs";
|
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 { type RemoteParticipant } from "livekit-client";
|
||||||
import { logger } from "matrix-js-sdk/lib/logger";
|
import { logger } from "matrix-js-sdk/lib/logger";
|
||||||
|
|
||||||
@@ -24,12 +24,12 @@ import { type Behavior } from "../../Behavior.ts";
|
|||||||
|
|
||||||
// Some test constants
|
// Some test constants
|
||||||
|
|
||||||
const TRANSPORT_1: LivekitTransportConfig = {
|
const TRANSPORT_1: LivekitTransport = {
|
||||||
type: "livekit",
|
type: "livekit",
|
||||||
livekit_service_url: "https://lk.example.org",
|
livekit_service_url: "https://lk.example.org",
|
||||||
};
|
};
|
||||||
|
|
||||||
const TRANSPORT_2: LivekitTransportConfig = {
|
const TRANSPORT_2: LivekitTransport = {
|
||||||
type: "livekit",
|
type: "livekit",
|
||||||
livekit_service_url: "https://lk.sample.com",
|
livekit_service_url: "https://lk.sample.com",
|
||||||
};
|
};
|
||||||
@@ -47,7 +47,7 @@ beforeEach(() => {
|
|||||||
vi.mocked(fakeConnectionFactory).createConnection = vi
|
vi.mocked(fakeConnectionFactory).createConnection = vi
|
||||||
.fn()
|
.fn()
|
||||||
.mockImplementation(
|
.mockImplementation(
|
||||||
(scope: ObservableScope, transport: LivekitTransportConfig) => {
|
(scope: ObservableScope, transport: LivekitTransport) => {
|
||||||
const mockConnection = {
|
const mockConnection = {
|
||||||
transport,
|
transport,
|
||||||
remoteParticipants$: new BehaviorSubject([]),
|
remoteParticipants$: new BehaviorSubject([]),
|
||||||
@@ -225,7 +225,7 @@ describe("connectionManagerData$ stream", () => {
|
|||||||
// Used in test to control fake connections' remoteParticipants$ streams
|
// Used in test to control fake connections' remoteParticipants$ streams
|
||||||
let fakeRemoteParticipantsStreams: Map<string, Behavior<RemoteParticipant[]>>;
|
let fakeRemoteParticipantsStreams: Map<string, Behavior<RemoteParticipant[]>>;
|
||||||
|
|
||||||
function keyForTransport(transport: LivekitTransportConfig): string {
|
function keyForTransport(transport: LivekitTransport): string {
|
||||||
return `${transport.livekit_service_url}`;
|
return `${transport.livekit_service_url}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,7 +233,7 @@ describe("connectionManagerData$ stream", () => {
|
|||||||
fakeRemoteParticipantsStreams = new Map();
|
fakeRemoteParticipantsStreams = new Map();
|
||||||
|
|
||||||
function getRemoteParticipantsFor(
|
function getRemoteParticipantsFor(
|
||||||
transport: LivekitTransportConfig,
|
transport: LivekitTransport,
|
||||||
): Behavior<RemoteParticipant[]> {
|
): Behavior<RemoteParticipant[]> {
|
||||||
return (
|
return (
|
||||||
fakeRemoteParticipantsStreams.get(keyForTransport(transport)) ??
|
fakeRemoteParticipantsStreams.get(keyForTransport(transport)) ??
|
||||||
@@ -245,7 +245,7 @@ describe("connectionManagerData$ stream", () => {
|
|||||||
vi.mocked(fakeConnectionFactory).createConnection = vi
|
vi.mocked(fakeConnectionFactory).createConnection = vi
|
||||||
.fn()
|
.fn()
|
||||||
.mockImplementation(
|
.mockImplementation(
|
||||||
(scope: ObservableScope, transport: LivekitTransportConfig) => {
|
(scope: ObservableScope, transport: LivekitTransport) => {
|
||||||
const fakeRemoteParticipants$ = new BehaviorSubject<
|
const fakeRemoteParticipants$ = new BehaviorSubject<
|
||||||
RemoteParticipant[]
|
RemoteParticipant[]
|
||||||
>([]);
|
>([]);
|
||||||
|
|||||||
@@ -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.
|
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 {
|
import {
|
||||||
combineLatest,
|
combineLatest,
|
||||||
map,
|
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.
|
// 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}`.
|
// 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;
|
return transport.livekit_service_url;
|
||||||
@@ -67,13 +67,13 @@ export class ConnectionManagerData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getConnectionForTransport(
|
public getConnectionForTransport(
|
||||||
transport: LivekitTransportConfig,
|
transport: LivekitTransport,
|
||||||
): Connection | null {
|
): Connection | null {
|
||||||
return this.store.get(this.getKey(transport))?.connection ?? null;
|
return this.store.get(this.getKey(transport))?.connection ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getParticipantsForTransport(
|
public getParticipantsForTransport(
|
||||||
transport: LivekitTransportConfig,
|
transport: LivekitTransport,
|
||||||
): RemoteParticipant[] {
|
): RemoteParticipant[] {
|
||||||
const key = this.getKey(transport);
|
const key = this.getKey(transport);
|
||||||
const existing = this.store.get(key);
|
const existing = this.store.get(key);
|
||||||
@@ -88,7 +88,7 @@ interface Props {
|
|||||||
scope: ObservableScope;
|
scope: ObservableScope;
|
||||||
connectionFactory: ConnectionFactory;
|
connectionFactory: ConnectionFactory;
|
||||||
localTransport$: Observable<LocalTransport>;
|
localTransport$: Observable<LocalTransport>;
|
||||||
remoteTransports$: Behavior<Epoch<LivekitTransportConfig[]>>;
|
remoteTransports$: Behavior<Epoch<LivekitTransport[]>>;
|
||||||
|
|
||||||
logger: Logger;
|
logger: Logger;
|
||||||
ownMembershipIdentity: CallMembershipIdentityParts;
|
ownMembershipIdentity: CallMembershipIdentityParts;
|
||||||
@@ -147,7 +147,7 @@ export function createConnectionManager$({
|
|||||||
* externally this is modified via `registerTransports()`.
|
* externally this is modified via `registerTransports()`.
|
||||||
*/
|
*/
|
||||||
const localAndRemoteTransports$: Behavior<
|
const localAndRemoteTransports$: Behavior<
|
||||||
Epoch<(LivekitTransportConfig | LocalTransport)[]>
|
Epoch<(LivekitTransport | LocalTransport)[]>
|
||||||
> = scope.behavior(
|
> = scope.behavior(
|
||||||
combineLatest([localTransportAsArray$, remoteTransports$]).pipe(
|
combineLatest([localTransportAsArray$, remoteTransports$]).pipe(
|
||||||
// Combine local and remote transports into one transport array
|
// Combine local and remote transports into one transport array
|
||||||
@@ -271,7 +271,7 @@ export function createConnectionManager$({
|
|||||||
return { connectionManagerData$ };
|
return { connectionManagerData$ };
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeDuplicateTransports<T extends LivekitTransportConfig>(
|
function removeDuplicateTransports<T extends LivekitTransport>(
|
||||||
transports: T[],
|
transports: T[],
|
||||||
): T[] {
|
): T[] {
|
||||||
return transports.reduce((acc, transport) => {
|
return transports.reduce((acc, transport) => {
|
||||||
|
|||||||
@@ -42,13 +42,11 @@ const fallbackMemberId = (userId: string, deviceId: string): string =>
|
|||||||
const transportA: LivekitTransport = {
|
const transportA: LivekitTransport = {
|
||||||
type: "livekit",
|
type: "livekit",
|
||||||
livekit_service_url: "https://lk.example.org",
|
livekit_service_url: "https://lk.example.org",
|
||||||
livekit_alias: "!alias:example.org",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const transportB: LivekitTransport = {
|
const transportB: LivekitTransport = {
|
||||||
type: "livekit",
|
type: "livekit",
|
||||||
livekit_service_url: "https://lk.sample.com",
|
livekit_service_url: "https://lk.sample.com",
|
||||||
livekit_alias: "!alias:sample.com",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const bobMembership = mockRtcMembership("@bob:example.org", "DEV000", {
|
const bobMembership = mockRtcMembership("@bob:example.org", "DEV000", {
|
||||||
@@ -126,7 +124,7 @@ function createEpochedMemberships$(m$: Observable<CallMembership[]>): {
|
|||||||
const membershipsWithTransport$ = memberships$.pipe(
|
const membershipsWithTransport$ = memberships$.pipe(
|
||||||
mapEpoch((members) => {
|
mapEpoch((members) => {
|
||||||
return members.map((m) => {
|
return members.map((m) => {
|
||||||
const tr = m.getTransport(m);
|
const tr = m.getTransport();
|
||||||
return {
|
return {
|
||||||
membership: m,
|
membership: m,
|
||||||
transport:
|
transport:
|
||||||
@@ -152,7 +150,7 @@ test("should signal participant on a connection that is publishing", async () =>
|
|||||||
);
|
);
|
||||||
|
|
||||||
const connection = {
|
const connection = {
|
||||||
transport: bobMembership.getTransport(bobMembership),
|
transport: bobMembership.getTransport(),
|
||||||
} as unknown as Connection;
|
} as unknown as Connection;
|
||||||
const dataWithPublisher = new ConnectionManagerData();
|
const dataWithPublisher = new ConnectionManagerData();
|
||||||
dataWithPublisher.add(connection, [
|
dataWithPublisher.add(connection, [
|
||||||
@@ -195,7 +193,7 @@ test("should signal participant on a connection that is not publishing", async (
|
|||||||
);
|
);
|
||||||
|
|
||||||
const connection = {
|
const connection = {
|
||||||
transport: bobMembership.getTransport(bobMembership),
|
transport: bobMembership.getTransport(),
|
||||||
} as unknown as Connection;
|
} as unknown as Connection;
|
||||||
const dataWithPublisher = new ConnectionManagerData();
|
const dataWithPublisher = new ConnectionManagerData();
|
||||||
dataWithPublisher.add(connection, []);
|
dataWithPublisher.add(connection, []);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ Please see LICENSE in the repository root for full details.
|
|||||||
import { type LocalParticipant, type RemoteParticipant } from "livekit-client";
|
import { type LocalParticipant, type RemoteParticipant } from "livekit-client";
|
||||||
import {
|
import {
|
||||||
type CallMembership,
|
type CallMembership,
|
||||||
type LivekitTransportConfig,
|
type LivekitTransport,
|
||||||
} from "matrix-js-sdk/lib/matrixrtc";
|
} from "matrix-js-sdk/lib/matrixrtc";
|
||||||
import { combineLatest, filter, map } from "rxjs";
|
import { combineLatest, filter, map } from "rxjs";
|
||||||
import { logger } from "matrix-js-sdk/lib/logger";
|
import { logger } from "matrix-js-sdk/lib/logger";
|
||||||
@@ -60,7 +60,7 @@ export interface RemoteMatrixLivekitMember extends MatrixLivekitMember {
|
|||||||
interface Props {
|
interface Props {
|
||||||
scope: ObservableScope;
|
scope: ObservableScope;
|
||||||
membershipsWithTransport$: Behavior<
|
membershipsWithTransport$: Behavior<
|
||||||
Epoch<{ membership: CallMembership; transport?: LivekitTransportConfig }[]>
|
Epoch<{ membership: CallMembership; transport?: LivekitTransport }[]>
|
||||||
>;
|
>;
|
||||||
connectionManager: IConnectionManager;
|
connectionManager: IConnectionManager;
|
||||||
localUser: { deviceId: string; userId: string };
|
localUser: { deviceId: string; userId: string };
|
||||||
@@ -165,7 +165,7 @@ export function createRemoteMatrixLivekitMembers$({
|
|||||||
// TODO add back in the callviewmodel pauseWhen(this.pretendToBeDisconnected$)
|
// TODO add back in the callviewmodel pauseWhen(this.pretendToBeDisconnected$)
|
||||||
|
|
||||||
// TODO add this to the JS-SDK
|
// TODO add this to the JS-SDK
|
||||||
export function areLivekitTransportsEqual<T extends LivekitTransportConfig>(
|
export function areLivekitTransportsEqual<T extends LivekitTransport>(
|
||||||
t1: T | null,
|
t1: T | null,
|
||||||
t2: T | null,
|
t2: T | null,
|
||||||
): boolean {
|
): boolean {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { BehaviorSubject, NEVER } from "rxjs";
|
|||||||
import { type Room as LivekitRoom } from "livekit-client";
|
import { type Room as LivekitRoom } from "livekit-client";
|
||||||
import EventEmitter from "events";
|
import EventEmitter from "events";
|
||||||
import fetchMock from "fetch-mock";
|
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 { logger } from "matrix-js-sdk/lib/logger";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -149,7 +149,7 @@ test("bob, carl, then bob joining no tracks yet", () => {
|
|||||||
a: expect.toSatisfy((co) =>
|
a: expect.toSatisfy((co) =>
|
||||||
areLivekitTransportsEqual(
|
areLivekitTransportsEqual(
|
||||||
co.transport,
|
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(
|
expect(
|
||||||
areLivekitTransportsEqual(
|
areLivekitTransportsEqual(
|
||||||
connection.transport,
|
connection.transport,
|
||||||
carlMembership.transports[0]! as LivekitTransportConfig,
|
carlMembership.transports[0]! as LivekitTransport,
|
||||||
),
|
),
|
||||||
).toBe(true);
|
).toBe(true);
|
||||||
return true;
|
return true;
|
||||||
@@ -216,7 +216,7 @@ test("bob, carl, then bob joining no tracks yet", () => {
|
|||||||
expect(
|
expect(
|
||||||
areLivekitTransportsEqual(
|
areLivekitTransportsEqual(
|
||||||
connection.transport,
|
connection.transport,
|
||||||
daveMembership.transports[0]! as LivekitTransportConfig,
|
daveMembership.transports[0]! as LivekitTransport,
|
||||||
),
|
),
|
||||||
).toBe(true);
|
).toBe(true);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ Please see LICENSE in the repository root for full details.
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
type CallMembership,
|
type CallMembership,
|
||||||
type LivekitTransportConfig,
|
type LivekitTransport,
|
||||||
type MatrixRTCSession,
|
type MatrixRTCSession,
|
||||||
MatrixRTCSessionEvent,
|
MatrixRTCSessionEvent,
|
||||||
isLivekitTransportConfig,
|
isLivekitTransport,
|
||||||
} from "matrix-js-sdk/lib/matrixrtc";
|
} from "matrix-js-sdk/lib/matrixrtc";
|
||||||
import { fromEvent } from "rxjs";
|
import { fromEvent } from "rxjs";
|
||||||
|
|
||||||
@@ -31,35 +31,32 @@ export const membershipsAndTransports$ = (
|
|||||||
memberships$: Behavior<Epoch<CallMembership[]>>,
|
memberships$: Behavior<Epoch<CallMembership[]>>,
|
||||||
): {
|
): {
|
||||||
membershipsWithTransport$: Behavior<
|
membershipsWithTransport$: Behavior<
|
||||||
Epoch<{ membership: CallMembership; transport?: LivekitTransportConfig }[]>
|
Epoch<{ membership: CallMembership; transport?: LivekitTransport }[]>
|
||||||
>;
|
>;
|
||||||
transports$: Behavior<Epoch<LivekitTransportConfig[]>>;
|
transports$: Behavior<Epoch<LivekitTransport[]>>;
|
||||||
} => {
|
} => {
|
||||||
const membershipsWithTransport$: Behavior<
|
const membershipsWithTransport$: Behavior<
|
||||||
Epoch<
|
Epoch<
|
||||||
{
|
{
|
||||||
membership: CallMembership;
|
membership: CallMembership;
|
||||||
transport: LivekitTransportConfig | undefined;
|
transport: LivekitTransport | undefined;
|
||||||
}[]
|
}[]
|
||||||
>
|
>
|
||||||
> = scope.behavior(
|
> = scope.behavior(
|
||||||
memberships$.pipe(
|
memberships$.pipe(
|
||||||
mapEpoch((memberships) => {
|
mapEpoch((memberships) => {
|
||||||
return memberships.map((membership) => {
|
return memberships.map((membership) => {
|
||||||
const oldestMembership = memberships[0] ?? membership;
|
const transport = membership.getTransport();
|
||||||
const transport = membership.getTransport(oldestMembership);
|
|
||||||
return {
|
return {
|
||||||
membership,
|
membership,
|
||||||
transport: isLivekitTransportConfig(transport)
|
transport: isLivekitTransport(transport) ? transport : undefined,
|
||||||
? transport
|
|
||||||
: undefined,
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
const transports$: Behavior<Epoch<LivekitTransportConfig[]>> = scope.behavior(
|
const transports$: Behavior<Epoch<LivekitTransport[]>> = scope.behavior(
|
||||||
membershipsWithTransport$.pipe(
|
membershipsWithTransport$.pipe(
|
||||||
mapEpoch((mts) => mts.flatMap(({ transport: t }) => (t ? [t] : []))),
|
mapEpoch((mts) => mts.flatMap(({ transport: t }) => (t ? [t] : []))),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -212,7 +212,6 @@ export function mockEmitter<T>(): EmitterMock<T> {
|
|||||||
export const exampleTransport: LivekitTransport = {
|
export const exampleTransport: LivekitTransport = {
|
||||||
type: "livekit",
|
type: "livekit",
|
||||||
livekit_service_url: "https://lk.example.org",
|
livekit_service_url: "https://lk.example.org",
|
||||||
livekit_alias: "!alias:example.org",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const exampleSfuConfig: SFUConfig = {
|
export const exampleSfuConfig: SFUConfig = {
|
||||||
|
|||||||
@@ -65,8 +65,12 @@ export default defineConfig(({ mode }) => {
|
|||||||
},
|
},
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
// The host already has these, and a second copy of any of them does not
|
// 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
|
// merely bloat the bundle: React would hold two sets of hooks, the
|
||||||
// Matrix client would run two sync loops.
|
// 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
|
// 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
|
// 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/sync",
|
||||||
"matrix-js-sdk/lib/types",
|
"matrix-js-sdk/lib/types",
|
||||||
"matrix-js-sdk/lib/utils",
|
"matrix-js-sdk/lib/utils",
|
||||||
|
"@vector-im/compound-web",
|
||||||
|
"@vector-im/compound-design-tokens/assets/web/icons",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export default defineConfig((configEnv) =>
|
|||||||
"src/**/*.test.ts",
|
"src/**/*.test.ts",
|
||||||
"src/**/*.test.tsx",
|
"src/**/*.test.tsx",
|
||||||
"component/**/*.test.ts",
|
"component/**/*.test.ts",
|
||||||
|
"component/**/*.test.tsx",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user