From bcd95da21f75ff346f2256d3502df16b5598f2d6 Mon Sep 17 00:00:00 2001 From: lebaudantoine Date: Sun, 23 Aug 2026 17:14:17 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B(frontend)=20make=20the=20device-in?= =?UTF-8?q?-use=20logic=20race-free?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The device-in-use logic could race when multiple detection events fired close together (e.g. rapid mic/cam toggles or several devices becoming busy at once), leading to inconsistent store state. Refactor the flow so all updates go through a serialized path, removing the race conditions and keeping the in-use state consistent regardless of event ordering. --- .../rooms/hooks/useWatchDeviceReleased.ts | 18 +++++++++++++----- .../rooms/livekit/hooks/useJoinTracks.ts | 3 ++- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/frontend/src/features/rooms/hooks/useWatchDeviceReleased.ts b/src/frontend/src/features/rooms/hooks/useWatchDeviceReleased.ts index 8329b188..889ca0d3 100644 --- a/src/frontend/src/features/rooms/hooks/useWatchDeviceReleased.ts +++ b/src/frontend/src/features/rooms/hooks/useWatchDeviceReleased.ts @@ -36,10 +36,18 @@ function useWatchKind( useEffect(() => { if (!inUse) return - const id = setInterval( - () => void probeDeviceReleased(kind, selectedDeviceId || undefined), - RETRY_INTERVAL_MS - ) - return () => clearInterval(id) + let stopped = false + let timer: number | undefined + const tick = async () => { + await probeDeviceReleased(kind, selectedDeviceId || undefined) + if (!stopped) { + timer = window.setTimeout(tick, RETRY_INTERVAL_MS) + } + } + timer = window.setTimeout(tick, RETRY_INTERVAL_MS) + return () => { + stopped = true + window.clearTimeout(timer) + } }, [kind, inUse, selectedDeviceId]) } diff --git a/src/frontend/src/features/rooms/livekit/hooks/useJoinTracks.ts b/src/frontend/src/features/rooms/livekit/hooks/useJoinTracks.ts index 1a60c132..40ef40be 100644 --- a/src/frontend/src/features/rooms/livekit/hooks/useJoinTracks.ts +++ b/src/frontend/src/features/rooms/livekit/hooks/useJoinTracks.ts @@ -151,14 +151,15 @@ function useLocalTrack({ let cancelled = false create() .then((newTrack) => { - noteDeviceReady(permissionKind) if (cancelled) { newTrack.stop() return } + noteDeviceReady(permissionKind) setTrack(newTrack) }) .catch((error) => { + if (cancelled) return onMediaPermissionError( error as Error, permissionKind,