'use client'; import { useEffect, useRef } from 'react'; import { io, type Socket } from 'socket.io-client'; import { useAuthStore } from '@/lib/auth-store'; import type { NotificationDto } from '@/lib/notifications-api'; import { useNotificationsStore } from '@/lib/notifications-store'; const SOCKET_URL = process.env['NEXT_PUBLIC_API_URL']?.replace('/api/v1', '') || 'http://localhost:3001'; /** * Hook that manages the Socket.IO connection for real-time notifications. * * - Connects when user is authenticated * - Listens for `notification:new` events * - Auto-reconnects on disconnect * - Disconnects on logout */ export function useSocketNotifications() { const socketRef = useRef(null); const isAuthenticated = useAuthStore((s) => s.isAuthenticated); const { addNotification, incrementUnread, fetchUnreadCount } = useNotificationsStore(); useEffect(() => { if (!isAuthenticated) { // Disconnect if user logs out if (socketRef.current) { socketRef.current.disconnect(); socketRef.current = null; } return; } // Don't create duplicate connections if (socketRef.current?.connected) return; const socket = io(SOCKET_URL, { path: '/socket.io', withCredentials: true, // Send httpOnly auth cookies transports: ['websocket', 'polling'], reconnection: true, reconnectionAttempts: Infinity, reconnectionDelay: 1000, reconnectionDelayMax: 10000, autoConnect: true, }); socket.on('connect', () => { // Fetch unread count on (re)connect to sync state fetchUnreadCount(); }); socket.on('notification:new', (data: NotificationDto) => { addNotification(data); incrementUnread(); }); socket.on('disconnect', (reason) => { // Socket.IO auto-reconnects for transport errors. // Only manual disconnects ('io client disconnect') need explicit reconnect. if (reason === 'io server disconnect') { socket.connect(); } }); socketRef.current = socket; return () => { socket.disconnect(); socketRef.current = null; }; }, [isAuthenticated, addNotification, incrementUnread, fetchUnreadCount]); }