From d54d54f5f37b4147092e0b697823a998714a1a3d Mon Sep 17 00:00:00 2001 From: saridsa2 Date: Tue, 17 Mar 2026 20:08:22 +0530 Subject: [PATCH] fix: prevent StrictMode double-mount from killing SIP WebSocket connection --- src/hooks/use-sip-phone.ts | 13 +++++++++++-- src/lib/sip-client.ts | 11 +++++++++++ src/providers/sip-provider.tsx | 11 ++++++++--- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/hooks/use-sip-phone.ts b/src/hooks/use-sip-phone.ts index ccfa76d..49cda38 100644 --- a/src/hooks/use-sip-phone.ts +++ b/src/hooks/use-sip-phone.ts @@ -71,6 +71,11 @@ export const useSipPhone = (config?: Partial) => { return; } + // Don't reconnect if already connected or connecting + if (sipClientRef.current?.isConnected() || sipClientRef.current?.isRegistered()) { + return; + } + if (sipClientRef.current) { sipClientRef.current.disconnect(); } @@ -127,11 +132,15 @@ export const useSipPhone = (config?: Partial) => { setIsOnHold(!isOnHold); }, [isOnHold]); - // Cleanup on unmount + // Cleanup only on actual page unload, not StrictMode remount useEffect(() => { - return () => { + const handleUnload = () => { sipClientRef.current?.disconnect(); }; + window.addEventListener('beforeunload', handleUnload); + return () => { + window.removeEventListener('beforeunload', handleUnload); + }; }, []); return { diff --git a/src/lib/sip-client.ts b/src/lib/sip-client.ts index fd8a7bf..fe7a020 100644 --- a/src/lib/sip-client.ts +++ b/src/lib/sip-client.ts @@ -15,15 +15,26 @@ export class SIPClient { ) {} connect(): void { + // Enable JsSIP debug logging to diagnose connection issues + JsSIP.debug.enable('JsSIP:*'); + const socket = new JsSIP.WebSocketInterface(this.config.wsServer); + // Extract SIP ID from URI for authorization_user + // URI format: sip:521814@blr-pub-rtc4.ozonetel.com + const sipId = this.config.uri.replace('sip:', '').split('@')[0]; + const configuration: UAConfiguration = { sockets: [socket], uri: this.config.uri, password: this.config.password, + authorization_user: sipId, display_name: this.config.displayName, register: true, register_expires: 120, + session_timers: false, + connection_recovery_min_interval: 2, + connection_recovery_max_interval: 30, }; this.ua = new JsSIP.UA(configuration); diff --git a/src/providers/sip-provider.tsx b/src/providers/sip-provider.tsx index e9879b4..467279b 100644 --- a/src/providers/sip-provider.tsx +++ b/src/providers/sip-provider.tsx @@ -1,4 +1,4 @@ -import { createContext, useContext, useEffect, type PropsWithChildren } from 'react'; +import { createContext, useContext, useEffect, useRef, type PropsWithChildren } from 'react'; import { useSipPhone } from '@/hooks/use-sip-phone'; type SipContextType = ReturnType; @@ -7,10 +7,15 @@ const SipContext = createContext(null); export const SipProvider = ({ children }: PropsWithChildren) => { const sipPhone = useSipPhone(); + const hasConnected = useRef(false); - // Auto-connect on mount + // Auto-connect on mount — skip StrictMode double-fire useEffect(() => { - sipPhone.connect(); + if (!hasConnected.current) { + hasConnected.current = true; + sipPhone.connect(); + } + // Do NOT disconnect on cleanup — the SIP connection should persist // eslint-disable-next-line react-hooks/exhaustive-deps }, []);