import { useState, useEffect, useRef, useCallback } from 'react'; import { useLocation } from 'react-router-dom'; import { useAuth } from '../context/AuthContext'; const WS_RECONNECT_DELAY = 3000; const PING_INTERVAL = 30000; export function usePresence() { const { token, adminName, isAuthenticated } = useAuth(); const location = useLocation(); const [viewers, setViewers] = useState([]); const wsRef = useRef(null); const pingRef = useRef(null); const reconnectRef = useRef(null); const getWsUrl = useCallback(() => { const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; return `${protocol}//${window.location.host}/api/sessions/live`; }, []); const connect = useCallback(() => { if (!isAuthenticated || !token) return; const ws = new WebSocket(getWsUrl()); wsRef.current = ws; ws.onopen = () => { ws.send(JSON.stringify({ type: 'auth', token })); }; ws.onmessage = (event) => { const msg = JSON.parse(event.data); if (msg.type === 'auth_success') { ws.send(JSON.stringify({ type: 'page_focus', page: location.pathname })); pingRef.current = setInterval(() => { if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ type: 'ping' })); } }, PING_INTERVAL); } if (msg.type === 'presence_update') { const currentPage = location.pathname; const onSamePage = msg.admins .filter(a => a.page === currentPage) .map(a => a.name === adminName ? 'me' : a.name); setViewers(onSamePage); } }; ws.onclose = () => { clearInterval(pingRef.current); reconnectRef.current = setTimeout(connect, WS_RECONNECT_DELAY); }; ws.onerror = () => { ws.close(); }; }, [isAuthenticated, token, adminName, location.pathname, getWsUrl]); useEffect(() => { connect(); return () => { clearTimeout(reconnectRef.current); clearInterval(pingRef.current); if (wsRef.current) { wsRef.current.onclose = null; wsRef.current.close(); } }; }, [connect]); useEffect(() => { if (wsRef.current?.readyState === WebSocket.OPEN) { wsRef.current.send(JSON.stringify({ type: 'page_focus', page: location.pathname })); } }, [location.pathname]); return { viewers }; }