From 2044754666be25f0c0824d14dd1d54ea4fefb8e6 Mon Sep 17 00:00:00 2001 From: Matiss Jurevics Date: Sun, 29 Mar 2026 17:30:00 +0000 Subject: [PATCH] fix(webapp): ignore stale client stream sessions --- WebApp/src/lib/app/controller.js | 39 ++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/WebApp/src/lib/app/controller.js b/WebApp/src/lib/app/controller.js index 5411f6b..f7e5f81 100644 --- a/WebApp/src/lib/app/controller.js +++ b/WebApp/src/lib/app/controller.js @@ -766,6 +766,19 @@ const clearClientStream = () => { setClientStreamMode('none'); }; +const hasReusableClientStreamSession = (streamSessionId) => + Boolean(streamSessionId && (remoteStreams.has(streamSessionId) || streamTimers.has(streamSessionId))); + +const removeCameraSessionMapping = (streamSessionId) => { + if (!streamSessionId) return; + + patchAppState((state) => ({ + cameraSessions: Object.fromEntries( + Object.entries(state.cameraSessions || {}).filter(([, sessionId]) => sessionId !== streamSessionId) + ) + })); +}; + const getLinkedCamera = (cameraDeviceId) => getAppState().linkedCameras.find((camera) => camera.cameraDeviceId === cameraDeviceId); @@ -1034,6 +1047,7 @@ const teardownPeerConnection = (streamSessionId) => { pendingCandidatesMap.clear(); connectedPeers.clear(); setConnectedStreamSessionIds(); + setAppState({ cameraSessions: {} }); clearClientStream(); return; } @@ -1046,6 +1060,7 @@ const teardownPeerConnection = (streamSessionId) => { pendingCandidatesMap.delete(streamSessionId); connectedPeers.delete(streamSessionId); setConnectedStreamSessionIds(); + removeCameraSessionMapping(streamSessionId); if (getAppState().activeStreamSessionId === streamSessionId) { clearClientStream(); @@ -2101,19 +2116,29 @@ const actions = { }, selectCamera(cameraDeviceId) { - const sessions = getAppState().cameraSessions || {}; + const currentState = getAppState(); + const sessions = currentState.cameraSessions || {}; + const existingSessionId = sessions[cameraDeviceId] || null; + const reusableSessionId = hasReusableClientStreamSession(existingSessionId) ? existingSessionId : null; + + if (currentState.activeCameraDeviceId !== cameraDeviceId || currentState.activeStreamSessionId !== reusableSessionId) { + clearClientStream(); + } + setAppState({ activeCameraDeviceId: cameraDeviceId, - activeStreamSessionId: sessions[cameraDeviceId] || null, + activeStreamSessionId: reusableSessionId, openLinkedCameraMenuId: null }); - void actions.requestStream(cameraDeviceId); - - attachClientStreamToElement(); - if (!getAppState().activeStreamSessionId) { - setClientStreamMode('connecting'); + if (reusableSessionId) { + attachClientStreamToElement(); + setClientStreamMode(remoteStreams.has(reusableSessionId) ? 'video' : 'connecting'); + return; } + + setClientStreamMode('connecting'); + void actions.requestStream(cameraDeviceId); }, closeStreamViewer() {