"use client"; import { useEffect, useRef, useState } from "react"; import { useTranslations } from "next-intl"; import { RefreshCw } from "lucide-react"; import { configureOffline, startSyncLoop } from "@/lib/offline/outbox"; import { SyncBadge } from "./sync-badge"; const SW_URL = "/sw.js"; const SW_DEV_FLAG = "craftvia.sw"; const UPDATE_CHECK_MS = 30 * 60_000; /** In development the SW is opt-in (localStorage craftvia.sw = "1"), otherwise HMR chunks would be cached. */ function swEnabled(): boolean { if (typeof navigator === "undefined" || !("serviceWorker" in navigator)) return false; if (process.env.NODE_ENV === "production") return true; try { return localStorage.getItem(SW_DEV_FLAG) === "1"; } catch { return false; } } export function OfflineRuntimeClient({ tenantId, userId, maxDays }: { tenantId: string; userId: string; maxDays: number }) { const t = useTranslations("offline.update"); const [waiting, setWaiting] = useState(null); const reloading = useRef(false); // outbox context + sync loop useEffect(() => { let stop: (() => void) | undefined; let cancelled = false; configureOffline({ tenantId, userId }, { maxDays }).then(() => { if (!cancelled) stop = startSyncLoop(); }); return () => { cancelled = true; stop?.(); }; }, [tenantId, userId, maxDays]); // service worker registration + update flow useEffect(() => { if (!swEnabled()) return; let timer: ReturnType | undefined; const onControllerChange = () => { if (!reloading.current) return; reloading.current = false; window.location.reload(); }; navigator.serviceWorker.addEventListener("controllerchange", onControllerChange); navigator.serviceWorker .register(SW_URL, { scope: "/", updateViaCache: "none" }) .then((reg) => { if (reg.waiting && navigator.serviceWorker.controller) setWaiting(reg.waiting); reg.addEventListener("updatefound", () => { const installing = reg.installing; installing?.addEventListener("statechange", () => { if (installing.state === "installed" && navigator.serviceWorker.controller) setWaiting(installing); }); }); // refresh the cached offline page with the current build and the signed-in session (reg.active ?? reg.installing ?? reg.waiting)?.postMessage({ type: "PRECACHE_OFFLINE" }); timer = setInterval(() => void reg.update().catch(() => undefined), UPDATE_CHECK_MS); }) .catch(() => undefined); return () => { navigator.serviceWorker.removeEventListener("controllerchange", onControllerChange); if (timer) clearInterval(timer); }; }, []); return ( <> {waiting && (

{t("available")}

)} ); }