diff --git a/src/backend/core/api/__init__.py b/src/backend/core/api/__init__.py index 8a419ee5..05ac309d 100644 --- a/src/backend/core/api/__init__.py +++ b/src/backend/core/api/__init__.py @@ -50,6 +50,7 @@ def get_frontend_configuration(request): else None, "default_country": settings.ROOM_TELEPHONY_DEFAULT_COUNTRY, }, + "livekit": {"url": settings.LIVEKIT_CONFIGURATION["url"]}, } frontend_configuration.update(settings.FRONTEND_CONFIGURATION) return Response(frontend_configuration) diff --git a/src/frontend/src/api/useConfig.ts b/src/frontend/src/api/useConfig.ts index b1673a50..876775da 100644 --- a/src/frontend/src/api/useConfig.ts +++ b/src/frontend/src/api/useConfig.ts @@ -37,6 +37,9 @@ export interface ApiConfig { default_country?: string } manifest_link?: string + livekit: { + url: string + } } const fetchConfig = (): Promise => { diff --git a/src/frontend/src/features/rooms/components/Conference.tsx b/src/frontend/src/features/rooms/components/Conference.tsx index a2dc831f..d84cc6cd 100644 --- a/src/frontend/src/features/rooms/components/Conference.tsx +++ b/src/frontend/src/features/rooms/components/Conference.tsx @@ -24,6 +24,7 @@ import { LocalUserChoices } from '@/stores/userChoices' import { navigateTo } from '@/navigation/navigateTo' import { MediaDeviceErrorAlert } from './MediaDeviceErrorAlert' import { usePostHog } from 'posthog-js/react' +import { useConfig } from '@/api/useConfig' export const Conference = ({ roomId, @@ -37,12 +38,15 @@ export const Conference = ({ initialRoomData?: ApiRoom }) => { const posthog = usePostHog() + const { data: apiConfig } = useConfig() useEffect(() => { posthog.capture('visit-room', { slug: roomId }) }, [roomId, posthog]) const fetchKey = [keys.room, roomId] + const [isConnectionWarmedUp, setIsConnectionWarmedUp] = useState(false) + const { mutateAsync: createRoom, status: createStatus, @@ -53,6 +57,36 @@ export const Conference = ({ }, }) + /** + * Warm up connection to LiveKit server before joining room + * This prefetch helps reduce initial connection latency by establishing + * an early HTTP connection to the WebRTC signaling server + * + * FIREFOX + PROXY WORKAROUND: + * On Firefox behind proxy configurations, WebSocket signaling fails to establish. + * Client receives HTTP 200 instead of expected 101 (Switching Protocols). + * This appears to be a certificate/security issue where the initial request + * is considered unsecure. By first calling the signaling server via HTTPS, + * subsequent WebSocket establishment works correctly in these setups. + * This is a temporary workaround - issue is reproducible on LiveKit's demo app. + */ + useEffect(() => { + const warmUpLivekitConnection = async () => { + if (isConnectionWarmedUp || !apiConfig) return + try { + // Make HTTPS request to establish secure connection + // This resolves Firefox+proxy WebSocket handshake issues + await fetch(apiConfig.livekit.url) + setIsConnectionWarmedUp(true) + } catch (error) { + // Don't block room connection if warmup fails + console.warn('LiveKit connection warmup failed:', error) + setIsConnectionWarmedUp(true) + } + } + warmUpLivekitConnection() + }, [isConnectionWarmedUp, apiConfig]) + const { status: fetchStatus, isError: isFetchError, @@ -125,9 +159,9 @@ export const Conference = ({