small device selection refactor

This commit is contained in:
Timo K
2026-05-11 20:02:56 +02:00
parent d37c064df3
commit 9f6ae4ba55
+31 -42
View File
@@ -5,14 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE in the repository root for full details. Please see LICENSE in the repository root for full details.
*/ */
import { import { type ChangeEvent, type FC, useCallback, useId } from "react";
type ChangeEvent,
type FC,
type ReactElement,
type ReactNode,
useCallback,
useId,
} from "react";
import { import {
Heading, Heading,
InlineField, InlineField,
@@ -20,8 +13,8 @@ import {
RadioControl, RadioControl,
Separator, Separator,
} from "@vector-im/compound-web"; } from "@vector-im/compound-web";
import { Trans, useTranslation } from "react-i18next";
import { useObservableEagerState } from "observable-hooks"; import { useObservableEagerState } from "observable-hooks";
import { t } from "i18next";
import { import {
type AudioOutputDeviceLabel, type AudioOutputDeviceLabel,
@@ -37,12 +30,39 @@ interface Props {
numberedLabel: (number: number) => string; numberedLabel: (number: number) => string;
} }
export function mediaDeviceLabelToString(
label: DeviceLabel | AudioOutputDeviceLabel,
numberedLabel: (number: number) => string,
): string {
let labelText = "";
switch (label.type) {
case "name":
labelText = label.name;
break;
case "number":
labelText = numberedLabel(label.number);
break;
case "default":
labelText =
label.name === null
? t("settings.devices.default")
: t("settings.devices.default") + " (" + label.name + ")";
break;
case "speaker":
labelText = t("settings.devices.loudspeaker");
break;
case "earpiece":
labelText = t("settings.devices.handset");
break;
}
return labelText;
}
export const DeviceSelection: FC<Props> = ({ export const DeviceSelection: FC<Props> = ({
device, device,
title, title,
numberedLabel, numberedLabel,
}) => { }) => {
const { t } = useTranslation();
const groupId = useId(); const groupId = useId();
const available = useObservableEagerState(device.available$); const available = useObservableEagerState(device.available$);
const selectedId = useObservableEagerState(device.selected$)?.id; const selectedId = useObservableEagerState(device.selected$)?.id;
@@ -70,38 +90,7 @@ export const DeviceSelection: FC<Props> = ({
<Separator className={styles.separator} /> <Separator className={styles.separator} />
<div className={styles.options}> <div className={styles.options}>
{[...available].map(([id, label]) => { {[...available].map(([id, label]) => {
let labelText: ReactNode; const labelText = mediaDeviceLabelToString(label, numberedLabel);
switch (label.type) {
case "name":
labelText = label.name;
break;
case "number":
labelText = numberedLabel(label.number);
break;
case "default":
labelText =
label.name === null ? (
t("settings.devices.default")
) : (
<Trans
i18nKey="settings.devices.default_named"
name={label.name}
>
Default{" "}
<span className={styles.secondary}>
({{ name: label.name } as unknown as ReactElement})
</span>
</Trans>
);
break;
case "speaker":
labelText = t("settings.devices.loudspeaker");
break;
case "earpiece":
labelText = t("settings.devices.handset");
break;
}
return ( return (
<InlineField <InlineField
key={id} key={id}