diff --git a/src/frontend/src/features/rooms/components/Join.tsx b/src/frontend/src/features/rooms/components/Join.tsx index 04544af2..f5de1f9c 100644 --- a/src/frontend/src/features/rooms/components/Join.tsx +++ b/src/frontend/src/features/rooms/components/Join.tsx @@ -20,6 +20,7 @@ import { EffectsConfiguration, EffectsConfigurationProps, } from '../livekit/components/effects/EffectsConfiguration' +import { SelectDevice } from '../livekit/components/controls/Device/SelectDevice' import { usePersistentUserChoices } from '../livekit/hooks/usePersistentUserChoices' import { BackgroundProcessorFactory } from '../livekit/components/blur' import { isMobileBrowser } from '@livekit/components-core' @@ -35,7 +36,6 @@ import { useLoginHint } from '@/hooks/useLoginHint' import { useSnapshot } from 'valtio' import { openPermissionsDialog, permissionsStore } from '@/stores/permissions' import { ToggleDevice } from './join/ToggleDevice' -import { SelectDevice } from './join/SelectDevice' import { useResolveInitiallyDefaultDeviceId } from '../livekit/hooks/useResolveInitiallyDefaultDeviceId' import { isSafari } from '@/utils/livekit' import type { LocalUserChoices } from '@/stores/userChoices' diff --git a/src/frontend/src/features/rooms/livekit/components/controls/Device/AudioDevicesControl.tsx b/src/frontend/src/features/rooms/livekit/components/controls/Device/AudioDevicesControl.tsx index 44e1fee9..9eb301f7 100644 --- a/src/frontend/src/features/rooms/livekit/components/controls/Device/AudioDevicesControl.tsx +++ b/src/frontend/src/features/rooms/livekit/components/controls/Device/AudioDevicesControl.tsx @@ -1,12 +1,8 @@ import { useTranslation } from 'react-i18next' -import { - useMediaDeviceSelect, - useTrackToggle, - UseTrackToggleProps, -} from '@livekit/components-react' -import { Button, Menu, MenuList } from '@/primitives' +import { useTrackToggle, UseTrackToggleProps } from '@livekit/components-react' +import { Button, Popover } from '@/primitives' import { RiArrowUpSLine, RiMicLine, RiMicOffLine } from '@remixicon/react' -import { LocalAudioTrack, LocalVideoTrack, Track } from 'livekit-client' +import { Track } from 'livekit-client' import { ToggleDevice } from '@/features/rooms/livekit/components/controls/ToggleDevice.tsx' import { css } from '@/styled-system/css' @@ -16,17 +12,16 @@ import { permissionsStore } from '@/stores/permissions' import { ToggleDeviceConfig } from '../../../config/ToggleDeviceConfig' import Source = Track.Source import * as React from 'react' +import { SelectDevice } from './SelectDevice' type AudioDevicesControlProps = Omit< UseTrackToggleProps, 'source' | 'onChange' > & { - track?: LocalAudioTrack | LocalVideoTrack hideMenu?: boolean } export const AudioDevicesControl = ({ - track, hideMenu, ...props }: AudioDevicesControlProps) => { @@ -44,8 +39,12 @@ export const AudioDevicesControl = ({ } const { t } = useTranslation('rooms', { keyPrefix: 'join' }) - const { saveAudioInputDeviceId, saveAudioInputEnabled } = - usePersistentUserChoices() + const { + userChoices: { audioDeviceId, audioOutputDeviceId }, + saveAudioInputDeviceId, + saveAudioInputEnabled, + saveAudioOutputDeviceId, + } = usePersistentUserChoices() const onChange = React.useCallback( (enabled: boolean, isUserInitiated: boolean) => @@ -63,9 +62,6 @@ export const AudioDevicesControl = ({ const isPermissionDeniedOrPrompted = permissions.isMicrophoneDenied || permissions.isMicrophonePrompted - const { devices, activeDeviceId, setActiveMediaDevice } = - useMediaDeviceSelect({ kind: 'audioinput', track }) - const selectLabel = t('audioinput.choose') return ( @@ -90,7 +86,7 @@ export const AudioDevicesControl = ({ }} /> {!hideMenu && ( - + - ({ - value: d.deviceId, - label: d.label, - }))} - selectedItem={activeDeviceId} - onAction={(value) => { - setActiveMediaDevice(value as string) - saveAudioInputDeviceId(value as string) - }} - variant="dark" - /> - +
+
+ +
+
+ +
+
+ )} ) diff --git a/src/frontend/src/features/rooms/components/join/SelectDevice.tsx b/src/frontend/src/features/rooms/livekit/components/controls/Device/SelectDevice.tsx similarity index 85% rename from src/frontend/src/features/rooms/components/join/SelectDevice.tsx rename to src/frontend/src/features/rooms/livekit/components/controls/Device/SelectDevice.tsx index 3d151ff2..0c0cad39 100644 --- a/src/frontend/src/features/rooms/components/join/SelectDevice.tsx +++ b/src/frontend/src/features/rooms/livekit/components/controls/Device/SelectDevice.tsx @@ -10,6 +10,7 @@ import { useEffect, useMemo } from 'react' import { Select } from '@/primitives/Select' import { useSnapshot } from 'valtio' import { permissionsStore } from '@/stores/permissions' +import { Placement } from '@react-types/overlays' type DeviceItems = Array<{ value: string; label: string }> @@ -17,14 +18,21 @@ type DeviceConfig = { icon: RemixiconComponentType } +type SelectDeviceContext = { + variant?: 'light' | 'dark' + placement?: Placement +} + type SelectDeviceProps = { id?: string onSubmit?: (id: string) => void kind: MediaDeviceKind + context?: 'join' | 'room' } type SelectDevicePermissionsProps = SelectDeviceProps & { config: DeviceConfig + contextProps: SelectDeviceContext } const SelectDevicePermissions = ({ @@ -32,6 +40,7 @@ const SelectDevicePermissions = ({ kind, config, onSubmit, + contextProps, }: SelectDevicePermissionsProps) => { const { t } = useTranslation('rooms', { keyPrefix: 'join' }) @@ -78,15 +87,28 @@ const SelectDevicePermissions = ({ onSubmit?.(key as string) setActiveMediaDevice(key as string) }} + {...contextProps} /> ) } -export const SelectDevice = ({ id, onSubmit, kind }: SelectDeviceProps) => { +export const SelectDevice = ({ + id, + onSubmit, + kind, + context = 'join', +}: SelectDeviceProps) => { const { t } = useTranslation('rooms', { keyPrefix: 'join' }) const permissions = useSnapshot(permissionsStore) + const contextProps = useMemo(() => { + if (context == 'room') { + return { variant: 'dark', placement: 'top' } + } + return {} + }, [context]) + const config = useMemo(() => { switch (kind) { case 'audioinput': @@ -128,6 +150,7 @@ export const SelectDevice = ({ id, onSubmit, kind }: SelectDeviceProps) => { items={[]} iconComponent={config?.icon} placeholder={t('selectDevice.permissionsNeeded')} + {...contextProps} /> ) } @@ -138,6 +161,7 @@ export const SelectDevice = ({ id, onSubmit, kind }: SelectDeviceProps) => { onSubmit={onSubmit} kind={kind} config={config} + contextProps={contextProps} /> ) } diff --git a/src/frontend/src/features/rooms/livekit/components/controls/Device/VideoDeviceControl.tsx b/src/frontend/src/features/rooms/livekit/components/controls/Device/VideoDeviceControl.tsx index 2d95265e..10fa9675 100644 --- a/src/frontend/src/features/rooms/livekit/components/controls/Device/VideoDeviceControl.tsx +++ b/src/frontend/src/features/rooms/livekit/components/controls/Device/VideoDeviceControl.tsx @@ -1,12 +1,8 @@ import { useTranslation } from 'react-i18next' -import { - useMediaDeviceSelect, - useTrackToggle, - UseTrackToggleProps, -} from '@livekit/components-react' -import { Button, Menu, MenuList } from '@/primitives' +import { useTrackToggle, UseTrackToggleProps } from '@livekit/components-react' +import { Button, Popover } from '@/primitives' import { RiArrowUpSLine, RiVideoOffLine, RiVideoOnLine } from '@remixicon/react' -import { LocalVideoTrack, Track, VideoCaptureOptions } from 'livekit-client' +import { Track, VideoCaptureOptions } from 'livekit-client' import { ToggleDevice } from '@/features/rooms/livekit/components/controls/ToggleDevice' import { css } from '@/styled-system/css' @@ -17,17 +13,16 @@ import { permissionsStore } from '@/stores/permissions' import { ToggleDeviceConfig } from '../../../config/ToggleDeviceConfig' import Source = Track.Source import * as React from 'react' +import { SelectDevice } from './SelectDevice' type VideoDeviceControlProps = Omit< UseTrackToggleProps, 'source' | 'onChange' > & { - track?: LocalVideoTrack hideMenu?: boolean } export const VideoDeviceControl = ({ - track, hideMenu, ...props }: VideoDeviceControlProps) => { @@ -90,9 +85,6 @@ export const VideoDeviceControl = ({ } as VideoCaptureOptions) } - const { devices, activeDeviceId, setActiveMediaDevice } = - useMediaDeviceSelect({ kind: 'videoinput', track }) - const selectLabel = t('videoinput.choose') return ( @@ -117,7 +109,7 @@ export const VideoDeviceControl = ({ }} /> {!hideMenu && ( - + - ({ - value: d.deviceId, - label: d.label, - }))} - selectedItem={activeDeviceId} - onAction={(value) => { - setActiveMediaDevice(value as string) - saveVideoInputDeviceId(value as string) - }} - variant="dark" - /> - +
+
+ +
+
+ )} )