/* Copyright 2022-2024 New Vector Ltd. SPDX-License-Identifier: AGPL-3.0-only Please see LICENSE in the repository root for full details. */ import { ChangeEvent, FC, ReactNode, useCallback } from "react"; import { Trans, useTranslation } from "react-i18next"; import { MatrixClient } from "matrix-js-sdk/src/matrix"; import { Dropdown, Text } from "@vector-im/compound-web"; import { Modal } from "../Modal"; import styles from "./SettingsModal.module.css"; import { Tab, TabContainer } from "../tabs/Tabs"; import { FieldRow, InputField } from "../input/Input"; import { AnalyticsNotice } from "../analytics/AnalyticsNotice"; import { ProfileSettingsTab } from "./ProfileSettingsTab"; import { FeedbackSettingsTab } from "./FeedbackSettingsTab"; import { useMediaDevices, MediaDevice, useMediaDeviceNames, } from "../livekit/MediaDevicesContext"; import { widget } from "../widget"; import { useSetting, developerSettingsTab as developerSettingsTabSetting, duplicateTiles as duplicateTilesSetting, useOptInAnalytics, } from "./settings"; import { isFirefox } from "../Platform"; type SettingsTab = | "audio" | "video" | "profile" | "feedback" | "more" | "developer"; interface Props { open: boolean; onDismiss: () => void; tab: SettingsTab; onTabChange: (tab: SettingsTab) => void; client: MatrixClient; roomId?: string; } export const defaultSettingsTab: SettingsTab = "audio"; export const SettingsModal: FC = ({ open, onDismiss, tab, onTabChange, client, roomId, }) => { const { t } = useTranslation(); const [optInAnalytics, setOptInAnalytics] = useOptInAnalytics(); const [developerSettingsTab, setDeveloperSettingsTab] = useSetting( developerSettingsTabSetting, ); const [duplicateTiles, setDuplicateTiles] = useSetting(duplicateTilesSetting); // Generate a `SelectInput` with a list of devices for a given device kind. const generateDeviceSelection = ( devices: MediaDevice, caption: string, ): ReactNode => { if (devices.available.length == 0) return null; const values = devices.available.map( ({ deviceId, label }, index) => [ deviceId, !!label && label.trim().length > 0 ? label : `${caption} ${index + 1}`, ] as [string, string], ); return ( devices.select(id)} values={values} // XXX This is unused because we set a defaultValue. The component // shouldn't require this prop. placeholder="" /> ); }; const optInDescription = (
You may withdraw consent by unchecking this box. If you are currently in a call, this setting will take effect at the end of the call.
); const devices = useMediaDevices(); useMediaDeviceNames(devices, open); const audioTab: Tab = { key: "audio", name: t("common.audio"), content: ( <> {generateDeviceSelection(devices.audioInput, t("common.microphone"))} {!isFirefox() && generateDeviceSelection( devices.audioOutput, t("settings.speaker_device_selection_label"), )} ), }; const videoTab: Tab = { key: "video", name: t("common.video"), content: generateDeviceSelection(devices.videoInput, t("common.camera")), }; const profileTab: Tab = { key: "profile", name: t("common.profile"), content: , }; const feedbackTab: Tab = { key: "feedback", name: t("settings.feedback_tab_title"), content: , }; const moreTab: Tab = { key: "more", name: t("settings.more_tab_title"), content: ( <>

{t("settings.developer_tab_title")}

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

): void => setDeveloperSettingsTab(event.target.checked) } />

{t("common.analytics")}

): void => { setOptInAnalytics?.(event.target.checked); }} /> ), }; const developerTab: Tab = { key: "developer", name: t("settings.developer_tab_title"), content: ( <>

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

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

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

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

): void => { const value = event.target.valueAsNumber; setDuplicateTiles(Number.isNaN(value) ? 0 : value); }, [setDuplicateTiles], )} /> ), }; const tabs = [audioTab, videoTab]; if (widget === null) tabs.push(profileTab); tabs.push(feedbackTab, moreTab); if (developerSettingsTab) tabs.push(developerTab); return ( ); };