40 lines
1.3 KiB
TypeScript
40 lines
1.3 KiB
TypeScript
import { useEffect, useRef, useState } from 'react';
|
|
|
|
type Stats = { onlineGuests: number; tasksSolved: number; latestPhotoAt?: string };
|
|
|
|
export function usePollStats(slug: string) {
|
|
const [data, setData] = useState<Stats | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const timer = useRef<number | null>(null);
|
|
const visible = typeof document !== 'undefined' ? document.visibilityState === 'visible' : true;
|
|
|
|
async function fetchOnce() {
|
|
try {
|
|
const res = await fetch(`/api/v1/events/${encodeURIComponent(slug)}/stats`, {
|
|
headers: { 'Cache-Control': 'no-store' },
|
|
});
|
|
if (res.status === 304) return;
|
|
const json = await res.json();
|
|
setData({ onlineGuests: json.online_guests ?? 0, tasksSolved: json.tasks_solved ?? 0, latestPhotoAt: json.latest_photo_at });
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
|
|
useEffect(() => {
|
|
setLoading(true);
|
|
fetchOnce();
|
|
function schedule() {
|
|
if (!visible) return;
|
|
timer.current = window.setInterval(fetchOnce, 10_000);
|
|
}
|
|
schedule();
|
|
return () => {
|
|
if (timer.current) window.clearInterval(timer.current);
|
|
};
|
|
}, [slug, visible]);
|
|
|
|
return { loading, onlineGuests: data?.onlineGuests ?? 0, tasksSolved: data?.tasksSolved ?? 0 };
|
|
}
|
|
|