import { useEffect, useRef } from "react"; import { api } from "../api"; import { FeedbackBanner, FreshnessBanner, LoadingState } from "../components/Feedback"; import { useAudioAnnouncements } from "../hooks/useAudioAnnouncements"; import { usePollingResource } from "../hooks/usePollingResource"; import { formatCallAnnouncement, isTimestampStale } from "../lib/format"; import type { CallBatchDto } from "../types"; import { DisplayCenter } from "./AdminPage"; function projectCurrentBatch(value: unknown): CallBatchDto | null { return value && typeof value === "object" ? value as CallBatchDto : null; } export function PublicDisplayCenterPage() { const { announce, audioRef } = useAudioAnnouncements(); const resource = usePollingResource((signal) => api.displayOverview(signal), { intervalMs: 5_000 }); const data = resource.data; const stale = Boolean(data) && isTimestampStale(resource.lastClientSuccessAt, 30_000); const lastCallsRef = useRef | null>(null); useEffect(() => { if (!data) return; const previousCalls = lastCallsRef.current; const nextCalls = new Map(); const announcements: string[] = []; for (const project of data.projects) { const batch = projectCurrentBatch(project.current_batch); const key = batch ? `${batch.batch_number}:${batch.called_at ?? ""}` : null; nextCalls.set(project.id, key); if (!previousCalls?.has(project.id) || !key || previousCalls.get(project.id) === key) continue; const announcement = formatCallAnnouncement(batch); if (announcement) announcements.push(announcement); } lastCallsRef.current = nextCalls; announce(announcements); }, [announce, data]); return (
跳到主要内容
); }