🚸(frontend) add alert for media devices already in use

Show explicit warning when microphone/camera are occupied by other
applications. Helps users understand permission failures and reminds
them to close other video conferencing apps.

Spotted issue through Crisp support - users often forget to quit other
webconfs that don't auto-disconnect when alone.
This commit is contained in:
lebaudantoine
2025-07-17 16:28:48 +02:00
parent d068558c8f
commit 26a90456f7
6 changed files with 108 additions and 1 deletions

View File

@@ -2,7 +2,12 @@ import { useEffect, useMemo, useState } from 'react'
import { useQuery } from '@tanstack/react-query'
import { useTranslation } from 'react-i18next'
import { LiveKitRoom } from '@livekit/components-react'
import { DisconnectReason, Room, RoomOptions } from 'livekit-client'
import {
DisconnectReason,
MediaDeviceFailure,
Room,
RoomOptions,
} from 'livekit-client'
import { keys } from '@/api/queryKeys'
import { queryClient } from '@/api/queryClient'
import { Screen } from '@/layout/Screen'
@@ -18,6 +23,7 @@ import { css } from '@/styled-system/css'
import { BackgroundProcessorFactory } from '../livekit/components/blur'
import { LocalUserChoices } from '@/stores/userChoices'
import { navigateTo } from '@/navigation/navigateTo'
import { MediaDeviceErrorAlert } from './MediaDeviceErrorAlert'
export const Conference = ({
roomId,
@@ -86,6 +92,13 @@ export const Conference = ({
const room = useMemo(() => new Room(roomOptions), [roomOptions])
const [showInviteDialog, setShowInviteDialog] = useState(mode === 'create')
const [mediaDeviceError, setMediaDeviceError] = useState<{
error: MediaDeviceFailure | null
kind: MediaDeviceKind | null
}>({
error: null,
kind: null,
})
const { t } = useTranslation('rooms')
if (isCreateError) {
@@ -132,6 +145,11 @@ export const Conference = ({
navigateTo('feedback', { duplicateIdentity: true })
}
}}
onMediaDeviceFailure={(e, kind) => {
if (e == MediaDeviceFailure.DeviceInUse && !!kind) {
setMediaDeviceError({ error: e, kind })
}
}}
>
<VideoConference />
{showInviteDialog && (
@@ -142,6 +160,10 @@ export const Conference = ({
onClose={() => setShowInviteDialog(false)}
/>
)}
<MediaDeviceErrorAlert
{...mediaDeviceError}
onClose={() => setMediaDeviceError({ error: null, kind: null })}
/>
</LiveKitRoom>
</Screen>
</QueryAware>

View File

@@ -0,0 +1,33 @@
import { MediaDeviceFailure } from 'livekit-client'
import { useTranslation } from 'react-i18next'
import { Button, Dialog, P } from '@/primitives'
export type MediaDeviceErrorAlertProps = {
error?: MediaDeviceFailure | null
kind?: MediaDeviceKind | null
onClose: () => void
}
export const MediaDeviceErrorAlert = ({
error,
kind,
onClose,
}: MediaDeviceErrorAlertProps) => {
const { t } = useTranslation('rooms', { keyPrefix: 'mediaErrorDialog' })
if (!error || !kind) return
return (
<Dialog
role="alertdialog"
isOpen={!!error}
onClose={onClose}
title={t(`${error}.title.${kind}`)}
>
<P>{t(`${error}.body.${kind}`)}</P>
<Button variant="text" size="sm" onPress={onClose}>
{t('close')}
</Button>
</Dialog>
)
}

View File

@@ -60,6 +60,19 @@
"description": "Teilen Sie diesen Link mit Personen, die Sie zum Meeting einladen möchten.",
"permissions": "Personen mit diesem Link benötigen keine Erlaubnis, um diesem Meeting beizutreten."
},
"mediaErrorDialog": {
"DeviceInUse": {
"title": {
"audioinput": "Mikrofon konnte nicht aktiviert werden",
"videoinput": "Kamera konnte nicht aktiviert werden"
},
"body": {
"audioinput": "Ihr Mikrofon wird bereits in einem anderen Tab oder von einer anderen Anwendung verwendet, was die Aktivierung in diesem Videoanruf verhindert. Wenn Sie möchten, schließen Sie den anderen Tab oder die Anwendung, um es hier zu verwenden.",
"videoinput": "Ihre Kamera wird bereits in einem anderen Tab oder von einer anderen Anwendung verwendet, was die Aktivierung in diesem Videoanruf verhindert. Wenn Sie möchten, schließen Sie den anderen Tab oder die Anwendung, um sie hier zu verwenden."
}
},
"close": "OK"
},
"error": {
"createRoom": {
"heading": "Authentifizierung erforderlich",

View File

@@ -60,6 +60,19 @@
"description": "Share this link with people you want to invite to the meeting.",
"permissions": "People with this link do not need your permission to join this meeting."
},
"mediaErrorDialog": {
"DeviceInUse": {
"title": {
"audioinput": "Microphone Activation Failed",
"videoinput": "Camera Activation Failed"
},
"body": {
"audioinput": "Your microphone is already in use by another tab or application, which prevents it from being activated in this video call. If you wish, close the other tab or application to use it here.",
"videoinput": "Your camera is already in use by another tab or application, which prevents it from being activated in this video call. If you wish, close the other tab or application to use it here."
}
},
"close": "OK"
},
"error": {
"createRoom": {
"heading": "Authentication Required",

View File

@@ -60,6 +60,19 @@
"description": "Partagez ce lien avec les personnes que vous souhaitez inviter à la réunion.",
"permissions": "Les personnes disposant de ce lien n'ont pas besoin de votre autorisation pour rejoindre cette réunion."
},
"mediaErrorDialog": {
"DeviceInUse": {
"title": {
"audioinput": "Activation microphone impossible",
"videoinput": "Activation caméra impossible"
},
"body": {
"audioinput": "Votre microphone est déjà utilisé dans un autre onglet ou une autre application, ce qui empêche son activation dans cette visioconférence. Si vous le souhaitez, fermez l'autre onglet ou application pour l'utiliser ici.",
"videoinput": "Votre caméra est déjà utilisée dans un autre onglet ou une autre application, ce qui empêche son activation dans cette visioconférence. Si vous le souhaitez, fermez l'autre onglet ou application pour l'utiliser ici."
}
},
"close": "OK"
},
"error": {
"createRoom": {
"heading": "Authentification requise",

View File

@@ -60,6 +60,19 @@
"description": "Deel deze link met mensen die u wilt uitnodigen voor de vergadering.",
"permissions": "Mensen met deze link hebben uw toestemming niet nodig om deel te nemen aan deze vergadering."
},
"mediaErrorDialog": {
"DeviceInUse": {
"title": {
"audioinput": "Microfoon activeren mislukt",
"videoinput": "Camera activeren mislukt"
},
"body": {
"audioinput": "Je microfoon wordt al gebruikt door een ander tabblad of een andere toepassing, waardoor deze niet geactiveerd kan worden in dit videogesprek. Sluit indien gewenst het andere tabblad of de toepassing om je microfoon hier te gebruiken.",
"videoinput": "Je camera wordt al gebruikt door een ander tabblad of een andere toepassing, waardoor deze niet geactiveerd kan worden in dit videogesprek. Sluit indien gewenst het andere tabblad of de toepassing om je camera hier te gebruiken."
}
},
"close": "OK"
},
"error": {
"createRoom": {
"heading": "Verificatie vereist",