diff --git a/frontend/src/hooks/useLobbyDevices/index.ts b/frontend/src/hooks/useLobbyDevices/index.ts index 75d4386..28fea9e 100644 --- a/frontend/src/hooks/useLobbyDevices/index.ts +++ b/frontend/src/hooks/useLobbyDevices/index.ts @@ -89,6 +89,7 @@ export function useLobbyDevices(meetingReady: boolean): LobbyDeviceState { handleDismissPrompt: deviceLifecycle.handleDismissPrompt, clearError: mediaStream.clearError, clearDeviceError: mediaStream.clearDeviceError, + enableListenerMode: deviceLifecycle.enableListenerMode, retryDeviceAccess: setupDevicesAndEnumerate, cleanup: useCallback(() => { mediaStream.stopStream(); diff --git a/frontend/src/hooks/useLobbyDevices/types.ts b/frontend/src/hooks/useLobbyDevices/types.ts index c8ce596..86b5e5f 100644 --- a/frontend/src/hooks/useLobbyDevices/types.ts +++ b/frontend/src/hooks/useLobbyDevices/types.ts @@ -25,6 +25,7 @@ export interface LobbyDeviceState { handleDismissPrompt: () => void; clearError: () => void; clearDeviceError: () => void; + enableListenerMode: () => void; retryDeviceAccess: () => void; cleanup: () => void; } diff --git a/frontend/src/hooks/useLobbyDevices/useDeviceLifecycle.ts b/frontend/src/hooks/useLobbyDevices/useDeviceLifecycle.ts index 8523348..4f7819e 100644 --- a/frontend/src/hooks/useLobbyDevices/useDeviceLifecycle.ts +++ b/frontend/src/hooks/useLobbyDevices/useDeviceLifecycle.ts @@ -15,6 +15,7 @@ export interface DeviceLifecycleReturn { listenerMode: boolean; handleAllowPermissions: () => void; handleDismissPrompt: () => void; + enableListenerMode: () => void; } /** @@ -70,10 +71,16 @@ export function useDeviceLifecycle({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [meetingReady]); + const enableListenerMode = () => { + setListenerMode(true); + enumerateDevices().catch(() => {}); + }; + return { showPermissionPrompt, listenerMode, handleAllowPermissions, handleDismissPrompt, + enableListenerMode, }; } diff --git a/frontend/src/pages/MeetingLobby/ErrorDisplay.tsx b/frontend/src/pages/MeetingLobby/ErrorDisplay.tsx index c3ce1d4..30074a2 100644 --- a/frontend/src/pages/MeetingLobby/ErrorDisplay.tsx +++ b/frontend/src/pages/MeetingLobby/ErrorDisplay.tsx @@ -6,6 +6,7 @@ interface ErrorDisplayProps { onClearDeviceError: () => void; onRetryDeviceAccess: () => void; onClearError: () => void; + onEnableListenerMode: () => void; } export const ErrorDisplay: React.FC = ({ @@ -14,6 +15,7 @@ export const ErrorDisplay: React.FC = ({ onClearDeviceError, onRetryDeviceAccess, onClearError, + onEnableListenerMode, }) => { if (deviceError) { return ( @@ -27,7 +29,10 @@ export const ErrorDisplay: React.FC = ({ // dialog appears on top of the modal, so the user keeps context. onRetryDeviceAccess(); }} - onDismiss={onClearDeviceError} + onDismiss={() => { + onClearDeviceError(); + onEnableListenerMode(); + }} /> ); diff --git a/frontend/src/pages/MeetingLobby/index.tsx b/frontend/src/pages/MeetingLobby/index.tsx index be78c62..6b24658 100644 --- a/frontend/src/pages/MeetingLobby/index.tsx +++ b/frontend/src/pages/MeetingLobby/index.tsx @@ -69,6 +69,7 @@ export const MeetingLobby: React.FC = ({ deviceError={devices.deviceError} error={displayError} onClearDeviceError={devices.clearDeviceError} + onEnableListenerMode={devices.enableListenerMode} onRetryDeviceAccess={async () => { // Browsers require getUserMedia to be called synchronously within the // user gesture (click). Calling it here directly — before any async