Annotate the default device with a label

This commit is contained in:
Robin
2024-12-13 15:22:44 -05:00
parent 9c57720852
commit de276b1fc3
4 changed files with 62 additions and 25 deletions

View File

@@ -155,6 +155,7 @@
"camera": "Camera", "camera": "Camera",
"camera_numbered": "Camera {{n}}", "camera_numbered": "Camera {{n}}",
"default": "Default", "default": "Default",
"default_named": "Default <2>({{name}})</2>",
"microphone": "Microphone", "microphone": "Microphone",
"microphone_numbered": "Microphone {{n}}", "microphone_numbered": "Microphone {{n}}",
"speaker": "Speaker", "speaker": "Speaker",

View File

@@ -31,7 +31,7 @@ import {
export type DeviceLabel = export type DeviceLabel =
| { type: "name"; name: string } | { type: "name"; name: string }
| { type: "number"; number: number } | { type: "number"; number: number }
| { type: "default" }; | { type: "default"; name: string | null };
export interface MediaDevice { export interface MediaDevice {
/** /**
@@ -104,7 +104,10 @@ function useMediaDevice(
!available.has("") && !available.has("") &&
!available.has("default") !available.has("default")
) )
available = new Map([["", { type: "default" }], ...available]); available = new Map([
["", { type: "default", name: availableRaw[0]?.label || null }],
...available,
]);
// Note: creating virtual default input devices would be another problem // Note: creating virtual default input devices would be another problem
// entirely, because requesting a media stream from deviceId "" won't // entirely, because requesting a media stream from deviceId "" won't
// automatically track the default device. // automatically track the default device.

View File

@@ -16,3 +16,7 @@
flex-direction: column; flex-direction: column;
gap: var(--cpd-space-4x); gap: var(--cpd-space-4x);
} }
.secondary {
color: var(--cpd-color-text-secondary);
}

View File

@@ -5,7 +5,14 @@ SPDX-License-Identifier: AGPL-3.0-only
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,7 +20,7 @@ import {
RadioControl, RadioControl,
Separator, Separator,
} from "@vector-im/compound-web"; } from "@vector-im/compound-web";
import { useTranslation } from "react-i18next"; import { Trans, useTranslation } from "react-i18next";
import { type MediaDevice } from "../livekit/MediaDevicesContext"; import { type MediaDevice } from "../livekit/MediaDevicesContext";
import styles from "./DeviceSelection.module.css"; import styles from "./DeviceSelection.module.css";
@@ -53,27 +60,49 @@ export const DeviceSelection: FC<Props> = ({
</Heading> </Heading>
<Separator className={styles.separator} /> <Separator className={styles.separator} />
<div className={styles.options}> <div className={styles.options}>
{[...devices.available].map(([id, label]) => ( {[...devices.available].map(([id, label]) => {
<InlineField let labelText: ReactNode;
key={id} switch (label.type) {
name={groupId} case "name":
control={ labelText = label.name;
<RadioControl break;
checked={id === devices.selectedId} case "number":
onChange={onChange} labelText = numberedLabel(label.number);
value={id} break;
/> case "default":
} labelText =
> label.name === null ? (
<Label> t("settings.devices.default")
{label.type === "name" ) : (
? label.name <Trans
: label.type === "number" i18nKey="settings.devices.default_named"
? numberedLabel(label.number) name={label.name}
: t("settings.devices.default")} >
</Label> Default{" "}
</InlineField> <span className={styles.secondary}>
))} ({{ name: label.name } as unknown as ReactElement})
</span>
</Trans>
);
break;
}
return (
<InlineField
key={id}
name={groupId}
control={
<RadioControl
checked={id === devices.selectedId}
onChange={onChange}
value={id}
/>
}
>
<Label>{labelText}</Label>
</InlineField>
);
})}
</div> </div>
</div> </div>
); );