"use client"; function urlBase64ToUint8Array(base64String: string): Uint8Array { const padding = "=".repeat((4 - (base64String.length % 4)) % 4); const base64 = (base64String + padding).replace(/-/g, "+").replace(/_/g, "/"); const rawData = window.atob(base64); const outputArray = new Uint8Array(rawData.length); for (let i = 0; i < rawData.length; i++) { outputArray[i] = rawData.charCodeAt(i); } return outputArray; } /** * Registers the service worker and makes sure a push subscription exists * and is saved server-side. Never throws or blocks the page on failure — * this is an enhancement on top of the in-page toast — but every step logs * to the console (visible in DevTools) since a silent no-op is otherwise * impossible to tell apart from "it worked" when diagnosing remotely. */ export async function ensurePushSubscribed(): Promise { if (typeof window === "undefined") return; if (!("serviceWorker" in navigator) || !("PushManager" in window)) { console.warn("[push] not supported in this browser"); return; } if (Notification.permission !== "granted") { console.warn("[push] Notification permission is", Notification.permission, "- skipping subscribe"); return; } try { const registration = await navigator.serviceWorker.register("/sw.js"); await navigator.serviceWorker.ready; console.log("[push] service worker registered:", registration.scope); let subscription = await registration.pushManager.getSubscription(); if (subscription) { console.log("[push] reusing existing subscription:", subscription.endpoint); } else { const keyRes = await fetch("/api/push/vapid-public-key"); if (!keyRes.ok) { console.error("[push] /api/push/vapid-public-key returned", keyRes.status, "- is VAPID configured server-side?"); return; } const { publicKey } = await keyRes.json(); subscription = await registration.pushManager.subscribe({ userVisibleOnly: true, // lib.dom's BufferSource typing is stricter than the runtime value // actually needs to satisfy — this Uint8Array is always backed by a // plain (non-shared) ArrayBuffer. applicationServerKey: urlBase64ToUint8Array(publicKey) as BufferSource, }); console.log("[push] created new subscription:", subscription.endpoint); } const saveRes = await fetch("/api/push/subscribe", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ...subscription.toJSON(), userAgent: navigator.userAgent }), }); if (saveRes.ok) { console.log("[push] subscription saved server-side — done"); } else { console.error("[push] failed to save subscription server-side:", saveRes.status, await saveRes.text()); } } catch (err) { console.error("[push] ensurePushSubscribed failed:", err); } }