Files
buzz-sheet/components/deployment-update-notifier.tsx
T

110 lines
3.5 KiB
TypeScript

"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<string>, 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<string>());
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;
}