Files
jackboxpartypack-gamepicker/frontend/src/hooks/usePresence.js
2026-03-23 09:57:22 -04:00

84 lines
2.3 KiB
JavaScript

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 };
}