Cleanup transport creation and local store hack.

This commit is contained in:
Timo K
2025-11-21 14:02:15 +01:00
parent 4099c4383d
commit a731981388
4 changed files with 90 additions and 21 deletions

View File

@@ -64,6 +64,7 @@
"developer_mode": { "developer_mode": {
"always_show_iphone_earpiece": "Show iPhone earpiece option on all platforms", "always_show_iphone_earpiece": "Show iPhone earpiece option on all platforms",
"crypto_version": "Crypto version: {{version}}", "crypto_version": "Crypto version: {{version}}",
"custom_livekit_url": "Custom Livekit-url",
"debug_tile_layout_label": "Debug tile layout", "debug_tile_layout_label": "Debug tile layout",
"device_id": "Device ID: {{id}}", "device_id": "Device ID: {{id}}",
"duplicate_tiles_label": "Number of additional tile copies per participant", "duplicate_tiles_label": "Number of additional tile copies per participant",
@@ -89,6 +90,7 @@
}, },
"mute_all_audio": "Mute all audio (participants, reactions, join sounds)", "mute_all_audio": "Mute all audio (participants, reactions, join sounds)",
"show_connection_stats": "Show connection statistics", "show_connection_stats": "Show connection statistics",
"update": "Update",
"url_params": "URL parameters" "url_params": "URL parameters"
}, },
"disconnected_banner": "Connectivity to the server has been lost.", "disconnected_banner": "Connectivity to the server has been lost.",

View File

@@ -11,8 +11,8 @@ import {
useCallback, useCallback,
useEffect, useEffect,
useMemo, useMemo,
useState,
useId, useId,
useState,
} from "react"; } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { import {
@@ -21,6 +21,7 @@ import {
} from "matrix-js-sdk"; } from "matrix-js-sdk";
import { logger } from "matrix-js-sdk/lib/logger"; import { logger } from "matrix-js-sdk/lib/logger";
import { import {
Button,
Root as Form, Root as Form,
Heading, Heading,
HelpMessage, HelpMessage,
@@ -38,6 +39,7 @@ import {
muteAllAudio as muteAllAudioSetting, muteAllAudio as muteAllAudioSetting,
alwaysShowIphoneEarpiece as alwaysShowIphoneEarpieceSetting, alwaysShowIphoneEarpiece as alwaysShowIphoneEarpieceSetting,
matrixRTCMode as matrixRTCModeSetting, matrixRTCMode as matrixRTCModeSetting,
customLivekitUrl as customLivekitUrlSetting,
MatrixRTCMode, MatrixRTCMode,
} from "./settings"; } from "./settings";
import type { Room as LivekitRoom } from "livekit-client"; import type { Room as LivekitRoom } from "livekit-client";
@@ -85,6 +87,12 @@ export const DeveloperSettingsTab: FC<Props> = ({ client, livekitRooms }) => {
alwaysShowIphoneEarpieceSetting, alwaysShowIphoneEarpieceSetting,
); );
const [customLivekitUrl, setCustomLivekitUrl] = useSetting(
customLivekitUrlSetting,
);
const [customLivekitUrlTextBuffer, setCustomLivekitUrlTextBuffer] =
useState("");
const [muteAllAudio, setMuteAllAudio] = useSetting(muteAllAudioSetting); const [muteAllAudio, setMuteAllAudio] = useSetting(muteAllAudioSetting);
const urlParams = useUrlParams(); const urlParams = useUrlParams();
@@ -200,6 +208,48 @@ export const DeveloperSettingsTab: FC<Props> = ({ client, livekitRooms }) => {
)} )}
/>{" "} />{" "}
</FieldRow> </FieldRow>
{/*// TODO this feels a bit off... There has to be better way to create the desired look.
Also the indent should be further to the left...*/}
<InlineField
name={t("developer_mode.custom_livekit_url")}
control={<></>}
>
<Label>{t("developer_mode.custom_livekit_url")}</Label>
<HelpMessage>
{customLivekitUrl === null
? "Use Default"
: `Current:${customLivekitUrl}`}
</HelpMessage>
</InlineField>
<FieldRow>
<InputField
id="customLivekitUrl"
type="text"
label={t("developer_mode.custom_livekit_url")}
value={customLivekitUrlTextBuffer}
onChange={useCallback(
(event: ChangeEvent<HTMLInputElement>): void => {
setCustomLivekitUrlTextBuffer(event.target.value);
},
[setCustomLivekitUrlTextBuffer],
)}
/>
<Button
onClick={useCallback(
(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
e.preventDefault();
setCustomLivekitUrl(
customLivekitUrlTextBuffer === ""
? null
: customLivekitUrlTextBuffer,
);
},
[setCustomLivekitUrl, customLivekitUrlTextBuffer],
)}
>
{t("developer_mode.update")}
</Button>
</FieldRow>
<Heading as="h3" type="body" weight="semibold" size="lg"> <Heading as="h3" type="body" weight="semibold" size="lg">
{t("developer_mode.matrixRTCMode.title")} {t("developer_mode.matrixRTCMode.title")}
</Heading> </Heading>

