diff --git a/src/settings/DeviceSelection.module.css b/src/settings/DeviceSelection.module.css new file mode 100644 index 00000000..daa4510e --- /dev/null +++ b/src/settings/DeviceSelection.module.css @@ -0,0 +1,18 @@ +.selection { + gap: 0; +} + +.title { + color: var(--cpd-color-text-secondary); + margin-block: var(--cpd-space-3x) 0; +} + +.separator { + margin-block: 6px var(--cpd-space-4x); +} + +.options { + display: flex; + flex-direction: column; + gap: var(--cpd-space-4x); +} diff --git a/src/settings/DeviceSelection.tsx b/src/settings/DeviceSelection.tsx new file mode 100644 index 00000000..005973a0 --- /dev/null +++ b/src/settings/DeviceSelection.tsx @@ -0,0 +1,71 @@ +/* +Copyright 2024 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only +Please see LICENSE in the repository root for full details. +*/ + +import { ChangeEvent, FC, useCallback, useId } from "react"; +import { + Heading, + InlineField, + Label, + RadioControl, + Separator, +} from "@vector-im/compound-web"; + +import { MediaDevice } from "../livekit/MediaDevicesContext"; +import styles from "./DeviceSelection.module.css"; + +interface Props { + devices: MediaDevice; + caption: string; +} + +export const DeviceSelection: FC = ({ devices, caption }) => { + const groupId = useId(); + const onChange = useCallback( + (e: ChangeEvent) => { + devices.select(e.target.value); + }, + [devices], + ); + + if (devices.available.length == 0) return null; + + return ( +
+ + {caption} + + +
+ {devices.available.map(({ deviceId, label }, index) => ( + + } + > + + + ))} +
+
+ ); +}; diff --git a/src/settings/SettingsModal.tsx b/src/settings/SettingsModal.tsx index 07ca5753..78afc2c5 100644 --- a/src/settings/SettingsModal.tsx +++ b/src/settings/SettingsModal.tsx @@ -5,10 +5,10 @@ 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 { ChangeEvent, FC, useCallback } from "react"; import { Trans, useTranslation } from "react-i18next"; import { MatrixClient } from "matrix-js-sdk/src/matrix"; -import { Dropdown, Separator, Text } from "@vector-im/compound-web"; +import { Root as Form, Text } from "@vector-im/compound-web"; import { Modal } from "../Modal"; import styles from "./SettingsModal.module.css"; @@ -19,7 +19,6 @@ import { ProfileSettingsTab } from "./ProfileSettingsTab"; import { FeedbackSettingsTab } from "./FeedbackSettingsTab"; import { useMediaDevices, - MediaDevice, useMediaDeviceNames, } from "../livekit/MediaDevicesContext"; import { widget } from "../widget"; @@ -33,6 +32,7 @@ import { import { isFirefox } from "../Platform"; import { PreferencesSettingsTab } from "./PreferencesSettingsTab"; import { Slider } from "../Slider"; +import { DeviceSelection } from "./DeviceSelection"; type SettingsTab = | "audio" @@ -70,40 +70,6 @@ export const SettingsModal: FC = ({ ); 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 = ( @@ -125,25 +91,30 @@ export const SettingsModal: FC = ({ name: t("common.audio"), content: ( <> - {generateDeviceSelection(devices.audioInput, t("common.microphone"))} - {!isFirefox() && - generateDeviceSelection( - devices.audioOutput, - t("settings.speaker_device_selection_label"), - )} - -
- -

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

- + -
+ {!isFirefox() && ( + + )} +
+ +

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

+ +
+ ), }; @@ -151,7 +122,14 @@ export const SettingsModal: FC = ({ const videoTab: Tab = { key: "video", name: t("common.video"), - content: generateDeviceSelection(devices.videoInput, t("common.camera")), + content: ( +
+ + + ), }; const preferencesTab: Tab = {