feat: implement tenant OAuth flow and guest achievements
This commit is contained in:
@@ -1,39 +1,67 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
type Stats = { onlineGuests: number; tasksSolved: number; latestPhotoAt?: string };
|
||||
export type EventStats = {
|
||||
onlineGuests: number;
|
||||
tasksSolved: number;
|
||||
latestPhotoAt: string | null;
|
||||
};
|
||||
|
||||
export function usePollStats(slug: string) {
|
||||
const [data, setData] = useState<Stats | null>(null);
|
||||
type StatsResponse = {
|
||||
online_guests?: number;
|
||||
tasks_solved?: number;
|
||||
latest_photo_at?: string;
|
||||
};
|
||||
|
||||
export function usePollStats(slug: string | null | undefined) {
|
||||
const [data, setData] = useState<EventStats>({ onlineGuests: 0, tasksSolved: 0, latestPhotoAt: null });
|
||||
const [loading, setLoading] = useState(true);
|
||||
const timer = useRef<number | null>(null);
|
||||
const visible = typeof document !== 'undefined' ? document.visibilityState === 'visible' : true;
|
||||
const [visible, setVisible] = useState(
|
||||
typeof document !== 'undefined' ? document.visibilityState === 'visible' : true
|
||||
);
|
||||
|
||||
async function fetchOnce() {
|
||||
const canPoll = Boolean(slug);
|
||||
|
||||
async function fetchOnce(activeSlug: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/v1/events/${encodeURIComponent(slug)}/stats`, {
|
||||
const res = await fetch(`/api/v1/events/${encodeURIComponent(activeSlug)}/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 });
|
||||
const json: StatsResponse = await res.json();
|
||||
setData({
|
||||
onlineGuests: json.online_guests ?? 0,
|
||||
tasksSolved: json.tasks_solved ?? 0,
|
||||
latestPhotoAt: json.latest_photo_at ?? null,
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
fetchOnce();
|
||||
function schedule() {
|
||||
if (!visible) return;
|
||||
timer.current = window.setInterval(fetchOnce, 10_000);
|
||||
const onVis = () => setVisible(document.visibilityState === 'visible');
|
||||
document.addEventListener('visibilitychange', onVis);
|
||||
return () => document.removeEventListener('visibilitychange', onVis);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!canPoll) {
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
const activeSlug = String(slug);
|
||||
fetchOnce(activeSlug);
|
||||
if (timer.current) window.clearInterval(timer.current);
|
||||
if (visible) {
|
||||
timer.current = window.setInterval(() => fetchOnce(activeSlug), 10_000);
|
||||
}
|
||||
schedule();
|
||||
return () => {
|
||||
if (timer.current) window.clearInterval(timer.current);
|
||||
};
|
||||
}, [slug, visible]);
|
||||
}, [slug, visible, canPoll]);
|
||||
|
||||
return { loading, onlineGuests: data?.onlineGuests ?? 0, tasksSolved: data?.tasksSolved ?? 0 };
|
||||
return { ...data, loading };
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user