diff --git a/packages/react/src/prefabs/MediaDeviceMenu.tsx b/packages/react/src/prefabs/MediaDeviceMenu.tsx index 208e47af3..9119b10d2 100644 --- a/packages/react/src/prefabs/MediaDeviceMenu.tsx +++ b/packages/react/src/prefabs/MediaDeviceMenu.tsx @@ -3,8 +3,12 @@ import * as React from 'react'; import { MediaDeviceSelect } from '../components/controls/MediaDeviceSelect'; import type { LocalAudioTrack, LocalVideoTrack } from 'livekit-client'; -/** @public */ -export interface MediaDeviceMenuProps extends React.ButtonHTMLAttributes { +interface KindWithInitialSelection { + kind: MediaDeviceKind; + initialSelection?: string; +} + +interface MediaDeviceMenuPropsSingleKind extends React.ButtonHTMLAttributes { kind?: MediaDeviceKind; initialSelection?: string; onActiveDeviceChange?: (kind: MediaDeviceKind, deviceId: string) => void; @@ -21,6 +25,26 @@ export interface MediaDeviceMenuProps extends React.ButtonHTMLAttributes { + kind?: KindWithInitialSelection[]; + initialSelection?: undefined; + onActiveDeviceChange?: (kind: MediaDeviceKind, deviceId: string) => void; + tracks?: Partial>; + /** + * this will call getUserMedia if the permissions are not yet given to enumerate the devices with device labels. + * in some browsers multiple calls to getUserMedia result in multiple permission prompts. + * It's generally advised only flip this to true, once a (preview) track has been acquired successfully with the + * appropriate permissions. + * + * @see {@link PreJoin} + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/enumerateDevices | MDN enumerateDevices} + */ + requestPermissions?: boolean; +} + +/** @public */ +export type MediaDeviceMenuProps = MediaDeviceMenuPropsSingleKind | MediaDeviceMenuPropsMultiKind; + /** * The `MediaDeviceMenu` component is a button that opens a menu that lists * all media devices and allows the user to select them. @@ -101,6 +125,26 @@ export function MediaDeviceMenu({ }; }, [handleClickOutside]); + // Normalize props to a consistent internal format + const kindsWithInitialSelection: KindWithInitialSelection[] = (() => { + if (kind === undefined) { + // Default to audio and video inputs when no kind is specified + return [{ kind: 'audioinput' as MediaDeviceKind }, { kind: 'videoinput' as MediaDeviceKind }]; + } else if (Array.isArray(kind)) { + // multi-kind case: kind is KindWithInitialSelection[] + return kind; + } else { + // single kind case: kind is MediaDeviceKind, initialSelection is string | undefined + return [{ kind, initialSelection }]; + } + })(); + + const kindLabels: Record = { + audioinput: 'Audio inputs', + videoinput: 'Video inputs', + audiooutput: 'Audio outputs', + }; + return ( <>