From 5e74fce6e2767e7d93a858d12c4295f4f877971e Mon Sep 17 00:00:00 2001 From: lebaudantoine Date: Sun, 15 Sep 2024 23:32:03 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=B8(frontend)=20simplify=20audio=20and?= =?UTF-8?q?=20video=20select=20inputs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Based on @manuhabitela's works. Align UX with common tools as Gmeet or Jitsi. Enhanced accessibility. --- .../src/features/rooms/components/Join.tsx | 4 +- .../controls/SelectToggleDevice.tsx | 120 ++++++++++++++++++ .../rooms/livekit/prefabs/ControlBar.tsx | 62 +++------ src/frontend/src/locales/de/rooms.json | 22 +++- src/frontend/src/locales/en/rooms.json | 24 +++- src/frontend/src/locales/fr/rooms.json | 24 +++- src/frontend/src/primitives/MenuList.tsx | 2 +- 7 files changed, 203 insertions(+), 55 deletions(-) create mode 100644 src/frontend/src/features/rooms/livekit/components/controls/SelectToggleDevice.tsx diff --git a/src/frontend/src/features/rooms/components/Join.tsx b/src/frontend/src/features/rooms/components/Join.tsx index 0657ec97..575be7b7 100644 --- a/src/frontend/src/features/rooms/components/Join.tsx +++ b/src/frontend/src/features/rooms/components/Join.tsx @@ -16,8 +16,8 @@ export const Join = ({ diff --git a/src/frontend/src/features/rooms/livekit/components/controls/SelectToggleDevice.tsx b/src/frontend/src/features/rooms/livekit/components/controls/SelectToggleDevice.tsx new file mode 100644 index 00000000..99a1ff71 --- /dev/null +++ b/src/frontend/src/features/rooms/livekit/components/controls/SelectToggleDevice.tsx @@ -0,0 +1,120 @@ +import { useTranslation } from 'react-i18next' +import { + useMediaDeviceSelect, + useTrackToggle, + UseTrackToggleProps, +} from '@livekit/components-react' +import { HStack } from '@/styled-system/jsx' +import { Button, Menu, MenuList, ToggleButton } from '@/primitives' +import { + RemixiconComponentType, + RiArrowDownSLine, + RiMicLine, + RiMicOffLine, + RiVideoOffLine, + RiVideoOnLine, +} from '@remixicon/react' +import { Track } from 'livekit-client' +import React from 'react' + +export type ToggleSource = Exclude< + Track.Source, + Track.Source.ScreenShareAudio | Track.Source.Unknown +> + +type SelectToggleSource = Exclude + +type SelectToggleDeviceConfig = { + kind: MediaDeviceKind + iconOn: RemixiconComponentType + iconOff: RemixiconComponentType +} + +type SelectToggleDeviceConfigMap = { + [key in SelectToggleSource]: SelectToggleDeviceConfig +} + +const selectToggleDeviceConfig: SelectToggleDeviceConfigMap = { + [Track.Source.Microphone]: { + kind: 'audioinput', + iconOn: RiMicLine, + iconOff: RiMicOffLine, + }, + [Track.Source.Camera]: { + kind: 'videoinput', + iconOn: RiVideoOnLine, + iconOff: RiVideoOffLine, + }, +} + +type SelectToggleDeviceProps = + UseTrackToggleProps & { + onActiveDeviceChange: (deviceId: string) => void + source: SelectToggleSource + } + +export const SelectToggleDevice = ({ + onActiveDeviceChange, + ...props +}: SelectToggleDeviceProps) => { + const config = selectToggleDeviceConfig[props.source] + if (!config) { + throw new Error('Invalid source') + } + + const { t } = useTranslation('rooms', { keyPrefix: 'join' }) + const { buttonProps, enabled } = useTrackToggle(props) + + const { kind, iconOn, iconOff } = config + + const { devices, activeDeviceId, setActiveMediaDevice } = + useMediaDeviceSelect({ kind }) + + const toggleLabel = t(enabled ? 'disable' : 'enable', { + keyPrefix: `join.${kind}`, + }) + + const selectLabel = t('choose', { keyPrefix: `join.${kind}` }) + const Icon = enabled ? iconOn : iconOff + + return ( + + + buttonProps.onClick?.( + e as unknown as React.MouseEvent + ) + } + aria-label={toggleLabel} + tooltip={toggleLabel} + groupPosition="left" + > + + + + + ({ + value: d.deviceId, + label: d.label, + }))} + selectedItem={activeDeviceId} + onAction={(value) => { + setActiveMediaDevice(value as string) + onActiveDeviceChange(value as string) + }} + /> + + + ) +} diff --git a/src/frontend/src/features/rooms/livekit/prefabs/ControlBar.tsx b/src/frontend/src/features/rooms/livekit/prefabs/ControlBar.tsx index 6fe0b89d..c60eb1dc 100644 --- a/src/frontend/src/features/rooms/livekit/prefabs/ControlBar.tsx +++ b/src/frontend/src/features/rooms/livekit/prefabs/ControlBar.tsx @@ -6,7 +6,6 @@ import { supportsScreenSharing } from '@livekit/components-core' import { DisconnectButton, LeaveIcon, - MediaDeviceMenu, TrackToggle, useMaybeLayoutContext, usePersistentUserChoices, @@ -20,6 +19,7 @@ import { OptionsButton } from '../components/controls/Options/OptionsButton' import { ParticipantsToggle } from '@/features/rooms/livekit/components/controls/Participants/ParticipantsToggle' import { ChatToggle } from '@/features/rooms/livekit/components/controls/ChatToggle' import { HandToggle } from '@/features/rooms/livekit/components/controls/HandToggle' +import { SelectToggleDevice } from '@/features/rooms/livekit/components/controls/SelectToggleDevice' /** @public */ export type ControlBarControls = { @@ -126,46 +126,26 @@ export function ControlBar({ return (
-
- - onDeviceError?.({ source: Track.Source.Microphone, error }) - } - > - {showText && t('controls.microphone')} - -
- - saveAudioInputDeviceId(deviceId ?? '') - } - /> -
-
-
- - onDeviceError?.({ source: Track.Source.Camera, error }) - } - > - {showText && t('controls.camera')} - -
- - saveVideoInputDeviceId(deviceId ?? '') - } - /> -
-
+ + onDeviceError?.({ source: Track.Source.Microphone, error }) + } + onActiveDeviceChange={(deviceId) => + saveAudioInputDeviceId(deviceId ?? '') + } + /> + + onDeviceError?.({ source: Track.Source.Camera, error }) + } + onActiveDeviceChange={(deviceId) => + saveVideoInputDeviceId(deviceId ?? '') + } + /> {browserSupportsScreenSharing && ( ({ const label = typeof item === 'string' ? item : item.label return ( {