View File

@@ -134,3 +134,8 @@ export const matrixRTCMode = new Setting<MatrixRTCMode>(
"matrix-rtc-mode", "matrix-rtc-mode",
MatrixRTCMode.Legacy, MatrixRTCMode.Legacy,
); );
export const customLivekitUrl = new Setting<string | null>(
"custom-livekit-url",
null,
);

View File

@@ -13,8 +13,15 @@ import {
isLivekitTransportConfig, isLivekitTransportConfig,
} 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 { combineLatest, distinctUntilChanged, first, from, map } from "rxjs"; import {
import { logger } from "matrix-js-sdk/lib/logger"; combineLatest,
distinctUntilChanged,
first,
from,
map,
switchMap,
} from "rxjs";
import { logger as rootLogger } from "matrix-js-sdk/lib/logger";
import { AutoDiscovery } from "matrix-js-sdk/lib/autodiscovery"; import { AutoDiscovery } from "matrix-js-sdk/lib/autodiscovery";
import { type Behavior } from "../../Behavior.ts"; import { type Behavior } from "../../Behavior.ts";
@@ -26,6 +33,9 @@ import {
type OpenIDClientParts, type OpenIDClientParts,
} from "../../../livekit/openIDSFU.ts"; } from "../../../livekit/openIDSFU.ts";
import { areLivekitTransportsEqual } from "../remoteMembers/MatrixLivekitMembers.ts"; import { areLivekitTransportsEqual } from "../remoteMembers/MatrixLivekitMembers.ts";
import { customLivekitUrl } from "../../../settings/settings.ts";
const logger = rootLogger.getChild("[LocalTransport]");
/* /*
* It figures out “which LiveKit focus URL/alias the local user should use,” * It figures out “which LiveKit focus URL/alias the local user should use,”
@@ -78,14 +88,16 @@ export const createLocalTransport$ = ({
* @throws * @throws
*/ */
const preferredTransport$: Behavior<LivekitTransport | null> = scope.behavior( const preferredTransport$: Behavior<LivekitTransport | null> = scope.behavior(
from(makeTransport(client, roomId)), customLivekitUrl.value$.pipe(
switchMap((customUrl) => from(makeTransport(client, roomId, customUrl))),
),
null, null,
); );
/** /**
* The transport we should advertise in our MatrixRTC membership. * The chosen transport we should advertise in our MatrixRTC membership.
*/ */
const advertisedTransport$ = scope.behavior( return scope.behavior(
combineLatest([ combineLatest([
useOldestMember$, useOldestMember$,
oldestMemberTransport$, oldestMemberTransport$,
@@ -99,7 +111,6 @@ export const createLocalTransport$ = ({
distinctUntilChanged(areLivekitTransportsEqual), distinctUntilChanged(areLivekitTransportsEqual),
), ),
); );
return advertisedTransport$;
}; };
const FOCI_WK_KEY = "org.matrix.msc4143.rtc_foci"; const FOCI_WK_KEY = "org.matrix.msc4143.rtc_foci";
@@ -114,31 +125,30 @@ const FOCI_WK_KEY = "org.matrix.msc4143.rtc_foci";
async function makeTransport( async function makeTransport(
client: Pick<MatrixClient, "getDomain"> & OpenIDClientParts, client: Pick<MatrixClient, "getDomain"> & OpenIDClientParts,
roomId: string, roomId: string,
urlFromDevSettings: string | null,
): Promise<LivekitTransport> { ): Promise<LivekitTransport> {
let transport: LivekitTransport | undefined; let transport: LivekitTransport | undefined;
logger.log("Searching for a preferred transport"); logger.trace("Searching for a preferred transport");
//TODO refactor this to use the jwt service returned alias. //TODO refactor this to use the jwt service returned alias.
const livekitAlias = roomId; const livekitAlias = roomId;
// TODO-MULTI-SFU: Either remove this dev tool or make it more official
const urlFromStorage = // DEVTOOL: Highest priority: Load from devtool setting
localStorage.getItem("robin-matrixrtc-auth") ?? if (urlFromDevSettings !== null) {
localStorage.getItem("timo-focus-url");
if (urlFromStorage !== null) {
const transportFromStorage: LivekitTransport = { const transportFromStorage: LivekitTransport = {
type: "livekit", type: "livekit",
livekit_service_url: urlFromStorage, livekit_service_url: urlFromDevSettings,
livekit_alias: livekitAlias, livekit_alias: livekitAlias,
}; };
logger.log( logger.info(
"Using LiveKit transport from local storage: ", "Using LiveKit transport from dev tools: ",
transportFromStorage, transportFromStorage,
); );
transport = transportFromStorage; transport = transportFromStorage;
} }
// Prioritize the .well-known/matrix/client, if available, over the configured SFU // WELL_KNOWN: Prioritize the .well-known/matrix/client, if available, over the configured SFU
const domain = client.getDomain(); const domain = client.getDomain();
if (domain) { if (domain && transport === undefined) {
// we use AutoDiscovery instead of relying on the MatrixClient having already // we use AutoDiscovery instead of relying on the MatrixClient having already
// been fully configured and started // been fully configured and started
const wellKnownFoci = (await AutoDiscovery.getRawClientConfig(domain))?.[ const wellKnownFoci = (await AutoDiscovery.getRawClientConfig(domain))?.[
@@ -148,22 +158,24 @@ async function makeTransport(
const wellKnownTransport: LivekitTransportConfig | undefined = const wellKnownTransport: LivekitTransportConfig | undefined =
wellKnownFoci.find((f) => f && isLivekitTransportConfig(f)); wellKnownFoci.find((f) => f && isLivekitTransportConfig(f));
if (wellKnownTransport !== undefined) { if (wellKnownTransport !== undefined) {
logger.log("Using LiveKit transport from .well-known: ", transport); logger.info("Using LiveKit transport from .well-known: ", transport);
transport = { ...wellKnownTransport, livekit_alias: livekitAlias }; transport = { ...wellKnownTransport, livekit_alias: livekitAlias };
} }
} }
} }
// CONFIG: Least prioritized; Load from config file
const urlFromConf = Config.get().livekit?.livekit_service_url; const urlFromConf = Config.get().livekit?.livekit_service_url;
if (urlFromConf) { if (urlFromConf && transport === undefined) {
const transportFromConf: LivekitTransport = { const transportFromConf: LivekitTransport = {
type: "livekit", type: "livekit",
livekit_service_url: urlFromConf, livekit_service_url: urlFromConf,
livekit_alias: livekitAlias, livekit_alias: livekitAlias,
}; };
logger.log("Using LiveKit transport from config: ", transportFromConf); logger.info("Using LiveKit transport from config: ", transportFromConf);
transport = transportFromConf; transport = transportFromConf;
} }
if (!transport) throw new MatrixRTCTransportMissingError(domain ?? ""); // this will call the jwt/sfu/get endpoint to pre create the livekit room. if (!transport) throw new MatrixRTCTransportMissingError(domain ?? ""); // this will call the jwt/sfu/get endpoint to pre create the livekit room.
await getSFUConfigWithOpenID( await getSFUConfigWithOpenID(