From 289260531bc606fc6d52ee44b8c874338caafa27 Mon Sep 17 00:00:00 2001 From: Robin Date: Tue, 13 Oct 2026 12:59:15 +0200 Subject: [PATCH] Let developers override the whole transport The developer setting that overrode the LiveKit service URL becomes a JSON transport, so that a `url`-only or combined transport can be tested against a homeserver that does not advertise one yet. The settings modal shows each LiveKit room's role instead of a never-populated isLocal flag. Folds "Avoid null in transport dev setting" from the same PR. The setting feeds the SDK's transport option; the config fallback becomes a transport the host builds. Co-authored-by: Johannes Marbach --- src/room/InCallView.tsx | 9 +++-- src/settings/DeveloperSettingsTab.test.tsx | 17 +++++----- src/settings/DeveloperSettingsTab.tsx | 39 ++++++++++++---------- 3 files changed, 36 insertions(+), 29 deletions(-) diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index d73fb6d9d..71d32a406 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -83,7 +83,7 @@ import { cameraCodec, cameraFramerate, cameraResolution, - customLivekitUrl, + customTransport, echoCancellationSetting, matrixRTCMode as matrixRTCModeSetting, noiseSuppressionSetting, @@ -823,7 +823,10 @@ export function rtcParticipationOptions( delegatedDelayedLeave: session.delegated_delayed_leave, }, mediaQuality: config.media_quality, - transportUrl: customLivekitUrl.value$.value ?? undefined, - fallbackTransportUrl: config.livekit?.livekit_service_url, + transport: customTransport.value$.value ?? undefined, + fallbackTransport: config.livekit && { + type: "livekit", + livekit_service_url: config.livekit.livekit_service_url, + }, }; } diff --git a/src/settings/DeveloperSettingsTab.test.tsx b/src/settings/DeveloperSettingsTab.test.tsx index 44aaa9155..6cd645faf 100644 --- a/src/settings/DeveloperSettingsTab.test.tsx +++ b/src/settings/DeveloperSettingsTab.test.tsx @@ -29,7 +29,7 @@ import { MatrixRTCMode, } from "@element-hq/matrixrtc-sdk"; import type * as MatrixRTCSdk from "@element-hq/matrixrtc-sdk"; -import { mockConfig } from "../utils/test"; +import { exampleTransport, mockConfig } from "../utils/test"; // Mock url params hook to avoid environment-dependent snapshot churn. vi.mock("../UrlParams", () => ({ @@ -232,10 +232,12 @@ describe("DeveloperSettingsTab", () => { const saveButton = screen.getByRole("button", { name: "Save" }); await user.click(saveButton); expect(authenticateWithTransport).toHaveBeenCalledWith( - expect.anything(), - expect.anything(), - "wss://example.livekit.valid", - "#testRoom", + expect.objectContaining({ + transport: { type: "livekit", url: "wss://example.livekit.valid" }, + serverName: "hs", + roomId: "#testRoom", + role: "publisher", + }), ); expect(customTransportSetting.getValue()).toEqual({ @@ -266,7 +268,7 @@ describe("DeveloperSettingsTab", () => { const saveButton = screen.getByRole("button", { name: "Save" }); (authenticateWithTransport as Mock).mockImplementation(() => { - throw new Error("Invalid URL"); + throw new Error("Failed to get SFU config"); }); await user.click(saveButton); expect( @@ -276,9 +278,6 @@ describe("DeveloperSettingsTab", () => { }); }); - // Add this test inside the describe("DeveloperSettingsTab", () => { block, - // after the custom transport tests: - describe("enable extended livekit logs", () => { afterEach(() => { enableExtendedLivekitLogsSetting.setValue(false); diff --git a/src/settings/DeveloperSettingsTab.tsx b/src/settings/DeveloperSettingsTab.tsx index 0642db575..b43c6ff0d 100644 --- a/src/settings/DeveloperSettingsTab.tsx +++ b/src/settings/DeveloperSettingsTab.tsx @@ -20,6 +20,7 @@ import { type MatrixClient, } from "matrix-js-sdk"; import { logger } from "matrix-js-sdk/lib/logger"; +import { type UnstableLivekitTransport } from "matrix-js-sdk/lib/matrixrtc"; import { EditInPlace, ErrorMessage, @@ -173,17 +174,17 @@ export const DeveloperSettingsTab: FC = ({ enableExtendedLivekitLogsSetting, ); - const [customTransportUpdateError, setCustomLivekitUrlUpdateError] = useState< + const [customTransportUpdateError, setCustomTransportUpdateError] = useState< string | null >(null); const [customTransport, setCustomTransport] = useSetting( customTransportSetting, ); - const [customTransportTextBuffer, setCustomLivekitUrlTextBuffer] = useState( + const [customTransportTextBuffer, setCustomTransportTextBuffer] = useState( () => transportText(customTransport), ); useEffect(() => { - setCustomLivekitUrlTextBuffer(transportText(customTransport)); + setCustomTransportTextBuffer(transportText(customTransport)); }, [customTransport]); const [muteAllAudio, setMuteAllAudio] = useSetting(muteAllAudioSetting); @@ -499,11 +500,7 @@ export const DeveloperSettingsTab: FC = ({ cancelButtonLabel={t("developer_mode.custom_transport.reset")} onSave={useCallback( async (e: React.FormEvent): Promise => { - if ( - roomId === undefined || - customTransportTextBuffer === "" || - customTransportTextBuffer === null - ) { + if (roomId === undefined || customTransportTextBuffer === "") { setCustomTransport(null); return; } @@ -511,36 +508,39 @@ export const DeveloperSettingsTab: FC = ({ try { const userId = client.getUserId(); const deviceId = client.getDeviceId(); + const serverName = client.getDomain(); - if (userId === null || deviceId === null) { - throw new Error("Invalid user or device ID"); + if (userId === null || deviceId === null || serverName === null) { + throw new Error("Invalid user, device ID or server name"); } - await authenticateWithTransport( + const transport = JSON.parse(customTransportTextBuffer); + // Validate the transport by attempting to get a token for it + await authenticateWithTransport({ client, membership: { userId, deviceId, memberId: "" }, transport, - serverName: client.getDomain()!, + serverName, roomId, slotId: "org.example.fake-slot#room", role: "publisher", logger, }); - setCustomLivekitUrlUpdateError(null); + setCustomTransportUpdateError(null); setCustomTransport(transport); } catch { - setCustomLivekitUrlUpdateError( + setCustomTransportUpdateError( "invalid transport (did not update)", ); } }, [customTransportTextBuffer, setCustomTransport, client, roomId], )} - value={customTransportTextBuffer ?? ""} + value={customTransportTextBuffer} onChange={useCallback( (event: ChangeEvent): void => { - setCustomLivekitUrlTextBuffer(event.target.value); + setCustomTransportTextBuffer(event.target.value); }, - [setCustomLivekitUrlTextBuffer], + [setCustomTransportTextBuffer], )} onCancel={useCallback( (e: React.FormEvent) => { @@ -659,6 +659,11 @@ export const DeveloperSettingsTab: FC = ({ ); }; +/** The text shown in the custom transport field for a given setting value. */ +function transportText(transport: UnstableLivekitTransport | null): string { + return transport === null ? "" : JSON.stringify(transport); +} + const TransportBox: FC = ({ transport, local }) => { const { t } = useTranslation(); const resolved = useBehavior(transport.resolved$);