/* Copyright 2022-2024 New Vector Ltd. SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE in the repository root for full details. */ import { type ChangeEvent, type FC, useCallback, useEffect, useMemo, useId, useState, } from "react"; import { useTranslation } from "react-i18next"; import { UNSTABLE_MSC4354_STICKY_EVENTS, type MatrixClient, } from "matrix-js-sdk"; import { logger } from "matrix-js-sdk/lib/logger"; import { EditInPlace, ErrorMessage, Root as Form, Heading, HelpMessage, InlineField, Label, RadioControl, } from "@vector-im/compound-web"; import { type Room as LivekitRoom } from "livekit-client"; import { FieldRow, InputField } from "../input/Input"; import { useSetting, duplicateTiles as duplicateTilesSetting, debugTileLayout as debugTileLayoutSetting, showConnectionStats as showConnectionStatsSetting, muteAllAudio as muteAllAudioSetting, alwaysShowIphoneEarpiece as alwaysShowIphoneEarpieceSetting, matrixRTCMode as matrixRTCModeSetting, customLivekitUrl as customLivekitUrlSetting, MatrixRTCMode, } from "./settings"; import styles from "./DeveloperSettingsTab.module.css"; import { useUrlParams } from "../UrlParams"; import { getSFUConfigWithOpenID } from "../livekit/openIDSFU"; interface Props { client: MatrixClient; roomId?: string; livekitRooms?: { room: LivekitRoom; url: string; isLocal?: boolean; livekitAlias?: string; }[]; env: ImportMetaEnv; } export const DeveloperSettingsTab: FC = ({ client, livekitRooms, roomId, env, }) => { const { t } = useTranslation(); const [duplicateTiles, setDuplicateTiles] = useSetting(duplicateTilesSetting); const [debugTileLayout, setDebugTileLayout] = useSetting( debugTileLayoutSetting, ); const [stickyEventsSupported, setStickyEventsSupported] = useState(false); useEffect(() => { client .doesServerSupportUnstableFeature(UNSTABLE_MSC4354_STICKY_EVENTS) .then((result) => { setStickyEventsSupported(result); }) .catch((ex) => { logger.warn("Failed to check if sticky events are supported", ex); }); }, [client]); const [matrixRTCMode, setMatrixRTCMode] = useSetting(matrixRTCModeSetting); const matrixRTCModeRadioGroup = useId(); const onMatrixRTCModeChange = useCallback( (e: ChangeEvent) => { setMatrixRTCMode(e.target.value as MatrixRTCMode); }, [setMatrixRTCMode], ); const [showConnectionStats, setShowConnectionStats] = useSetting( showConnectionStatsSetting, ); const [alwaysShowIphoneEarpiece, setAlwaysShowIphoneEarpiece] = useSetting( alwaysShowIphoneEarpieceSetting, ); const [customLivekitUrlUpdateError, setCustomLivekitUrlUpdateError] = useState(null); const [customLivekitUrl, setCustomLivekitUrl] = useSetting( customLivekitUrlSetting, ); const [customLivekitUrlTextBuffer, setCustomLivekitUrlTextBuffer] = useState(customLivekitUrl); useEffect(() => { setCustomLivekitUrlTextBuffer(customLivekitUrl); }, [customLivekitUrl]); const [muteAllAudio, setMuteAllAudio] = useSetting(muteAllAudioSetting); const urlParams = useUrlParams(); const localSfuUrl = useMemo((): URL | null => { const localRoom = livekitRooms?.find((r) => r.isLocal)?.room; if (localRoom?.engine.client.ws?.url) { // strip the URL params const url = new URL(localRoom.engine.client.ws.url); url.search = ""; return url; } return null; }, [livekitRooms]); return ( <>

{t("developer_mode.hostname", { hostname: window.location.hostname || "unknown", })}

{t("version", { productName: import.meta.env.VITE_PRODUCT_NAME || "Element Call", version: import.meta.env.VITE_APP_VERSION || "dev", })}

{t("developer_mode.crypto_version", { version: client.getCrypto()?.getVersion() || "unknown", })}

{t("developer_mode.matrix_id", { id: client.getUserId() || "unknown", })}

{t("developer_mode.device_id", { id: client.getDeviceId() || "unknown", })}

): void => { const value = event.target.valueAsNumber; if (value < 0) { return; } setDuplicateTiles(Number.isNaN(value) ? 0 : value); }, [setDuplicateTiles], )} /> ): void => setDebugTileLayout(event.target.checked) } /> ): void => { setShowConnectionStats(event.target.checked); }, [setShowConnectionStats], )} /> ): void => { setMuteAllAudio(event.target.checked); }, [setMuteAllAudio], )} /> {" "} ): void => { setAlwaysShowIphoneEarpiece(event.target.checked); }, [setAlwaysShowIphoneEarpiece], )} />{" "} e.preventDefault()} helpLabel={ customLivekitUrl === null ? t("developer_mode.custom_livekit_url.from_config") : t("developer_mode.custom_livekit_url.current_url") + customLivekitUrl } label={t("developer_mode.custom_livekit_url.label")} saveButtonLabel={t("developer_mode.custom_livekit_url.save")} savingLabel={t("developer_mode.custom_livekit_url.saving")} cancelButtonLabel={t("developer_mode.custom_livekit_url.reset")} onSave={useCallback( async (e: React.FormEvent): Promise => { if ( roomId === undefined || customLivekitUrlTextBuffer === "" || customLivekitUrlTextBuffer === null ) { setCustomLivekitUrl(null); return; } try { const userId = client.getUserId(); const deviceId = client.getDeviceId(); if (userId === null || deviceId === null) { throw new Error("Invalid user or device ID"); } await getSFUConfigWithOpenID( client, { userId, deviceId, memberId: "" }, customLivekitUrlTextBuffer, roomId, ); setCustomLivekitUrlUpdateError(null); setCustomLivekitUrl(customLivekitUrlTextBuffer); } catch { setCustomLivekitUrlUpdateError("invalid URL (did not update)"); } }, [customLivekitUrlTextBuffer, setCustomLivekitUrl, client, roomId], )} value={customLivekitUrlTextBuffer ?? ""} onChange={useCallback( (event: ChangeEvent): void => { setCustomLivekitUrlTextBuffer(event.target.value); }, [setCustomLivekitUrlTextBuffer], )} onCancel={useCallback( (e: React.FormEvent) => { setCustomLivekitUrl(null); }, [setCustomLivekitUrl], )} serverInvalid={customLivekitUrlUpdateError !== null} > {customLivekitUrlUpdateError !== null && ( {customLivekitUrlUpdateError} )} {t("developer_mode.matrixRTCMode.title")}
} > {t("developer_mode.matrixRTCMode.Legacy.description")} } > {t("developer_mode.matrixRTCMode.Comptibility.description")} } > {t("developer_mode.matrixRTCMode.Matrix_2_0.description")}
{livekitRooms?.map((livekitRoom) => (

{t("developer_mode.livekit_sfu", { url: livekitRoom.url || "unknown", })}

LivekitAlias: {livekitRoom.livekitAlias}

{livekitRoom.isLocal &&

ws-url: {localSfuUrl?.href}

}

{t("developer_mode.livekit_server_info")}( {livekitRoom.isLocal ? "local" : "remote"})

            {livekitRoom.room.serverInfo
              ? JSON.stringify(livekitRoom.room.serverInfo, null, 2)
              : "undefined"}
            {livekitRoom.room.metadata}
          

Local Participant

            {livekitRoom.room.localParticipant.identity}
          

Remote Participants

    {Array.from(livekitRoom.room.remoteParticipants.keys()).map( (id) => (
  • {id}
  • ), )}
))}

{t("developer_mode.environment_variables")}

{JSON.stringify(env, null, 2)}

{t("developer_mode.url_params")}

{JSON.stringify(urlParams, null, 2)}
); };