♻️(frontend) replace toggle device config with keyboard shortcut hook
Remove ugly toggle device configuration and implement hook to determine appropriate keyboard shortcuts based on media device kind. Cleaner approach that encapsulates shortcut logic in reusable hook instead of scattered configuration objects.
This commit is contained in:
committed by
aleb_the_flash
parent
2367750395
commit
f17e0a3ba0
@@ -660,18 +660,16 @@ export const Join = ({
|
||||
})}
|
||||
>
|
||||
<ToggleDevice
|
||||
source={Track.Source.Microphone}
|
||||
kind="audioinput"
|
||||
initialState={audioEnabled}
|
||||
track={audioTrack}
|
||||
onChange={(enabled) => saveAudioInputEnabled(enabled)}
|
||||
onDeviceError={(error) => console.error(error)}
|
||||
/>
|
||||
<ToggleDevice
|
||||
source={Track.Source.Camera}
|
||||
kind="videoinput"
|
||||
initialState={videoEnabled}
|
||||
track={videoTrack}
|
||||
onChange={(enabled) => saveVideoInputEnabled(enabled)}
|
||||
onDeviceError={(error) => console.error(error)}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
|
||||
@@ -1,33 +1,32 @@
|
||||
import { UseTrackToggleProps } from '@livekit/components-react'
|
||||
import { ToggleDevice as BaseToggleDevice } from '../../livekit/components/controls/Device/ToggleDevice'
|
||||
import {
|
||||
TOGGLE_DEVICE_CONFIG,
|
||||
ToggleSource,
|
||||
} from '../../livekit/config/ToggleDeviceConfig'
|
||||
import { LocalAudioTrack, LocalVideoTrack } from 'livekit-client'
|
||||
import { LocalAudioTrack, LocalVideoTrack, Track } from 'livekit-client'
|
||||
import { ButtonRecipeProps } from '@/primitives/buttonRecipe'
|
||||
import { useCallback, useState } from 'react'
|
||||
|
||||
type ToggleDeviceProps<T extends ToggleSource> = UseTrackToggleProps<T> & {
|
||||
type ToggleSource = Exclude<
|
||||
Track.Source,
|
||||
| Track.Source.ScreenShareAudio
|
||||
| Track.Source.Unknown
|
||||
| Track.Source.ScreenShare
|
||||
>
|
||||
|
||||
type ToggleDeviceProps<T extends ToggleSource> = Pick<
|
||||
UseTrackToggleProps<T>,
|
||||
'onChange' | 'initialState'
|
||||
> & {
|
||||
track?: LocalAudioTrack | LocalVideoTrack
|
||||
source: ToggleSource
|
||||
kind: MediaDeviceKind
|
||||
variant?: NonNullable<ButtonRecipeProps>['variant']
|
||||
}
|
||||
|
||||
export const ToggleDevice = <T extends ToggleSource>({
|
||||
track,
|
||||
kind,
|
||||
onChange,
|
||||
...props
|
||||
initialState,
|
||||
}: ToggleDeviceProps<T>) => {
|
||||
const config = TOGGLE_DEVICE_CONFIG[props.source]
|
||||
|
||||
if (!config) {
|
||||
throw new Error('Invalid source')
|
||||
}
|
||||
|
||||
const [isTrackEnabled, setIsTrackEnabled] = useState(
|
||||
props.initialState ?? false
|
||||
)
|
||||
const [isTrackEnabled, setIsTrackEnabled] = useState(initialState ?? false)
|
||||
|
||||
const toggle = useCallback(async () => {
|
||||
try {
|
||||
@@ -49,7 +48,7 @@ export const ToggleDevice = <T extends ToggleSource>({
|
||||
<BaseToggleDevice
|
||||
enabled={isTrackEnabled}
|
||||
toggle={toggle}
|
||||
config={config}
|
||||
kind={kind}
|
||||
variant="whiteCircle"
|
||||
errorVariant="errorCircle"
|
||||
toggleButtonProps={{
|
||||
|
||||
@@ -8,7 +8,6 @@ import { ToggleDevice } from './ToggleDevice'
|
||||
import { css } from '@/styled-system/css'
|
||||
import { usePersistentUserChoices } from '../../../hooks/usePersistentUserChoices'
|
||||
import { useCannotUseDevice } from '../../../hooks/useCannotUseDevice'
|
||||
import { ToggleDeviceConfig } from '../../../config/ToggleDeviceConfig'
|
||||
import Source = Track.Source
|
||||
import * as React from 'react'
|
||||
import { SelectDevice } from './SelectDevice'
|
||||
@@ -24,16 +23,6 @@ export const AudioDevicesControl = ({
|
||||
hideMenu,
|
||||
...props
|
||||
}: AudioDevicesControlProps) => {
|
||||
const config: ToggleDeviceConfig = {
|
||||
kind: 'audioinput',
|
||||
shortcut: {
|
||||
key: 'd',
|
||||
ctrlKey: true,
|
||||
},
|
||||
longPress: {
|
||||
key: 'Space',
|
||||
},
|
||||
}
|
||||
const { t } = useTranslation('rooms', { keyPrefix: 'join' })
|
||||
|
||||
const {
|
||||
@@ -67,7 +56,7 @@ export const AudioDevicesControl = ({
|
||||
>
|
||||
<ToggleDevice
|
||||
{...trackProps}
|
||||
config={config}
|
||||
kind="audioinput"
|
||||
variant="primaryDark"
|
||||
toggle={trackProps.toggle}
|
||||
toggleButtonProps={{
|
||||
|
||||
@@ -14,21 +14,21 @@ import {
|
||||
import { ButtonRecipeProps } from '@/primitives/buttonRecipe'
|
||||
import { ToggleButtonProps } from '@/primitives/ToggleButton'
|
||||
import { openPermissionsDialog } from '@/stores/permissions'
|
||||
import { ToggleDeviceConfig } from '../../../config/ToggleDeviceConfig'
|
||||
import { useCannotUseDevice } from '../../../hooks/useCannotUseDevice'
|
||||
import { useDeviceIcons } from '../../../hooks/useDeviceIcons'
|
||||
import { useDeviceShortcut } from '../../../hooks/useDeviceShortcut'
|
||||
|
||||
export type ToggleDeviceProps = {
|
||||
enabled: boolean
|
||||
toggle: () => void
|
||||
config: ToggleDeviceConfig
|
||||
kind: 'audioinput' | 'videoinput'
|
||||
variant?: NonNullable<ButtonRecipeProps>['variant']
|
||||
errorVariant?: NonNullable<ButtonRecipeProps>['variant']
|
||||
toggleButtonProps?: Partial<ToggleButtonProps>
|
||||
}
|
||||
|
||||
export const ToggleDevice = ({
|
||||
config,
|
||||
kind,
|
||||
enabled,
|
||||
toggle,
|
||||
variant = 'primaryDark',
|
||||
@@ -37,8 +37,6 @@ export const ToggleDevice = ({
|
||||
}: ToggleDeviceProps) => {
|
||||
const { t } = useTranslation('rooms', { keyPrefix: 'join' })
|
||||
|
||||
const { kind, shortcut, longPress } = config
|
||||
|
||||
const [pushToTalk, setPushToTalk] = useState(false)
|
||||
|
||||
const onKeyDown = () => {
|
||||
@@ -54,16 +52,21 @@ export const ToggleDevice = ({
|
||||
|
||||
const deviceIcons = useDeviceIcons(kind)
|
||||
const cannotUseDevice = useCannotUseDevice(kind)
|
||||
const deviceShortcut = useDeviceShortcut(kind)
|
||||
|
||||
useRegisterKeyboardShortcut({ shortcut, handler: toggle })
|
||||
useLongPress({ keyCode: longPress?.key, onKeyDown, onKeyUp })
|
||||
useRegisterKeyboardShortcut({ shortcut: deviceShortcut, handler: toggle })
|
||||
useLongPress({
|
||||
keyCode: kind === 'audioinput' ? 'Space' : undefined,
|
||||
onKeyDown,
|
||||
onKeyUp,
|
||||
})
|
||||
|
||||
const toggleLabel = useMemo(() => {
|
||||
const label = t(enabled ? 'disable' : 'enable', {
|
||||
keyPrefix: `join.${kind}`,
|
||||
})
|
||||
return shortcut ? appendShortcutLabel(label, shortcut) : label
|
||||
}, [enabled, kind, shortcut, t])
|
||||
return deviceShortcut ? appendShortcutLabel(label, deviceShortcut) : label
|
||||
}, [enabled, kind, deviceShortcut, t])
|
||||
|
||||
const Icon =
|
||||
enabled && !cannotUseDevice ? deviceIcons.toggleOn : deviceIcons.toggleOff
|
||||
|
||||
@@ -9,7 +9,6 @@ import { css } from '@/styled-system/css'
|
||||
import { usePersistentUserChoices } from '../../../hooks/usePersistentUserChoices'
|
||||
import { useCannotUseDevice } from '../../../hooks/useCannotUseDevice'
|
||||
import { BackgroundProcessorFactory } from '../../blur'
|
||||
import { ToggleDeviceConfig } from '../../../config/ToggleDeviceConfig'
|
||||
import Source = Track.Source
|
||||
import * as React from 'react'
|
||||
import { SelectDevice } from './SelectDevice'
|
||||
@@ -25,14 +24,6 @@ export const VideoDeviceControl = ({
|
||||
hideMenu,
|
||||
...props
|
||||
}: VideoDeviceControlProps) => {
|
||||
const config: ToggleDeviceConfig = {
|
||||
kind: 'videoinput',
|
||||
shortcut: {
|
||||
key: 'e',
|
||||
ctrlKey: true,
|
||||
},
|
||||
}
|
||||
|
||||
const { t } = useTranslation('rooms', { keyPrefix: 'join' })
|
||||
|
||||
const { userChoices, saveVideoInputDeviceId, saveVideoInputEnabled } =
|
||||
@@ -90,7 +81,7 @@ export const VideoDeviceControl = ({
|
||||
>
|
||||
<ToggleDevice
|
||||
{...trackProps}
|
||||
config={config}
|
||||
kind="videoinput"
|
||||
variant="primaryDark"
|
||||
toggle={toggle}
|
||||
toggleButtonProps={{
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import { Track } from 'livekit-client'
|
||||
import { Shortcut } from '@/features/shortcuts/types'
|
||||
|
||||
export type ToggleSource = Exclude<
|
||||
Track.Source,
|
||||
| Track.Source.ScreenShareAudio
|
||||
| Track.Source.Unknown
|
||||
| Track.Source.ScreenShare
|
||||
>
|
||||
|
||||
export type ToggleDeviceConfig = {
|
||||
kind: MediaDeviceKind
|
||||
shortcut?: Shortcut
|
||||
longPress?: Shortcut
|
||||
}
|
||||
|
||||
type ToggleDeviceConfigMap = {
|
||||
[key in ToggleSource]: ToggleDeviceConfig
|
||||
}
|
||||
|
||||
export const TOGGLE_DEVICE_CONFIG = {
|
||||
[Track.Source.Microphone]: {
|
||||
kind: 'audioinput',
|
||||
shortcut: {
|
||||
key: 'd',
|
||||
ctrlKey: true,
|
||||
},
|
||||
longPress: {
|
||||
key: 'Space',
|
||||
},
|
||||
},
|
||||
[Track.Source.Camera]: {
|
||||
kind: 'videoinput',
|
||||
shortcut: {
|
||||
key: 'e',
|
||||
ctrlKey: true,
|
||||
},
|
||||
},
|
||||
} as const satisfies ToggleDeviceConfigMap
|
||||
@@ -0,0 +1,21 @@
|
||||
import { useMemo } from 'react'
|
||||
import { Shortcut } from '@/features/shortcuts/types'
|
||||
|
||||
export const useDeviceShortcut = (kind: MediaDeviceKind) => {
|
||||
return useMemo<Shortcut | undefined>(() => {
|
||||
switch (kind) {
|
||||
case 'audioinput':
|
||||
return {
|
||||
key: 'e',
|
||||
ctrlKey: true,
|
||||
}
|
||||
case 'videoinput':
|
||||
return {
|
||||
key: 'd',
|
||||
ctrlKey: true,
|
||||
}
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
}, [kind])
|
||||
}
|
||||
Reference in New Issue
Block a user