import { useEffect, useRef, useCallback, useState } from 'react'; import { useSession } from 'next-auth/react'; import { RocketChatCallListener, CallEvent } from '@/lib/services/rocketchat-call-listener'; import { useWidgetNotification } from './use-widget-notification'; import { logger } from '@/lib/logger'; import { IncomingCall } from '@/components/incoming-call-notification'; /** * Hook to listen for incoming RocketChat calls and trigger notifications */ export function useRocketChatCalls() { const { data: session } = useSession(); const { triggerNotification } = useWidgetNotification(); const listenerRef = useRef(null); const unsubscribeRef = useRef<(() => void) | null>(null); const initializedRef = useRef(false); const [incomingCall, setIncomingCall] = useState(null); const currentCallRoomIdRef = useRef(null); /** * Get RocketChat credentials for the current user */ const getRocketChatCredentials = useCallback(async () => { if (!session?.user?.id) { console.log('[useRocketChatCalls] No session user ID'); return null; } try { console.log('[useRocketChatCalls] Getting RocketChat credentials...'); // Extract base URL from environment const baseUrl = process.env.NEXT_PUBLIC_IFRAME_PAROLE_URL?.split('/channel')[0]; if (!baseUrl) { console.error('[useRocketChatCalls] RocketChat base URL not configured'); logger.error('[useRocketChatCalls] RocketChat base URL not configured'); return null; } // Get user's RocketChat ID and token const tokenResponse = await fetch('/api/rocket-chat/user-token', { credentials: 'include', }); if (!tokenResponse.ok) { const errorText = await tokenResponse.text(); console.error('[useRocketChatCalls] Failed to get user token', { status: tokenResponse.status, error: errorText, }); logger.error('[useRocketChatCalls] Failed to get user token', { status: tokenResponse.status, }); return null; } const tokenData = await tokenResponse.json(); console.log('[useRocketChatCalls] ✅ Got credentials', { userId: tokenData.userId, hasToken: !!tokenData.authToken, baseUrl, }); return { userId: tokenData.userId, authToken: tokenData.authToken, baseUrl, }; } catch (error) { console.error('[useRocketChatCalls] Error getting credentials', error); logger.error('[useRocketChatCalls] Error getting credentials', { error }); return null; } }, [session?.user?.id]); /** * Initialize call listener */ const initializeListener = useCallback(async () => { if (initializedRef.current || listenerRef.current) { console.log('[useRocketChatCalls] Already initialized, skipping'); return; } console.log('[useRocketChatCalls] Initializing call listener...'); const credentials = await getRocketChatCredentials(); if (!credentials) { console.warn('[useRocketChatCalls] Could not get credentials, skipping initialization'); logger.warn('[useRocketChatCalls] Could not get credentials, skipping initialization'); return; } try { const listener = RocketChatCallListener.getInstance(); const success = await listener.initialize( credentials.userId, credentials.authToken, credentials.baseUrl ); console.log('[useRocketChatCalls] Listener initialization result:', success); if (success) { listenerRef.current = listener; initializedRef.current = true; // Subscribe to call events const unsubscribe = listener.onCall((callEvent: CallEvent) => { // Handle call ended events (when caller hangs up) if (callEvent.type === 'call-ended') { console.log('[useRocketChatCalls] 📞 CALL ENDED - Checking if notification should be removed', { endedRoomId: callEvent.roomId, currentCallRoomId: currentCallRoomIdRef.current, from: callEvent.from.username, }); logger.info('[useRocketChatCalls] Call ended', { roomId: callEvent.roomId, currentCallRoomId: currentCallRoomIdRef.current, from: callEvent.from.username, }); // Only remove notification if it matches the current call's roomId // This prevents removing notifications from other calls if (currentCallRoomIdRef.current === callEvent.roomId || !currentCallRoomIdRef.current) { console.log('[useRocketChatCalls] ✅ Removing notification for ended call', { roomId: callEvent.roomId, }); setIncomingCall(null); currentCallRoomIdRef.current = null; } else { console.log('[useRocketChatCalls] ⏭️ Ignoring call ended event - different room', { endedRoomId: callEvent.roomId, currentCallRoomId: currentCallRoomIdRef.current, }); } return; } // Handle incoming call events if (callEvent.type === 'call-incoming') { console.log('[useRocketChatCalls] 🎉 INCOMING CALL DETECTED!', { from: callEvent.from.username, roomId: callEvent.roomId, roomName: callEvent.roomName, }); logger.info('[useRocketChatCalls] Incoming call detected', { from: callEvent.from.username, roomId: callEvent.roomId, }); // Show incoming call notification UI (Outlook-style rectangle) setIncomingCall({ from: { userId: callEvent.from.userId, username: callEvent.from.username, name: callEvent.from.name || callEvent.from.username, }, roomId: callEvent.roomId, roomName: callEvent.roomName || callEvent.roomId, timestamp: callEvent.timestamp, }); // Track the current call's roomId so we can match it when the call ends currentCallRoomIdRef.current = callEvent.roomId; console.log('[useRocketChatCalls] 📞 Incoming call notification UI set', { from: callEvent.from.username, roomId: callEvent.roomId, }); // Trigger notification badge // For calls, we want to increment the existing count, not replace it // So we fetch current count first, then increment triggerNotification({ source: 'rocketchat', count: 1, // This will be added to existing count in the registry items: [ { id: `call-${callEvent.roomId}-${Date.now()}`, title: `📞 Appel entrant de ${callEvent.from.name || callEvent.from.username}`, message: `Appel vidéo/audio dans ${callEvent.roomName || 'chat'}`, link: `/parole?room=${callEvent.roomId}`, timestamp: callEvent.timestamp, metadata: { type: 'call', from: callEvent.from, roomId: callEvent.roomId, }, }, ], }).then(() => { console.log('[useRocketChatCalls] ✅ Notification triggered successfully'); }).catch((error) => { console.error('[useRocketChatCalls] ❌ Error triggering notification', error); }); } }); unsubscribeRef.current = unsubscribe; console.log('[useRocketChatCalls] ✅ Call listener initialized and subscribed'); logger.debug('[useRocketChatCalls] Call listener initialized'); } else { console.error('[useRocketChatCalls] ❌ Failed to initialize listener'); } } catch (error) { console.error('[useRocketChatCalls] Error initializing listener', error); logger.error('[useRocketChatCalls] Error initializing listener', { error }); } }, [getRocketChatCredentials, triggerNotification]); /** * Cleanup on unmount */ useEffect(() => { if (session?.user?.id) { initializeListener(); } return () => { if (unsubscribeRef.current) { unsubscribeRef.current(); unsubscribeRef.current = null; } if (listenerRef.current) { listenerRef.current.disconnect(); listenerRef.current = null; } initializedRef.current = false; }; }, [session?.user?.id, initializeListener]); /** * Helper function to clear the incoming call notification * This ensures we also clear the roomId reference */ const clearIncomingCall = useCallback(() => { setIncomingCall(null); currentCallRoomIdRef.current = null; }, []); return { incomingCall, setIncomingCall, clearIncomingCall, }; }