"use client"; import { useEffect, useRef } from "react"; import { toast } from "sonner"; import { hasDeploymentChanged, normalizeDeploymentId, } from "@/lib/deployment/version"; import { parseDeploymentStatusEvent, type DeploymentStatus, } from "@/lib/deployment/status"; const UPDATE_TOAST_ID = "deployment-update"; const NOTICE_DURATION_MS = 10_000; function noticeKey(deploymentId: string, status: DeploymentStatus): string { return `deployment-notice:${deploymentId}:${status}`; } function rememberNotice(seen: Set, key: string): boolean { if (seen.has(key)) return false; try { if (sessionStorage.getItem(key)) return false; sessionStorage.setItem(key, "1"); } catch { // In-memory deduplication still works when storage is unavailable. } seen.add(key); return true; } export function DeploymentUpdateNotifier({ deploymentId, }: { deploymentId: string; }) { const currentDeployment = useRef(normalizeDeploymentId(deploymentId)); const updateAvailable = useRef(false); const seenNotices = useRef(new Set()); useEffect(() => { const source = new EventSource("/api/active"); const showReady = () => { if (updateAvailable.current) return; updateAvailable.current = true; toast("มีอัปเดตใหม่พร้อมใช้งาน", { id: UPDATE_TOAST_ID, description: "รีโหลดเพื่ออัปเดตเป็นเวอร์ชันล่าสุด", action: { label: "รีโหลด", onClick: () => window.location.reload(), }, duration: Infinity, }); }; const receiveDeployment = (event: Event) => { if (!(event instanceof MessageEvent) || typeof event.data !== "string") { return; } const nextDeployment = normalizeDeploymentId(event.data); if (hasDeploymentChanged(currentDeployment.current, nextDeployment)) { showReady(); } }; const receiveStatus = (event: Event) => { if (!(event instanceof MessageEvent) || typeof event.data !== "string") { return; } const update = parseDeploymentStatusEvent(event.data); if ( !update || update.deploymentId === currentDeployment.current || updateAvailable.current ) { return; } const key = noticeKey(update.deploymentId, update.status); if (!rememberNotice(seenNotices.current, key)) return; if (update.status === "ready") { showReady(); } else if (update.status === "deploying") { toast("กำลังอัปเดตเว็บไซต์", { id: UPDATE_TOAST_ID, description: "เวอร์ชันใหม่จะพร้อมใช้งานในอีกไม่กี่นาที", duration: NOTICE_DURATION_MS, }); } else { toast("การอัปเดตล่าช้า", { id: UPDATE_TOAST_ID, description: "เวอร์ชันปัจจุบันยังใช้งานได้ตามปกติ", duration: NOTICE_DURATION_MS, }); } }; source.addEventListener("deployment", receiveDeployment); source.addEventListener("deployment-status", receiveStatus); return () => { source.removeEventListener("deployment", receiveDeployment); source.removeEventListener("deployment-status", receiveStatus); source.close(); }; }, []); return null; }