use previous device selection logic

This commit is contained in:
Timo K
2026-05-18 19:28:53 +02:00
parent 68a487c9de
commit bc81da43c2
3 changed files with 61 additions and 33 deletions

View File

@@ -204,7 +204,6 @@
"change_device_button": "Change audio device", "change_device_button": "Change audio device",
"default": "Default", "default": "Default",
"default_named": "Default <2>({{name}})</2>", "default_named": "Default <2>({{name}})</2>",
"default_numbered": "Default {{n}}",
"handset": "Handset", "handset": "Handset",
"loudspeaker": "Loudspeaker", "loudspeaker": "Loudspeaker",
"microphone": "Microphone", "microphone": "Microphone",

View File

@@ -114,14 +114,19 @@ export const MediaMuteAndSwitchButton: FC<MediaMuteAndSwitchButtonProps> = ({
let IconOptions: ComponentType<React.SVGAttributes<SVGElement>> | undefined; let IconOptions: ComponentType<React.SVGAttributes<SVGElement>> | undefined;
let optionsButtonLabel: string; let optionsButtonLabel: string;
let numberedLabel: (number: number) => string;
switch (iconsAndLabels) { switch (iconsAndLabels) {
case "video": case "video":
IconOptions = VideoCallIcon; IconOptions = VideoCallIcon;
optionsButtonLabel = t("settings.devices.camera"); optionsButtonLabel = t("settings.devices.camera");
numberedLabel = (n): string =>
t("settings.devices.microphone_numbered", { n });
break; break;
case "audio": case "audio":
IconOptions = MicOnIcon; IconOptions = MicOnIcon;
optionsButtonLabel = t("settings.devices.microphone"); optionsButtonLabel = t("settings.devices.microphone");
numberedLabel = (n): string =>
t("settings.devices.camera_numbered", { n });
break; break;
} }
return ( return (
@@ -154,16 +159,15 @@ export const MediaMuteAndSwitchButton: FC<MediaMuteAndSwitchButtonProps> = ({
} }
> >
{options?.map(({ label, id }) => { {options?.map(({ label, id }) => {
const labelText = ((): string => { let labelText: string;
switch (label.type) { switch (label.type) {
case "name": case "name":
return label.name; labelText = label.name;
case "number": break;
return t("settings.devices.default_numbered", { case "number":
n: label.number, labelText = numberedLabel(label.number);
}); break;
} }
})();
return ( return (
<MenuItem <MenuItem
hideChevron hideChevron

View File

@@ -5,7 +5,14 @@ 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 { type ChangeEvent, type FC, useCallback, useId } from "react"; import {
type ChangeEvent,
type FC,
type ReactElement,
type ReactNode,
useCallback,
useId,
} from "react";
import { import {
Heading, Heading,
InlineField, InlineField,
@@ -13,8 +20,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 { useTranslation } from "react-i18next";
import { import {
type AudioOutputDeviceLabel, type AudioOutputDeviceLabel,
@@ -30,11 +37,15 @@ interface Props {
numberedLabel: (number: number) => string; numberedLabel: (number: number) => string;
} }
export const DeviceSelection: FC<Props> = ({ device, title }) => { export const DeviceSelection: FC<Props> = ({
device,
title,
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;
const { t } = useTranslation();
const onChange = useCallback( const onChange = useCallback(
(e: ChangeEvent<HTMLInputElement>) => { (e: ChangeEvent<HTMLInputElement>) => {
device.select(e.target.value); device.select(e.target.value);
@@ -59,24 +70,38 @@ export const DeviceSelection: FC<Props> = ({ device, title }) => {
<Separator className={styles.separator} /> <Separator className={styles.separator} />
<div className={styles.options}> <div className={styles.options}>
{[...available].map(([id, label]) => { {[...available].map(([id, label]) => {
const labelText = ((): string => { let labelText: ReactNode;
switch (label.type) { switch (label.type) {
case "name": case "name":
return label.name; labelText = label.name;
case "number": break;
return t("settings.devices.default_numbered", { case "number":
n: label.number, labelText = numberedLabel(label.number);
}); break;
case "default": case "default":
return label.name === null labelText =
? t("settings.devices.default") label.name === null ? (
: t("settings.devices.default_named", label.name); t("settings.devices.default")
case "speaker": ) : (
return t("settings.devices.loudspeaker"); <Trans
case "earpiece": i18nKey="settings.devices.default_named"
return t("settings.devices.handset"); 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}