Initialize repo and add session changes (2025-09-08)

This commit is contained in:
Auto Commit
2025-09-08 14:03:43 +02:00
commit 44ab0a534b
327 changed files with 40952 additions and 0 deletions

View File

@@ -0,0 +1,42 @@
import { useEffect, useRef, useState } from 'react';
type Photo = { id: number; file_path?: string; thumbnail_path?: string; created_at?: string };
export function usePollGalleryDelta(slug: string) {
const [photos, setPhotos] = useState<Photo[]>([]);
const [loading, setLoading] = useState(true);
const [newCount, setNewCount] = useState(0);
const latestAt = useRef<string | null>(null);
const timer = useRef<number | null>(null);
async function fetchDelta() {
const qs = latestAt.current ? `?since=${encodeURIComponent(latestAt.current)}` : '';
const res = await fetch(`/api/v1/events/${encodeURIComponent(slug)}/photos${qs}`, {
headers: { 'Cache-Control': 'no-store' },
});
if (res.status === 304) return;
const json = await res.json();
if (Array.isArray(json.data)) {
const added = json.data.length;
const merged = latestAt.current ? [...json.data, ...photos] : json.data;
if (added > 0 && latestAt.current) setNewCount((c) => c + added);
setPhotos(merged);
}
if (json.latest_photo_at) latestAt.current = json.latest_photo_at;
setLoading(false);
}
useEffect(() => {
setLoading(true);
latestAt.current = null;
setPhotos([]);
fetchDelta();
timer.current = window.setInterval(fetchDelta, 30_000);
return () => {
if (timer.current) window.clearInterval(timer.current);
};
}, [slug]);
function acknowledgeNew() { setNewCount(0); }
return { loading, photos, newCount, acknowledgeNew };
}

View File

@@ -0,0 +1,39 @@
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 };
}