diff --git a/.gitignore b/.gitignore index 4233ecbeb..3831c2ca4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ node_modules .DS_Store .env dist +dist-embedded dist-ssr *.local *.bkp diff --git a/README.md b/README.md index 2cbe2d127..213eb4f04 100644 --- a/README.md +++ b/README.md @@ -361,6 +361,33 @@ pnpm backend # podman-compose -f docker-compose-dev.yml up ``` +The two Element Web instances load the Element Call widget from +`./dist-embedded`, which is mounted over the copy bundled in the Element Web +image. Build it from the current sources before starting the backend, and keep +it rebuilding on change: + +```sh +VITE_MATRIX_RTC_MODE=matrix_2_0 pnpm build:embedded:development --outDir dist-embedded --watch +``` + +Build it at least once before the first `pnpm backend`, since Docker otherwise +creates an empty `dist-embedded` directory that the build cannot write to. +Reload the page after a rebuild to pick up the new widget. The widget has no +`config.json` in this setup, so `VITE_MATRIX_RTC_MODE` sets the default +MatrixRTC mode of the build instead (see +[docs/matrix_rtc_modes.md](docs/matrix_rtc_modes.md)); it can still be changed +under Settings → Developer. Do not delete `dist-embedded` while the backend is +running: the containers hold a mount of the directory, and a new directory of +the same name is not picked up until they are recreated. + +> [!NOTE] +> When Docker runs in a VM (colima, Docker Desktop), the SFUs' container IPs +> are not routable from the host, so they advertise `127.0.0.1` as their ICE +> candidate instead and the browser reaches them through the published ports. +> This only works in Chromium-based browsers: Firefox ignores loopback +> candidates, and calls drop about fifteen seconds after joining. Media runs +> over ICE-TCP, since the UDP port ranges are not forwarded out of the VM. + > [!NOTE] > To ensure your local development frontend functions properly, you’ll need to > add certificate exceptions in your browser for `https://localhost:3000` and diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index 2db9a63a0..a767b9916 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -157,6 +157,9 @@ services: pull_policy: always volumes: - ./backend/ew.test.config.json:/app/config.json:Z + # Replaces the Element Call widget bundled in the image with the embedded + # build of the current sources. See "Backend" in README.md. + - ./dist-embedded:/app/widgets/element-call:ro,Z environment: ELEMENT_WEB_PORT: 8081 ports: @@ -169,6 +172,9 @@ services: pull_policy: always volumes: - ./backend/ew.test.othersite.config.json:/app/config.json:Z + # Replaces the Element Call widget bundled in the image with the embedded + # build of the current sources. See "Backend" in README.md. + - ./dist-embedded:/app/widgets/element-call:ro,Z environment: ELEMENT_WEB_PORT: 18081 ports: diff --git a/docs/matrix_rtc_modes.md b/docs/matrix_rtc_modes.md index 4f2e864c4..f6b1f94bc 100644 --- a/docs/matrix_rtc_modes.md +++ b/docs/matrix_rtc_modes.md @@ -46,3 +46,11 @@ Valid values are `compatibility` and `matrix_2_0`; an invalid value is ignored (with a warning) and the user's choice applies. Pinning `matrix_2_0` on a homeserver without sticky event support makes joining fail with a "sticky events required" error. + +A build can change the default mode, for setups without a `config.json` by setting +`VITE_MATRIX_RTC_MODE` at build time: + +```sh +VITE_MATRIX_RTC_MODE=matrix_2_0 pnpm build:embedded +``` + diff --git a/src/settings/settings.test.ts b/src/settings/settings.test.ts index 5280e58ea..b02306bb4 100644 --- a/src/settings/settings.test.ts +++ b/src/settings/settings.test.ts @@ -5,12 +5,15 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE in the repository root for full details. */ -import { describe, expect, it, beforeEach } from "vitest"; +import { describe, expect, it, beforeEach, vi } from "vitest"; +import { logger } from "matrix-js-sdk/lib/logger"; +import { MatrixRTCMode } from "../config/ConfigOptions"; import { Setting, parseResolution, seedSettingsFromConfig, + defaultMatrixRTCMode, screenShareCodec, screenShareResolution, screenShareFramerate, @@ -38,6 +41,28 @@ describe("parseResolution", () => { }); }); +describe("defaultMatrixRTCMode", () => { + it("is Compatibility when the build sets nothing", () => { + expect(defaultMatrixRTCMode(undefined)).toBe(MatrixRTCMode.Compatibility); + }); + + it.each([MatrixRTCMode.Compatibility, MatrixRTCMode.Matrix_2_0])( + "takes a valid build value (%s)", + (mode) => { + expect(defaultMatrixRTCMode(mode)).toBe(mode); + }, + ); + + it("ignores an invalid build value and warns", () => { + const warn = vi.spyOn(logger, "warn").mockImplementation(() => {}); + expect(defaultMatrixRTCMode("nonsense")).toBe(MatrixRTCMode.Compatibility); + expect(warn).toHaveBeenCalledWith( + expect.stringContaining("VITE_MATRIX_RTC_MODE"), + ); + warn.mockRestore(); + }); +}); + describe("Setting", () => { describe("seedFromConfig", () => { it("updates value when no localStorage value exists", () => { diff --git a/src/settings/settings.ts b/src/settings/settings.ts index 6b4029044..f3c2f726e 100644 --- a/src/settings/settings.ts +++ b/src/settings/settings.ts @@ -149,9 +149,20 @@ export const enableExtendedLivekitLogs = new Setting( false, ); +export function defaultMatrixRTCMode( + buildValue: string | undefined = import.meta.env.VITE_MATRIX_RTC_MODE, +): MatrixRTCMode { + if (buildValue === undefined) return MatrixRTCMode.Compatibility; + if (Object.values(MatrixRTCMode).includes(buildValue)) { + return buildValue as MatrixRTCMode; + } + logger.warn(`Ignoring invalid VITE_MATRIX_RTC_MODE: ${buildValue}`); + return MatrixRTCMode.Compatibility; +} + export const matrixRTCMode = new Setting( "matrix-rtc-mode", - MatrixRTCMode.Compatibility, + defaultMatrixRTCMode(), ); export const customTransport = new Setting(