/* 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, useState } from "react"; import { Trans, useTranslation } from "react-i18next"; import { MatrixClient } from "matrix-js-sdk/src/matrix"; import { Root as Form, Separator, Text } from "@vector-im/compound-web"; import { BackgroundBlur as backgroundBlur } from "@livekit/track-processors"; import { logger } from "matrix-js-sdk/src/logger"; 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, useMediaDeviceNames, } from "../livekit/MediaDevicesContext"; import { widget } from "../widget"; import { useSetting, developerSettingsTab as developerSettingsTabSetting, duplicateTiles as duplicateTilesSetting, backgroundBlur as backgroundBlurSetting, useOptInAnalytics, soundEffectVolumeSetting, } from "./settings"; import { isFirefox } from "../Platform"; import { PreferencesSettingsTab } from "./PreferencesSettingsTab"; import { Slider } from "../Slider"; import { DeviceSelection } from "./DeviceSelection"; type SettingsTab = | "audio" | "video" | "profile" | "preferences" | "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 `Checkbox` input to turn blur on or off. const BlurCheckbox: React.FC = (): ReactNode => { const [blur, setBlur] = useSetting(backgroundBlurSetting); let canBlur = true; try { backgroundBlur(15); } catch (e) { logger.debug( "Cannot blur, so we do not show the option in settings. error: ", e, ); canBlur = false; setBlur(false); } return ( <>

{t("settings.background_blur_header")}

setBlur(b.target.checked)} disabled={!canBlur} /> ); }; 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 [soundVolume, setSoundVolume] = useSetting(soundEffectVolumeSetting); const [soundVolumeRaw, setSoundVolumeRaw] = useState(soundVolume); const audioTab: Tab = { key: "audio", name: t("common.audio"), content: ( <>
{!isFirefox() && ( )}

{t("settings.audio_tab.effect_volume_description")}

), }; const videoTab: Tab = { key: "video", name: t("common.video"), content: ( <>
), }; const preferencesTab: Tab = { key: "preferences", name: t("common.preferences"), content: , }; 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("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], )} /> ), }; const tabs = [audioTab, videoTab]; if (widget === null) tabs.push(profileTab); tabs.push(preferencesTab, feedbackTab, moreTab); if (developerSettingsTab) tabs.push(developerTab); return ( ); };