wip allow selecting audioutput from join screen

This commit is contained in:
lebaudantoine
2025-07-31 21:09:00 +02:00
parent e29c241d4e
commit 1ae08420a4
6 changed files with 148 additions and 28 deletions
@@ -7,7 +7,7 @@ import { LocalVideoTrack, Track } from 'livekit-client'
import { H } from '@/primitives/H'
import { Field } from '@/primitives/Field'
import { Button, Dialog, Text, Form } from '@/primitives'
import { HStack, VStack } from '@/styled-system/jsx'
import { VStack } from '@/styled-system/jsx'
import { Heading } from 'react-aria-components'
import { RiImageCircleAiFill } from '@remixicon/react'
import {
@@ -27,6 +27,7 @@ import { Spinner } from '@/primitives/Spinner'
import { ApiAccessLevel } from '../api/ApiRoom'
import { useLoginHint } from '@/hooks/useLoginHint'
import { ToggleDeviceJoin } from '@/features/rooms/components/ToggleDeviceJoin'
import { SelectDeviceJoin } from '@/features/rooms/components/SelectDeviceJoin'
const onError = (e: Error) => console.error('ERROR', e)
@@ -119,6 +120,7 @@ export const Join = ({
audioEnabled,
videoEnabled,
audioDeviceId,
audioOutputDeviceId,
videoDeviceId,
processorSerialized,
username,
@@ -126,11 +128,14 @@ export const Join = ({
saveAudioInputEnabled,
saveVideoInputEnabled,
saveAudioInputDeviceId,
saveAudioOutputDeviceId,
saveVideoInputDeviceId,
saveUsername,
saveProcessorSerialized,
} = usePersistentUserChoices()
// fix - permission issue, when permission not set doesn't refresh
// fix - when mic change, camera blink
const tracks = usePreviewTracks(
{
audio: { deviceId: audioDeviceId },
@@ -351,6 +356,7 @@ export const Join = ({
<div
className={css({
width: '100%',
minWidth: 0,
lg: {
width: '740px',
},
@@ -447,32 +453,53 @@ export const Join = ({
}
/>
</div>
<HStack justify="center" padding={1.5}>
<SelectToggleDevice
source={Track.Source.Microphone}
initialState={audioEnabled}
track={audioTrack}
initialDeviceId={audioDeviceId}
onChange={(enabled) => saveAudioInputEnabled(enabled)}
onDeviceError={(error) => console.error(error)}
onActiveDeviceChange={(deviceId) =>
saveAudioInputDeviceId(deviceId ?? '')
}
variant="tertiary"
/>
<SelectToggleDevice
source={Track.Source.Camera}
initialState={videoEnabled}
track={videoTrack}
initialDeviceId={videoDeviceId}
onChange={(enabled) => saveVideoInputEnabled(enabled)}
onDeviceError={(error) => console.error(error)}
onActiveDeviceChange={(deviceId) =>
saveVideoInputDeviceId(deviceId ?? '')
}
variant="tertiary"
/>
</HStack>
<div
className={css({
display: 'none',
lg: {
display: 'flex',
justifyContent: 'center',
paddingTop: 1.5,
gap: '2%',
width: '80%',
marginX: 'auto',
},
})}
>
<div
className={css({
width: '30%',
})}
>
<SelectDeviceJoin
kind="audioinput"
id={audioDeviceId}
onSubmit={saveAudioInputDeviceId}
/>
</div>
<div
className={css({
width: '30%',
})}
>
<SelectDeviceJoin
kind="audiooutput"
id={audioOutputDeviceId}
onSubmit={saveAudioOutputDeviceId}
/>
</div>
<div
className={css({
width: '30%',
})}
>
<SelectDeviceJoin
kind="videoinput"
id={videoDeviceId}
onSubmit={saveVideoInputDeviceId}
/>
</div>
</div>
</div>
<div
className={css({
@@ -481,7 +508,7 @@ export const Join = ({
padding: '0',
sm: {
width: '448px',
padding: '0 3rem 9rem 3rem',
padding: '0 3rem 4rem 3rem',
},
})}
>
@@ -0,0 +1,69 @@
import { RiMicLine, RiVideoOnLine, RiVolumeDownLine } from '@remixicon/react'
import { Select } from '@/primitives/Select.tsx'
import { useMemo } from 'react'
import { useMediaDeviceSelect } from '@livekit/components-react'
import { useTranslation } from 'react-i18next'
type DeviceItems = Array<{ value: string; label: string }>
export const SelectDeviceJoin = ({
id,
onSubmit,
kind,
}: {
id?: string
onSubmit?: (id: string) => void
kind: MediaDeviceKind
}) => {
const { t } = useTranslation('rooms', { keyPrefix: 'join' })
const config = useMemo(() => {
switch (kind) {
case 'audioinput':
return {
icon: RiMicLine,
}
case 'audiooutput':
return {
icon: RiVolumeDownLine,
}
case 'videoinput':
return {
icon: RiVideoOnLine,
}
}
}, [kind])
const getDefaultSelectedKey = (items: DeviceItems) => {
if (!items || items.length === 0) return
const defaultItem =
items.find((item) => item.value === 'default') || items[0]
return defaultItem.value
}
const {
devices: devices,
activeDeviceId: activeDeviceId,
setActiveMediaDevice: setActiveMediaDevice,
} = useMediaDeviceSelect({ kind, requestPermissions: false })
const items: DeviceItems = devices.map((d) => ({
value: d.deviceId,
label: d.label,
}))
return (
<Select
aria-label={t(`${kind}.choose`)}
label=""
items={items}
iconComponent={config.icon}
placeholder={t('selectDevice.loading')}
defaultSelectedKey={id || activeDeviceId || getDefaultSelectedKey(items)}
onSelectionChange={(key) => {
onSubmit?.(key as string)
setActiveMediaDevice(key as string)
}}
/>
)
}
+6
View File
@@ -8,6 +8,9 @@
"back": "Dem Meeting erneut beitreten"
},
"join": {
"selectDevice": {
"loading": "Lädt…"
},
"videoinput": {
"choose": "Kamera auswählen",
"disable": "Kamera deaktivieren",
@@ -21,6 +24,9 @@
"enable": "Mikrofon aktivieren",
"label": "Mikrofon"
},
"audiooutput": {
"choose": "Lautsprecher auswählen"
},
"effects": {
"description": "Effekte anwenden",
"title": "Effekte",
+6
View File
@@ -8,6 +8,9 @@
"back": "Rejoin the meeting"
},
"join": {
"selectDevice": {
"loading": "Loading…"
},
"videoinput": {
"choose": "Select camera",
"disable": "Disable camera",
@@ -21,6 +24,9 @@
"enable": "Enable microphone",
"label": "Microphone"
},
"audiooutput": {
"choose": "Select speakers"
},
"effects": {
"description": "Apply effects",
"title": "Effects",
+6
View File
@@ -8,6 +8,9 @@
"back": "Réintégrer la réunion"
},
"join": {
"selectDevice": {
"loading": "Chargement…"
},
"videoinput": {
"choose": "Choisir la webcam",
"disable": "Désactiver la webcam",
@@ -21,6 +24,9 @@
"enable": "Activer le micro",
"label": "Microphone"
},
"audiooutput": {
"choose": "Choisir le haut parleur"
},
"heading": "Rejoindre la réunion ?",
"effects": {
"description": "Effets d'arrière plan",
+6
View File
@@ -8,6 +8,9 @@
"back": "Sluit weer bij de vergadering aan"
},
"join": {
"selectDevice": {
"loading": "Laden…"
},
"videoinput": {
"choose": "Selecteer camera",
"disable": "Camera uitschakelen",
@@ -21,6 +24,9 @@
"enable": "Microfoon dempen opheffen",
"label": "Microfoon"
},
"audiooutput": {
"choose": "Selecteer luidsprekers"
},
"effects": {
"description": "Pas effecten toe",
"title": "Effecten",