(function () { if (window.Bliss) return; function pageStructureId() { return ( document.body?.dataset?.blissStructureId || document.querySelector("[data-bliss-structure-id]")?.dataset ?.blissStructureId || null ); } function clientId() { const key = "bliss:client_id"; let id = localStorage.getItem(key); if (!id) { id = crypto.randomUUID?.() || String(Date.now()) + "-" + Math.random().toString(36).slice(2); localStorage.setItem(key, id); } return id; } function b64ToU8(s) { const pad = "=".repeat((4 - (s.length % 4)) % 4); const raw = atob((s + pad).replace(/-/g, "+").replace(/_/g, "/")); const a = new Uint8Array(raw.length); for (let i = 0; i < raw.length; i++) a[i] = raw.charCodeAt(i); return a; } async function registration() { if (!("serviceWorker" in navigator)) { throw new Error("Service workers are not supported here."); } return navigator.serviceWorker.register("/_bliss/notification-sw.js"); } async function postVisibility(state) { if (!("serviceWorker" in navigator)) return; const reg = await registration().catch(() => null); reg?.active?.postMessage({ type: "bliss:visibility", state, structureId: pageStructureId(), }); } document.addEventListener("visibilitychange", () => { postVisibility(document.visibilityState === "visible" ? "open" : "closed"); }); if (document.readyState === "loading") { document.addEventListener( "DOMContentLoaded", () => postVisibility("open"), { once: true, }, ); } else { postVisibility("open"); } window.Bliss = { structureId: pageStructureId, clientId, notifications: { supported() { return ( "serviceWorker" in navigator && "PushManager" in window && "Notification" in window ); }, permission() { return "Notification" in window ? Notification.permission : "denied"; }, async register(options = {}) { const structureId = options.structureId || pageStructureId(); if (!structureId) throw new Error("No Bliss structure is active."); if (!this.supported()) { throw new Error("Push notifications are not supported here."); } const reg = await registration(); if (Notification.permission !== "granted") { const permission = await Notification.requestPermission(); if (permission !== "granted") { throw new Error("Notification permission was not granted."); } } const vapidResponse = await fetch("/_bliss/notifications/key"); if (!vapidResponse.ok) { throw new Error("Could not load notification key."); } const { publicKey } = await vapidResponse.json(); const sub = (await reg.pushManager.getSubscription()) || (await reg.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: b64ToU8(publicKey), })); const ua = String(navigator.userAgent || "") + " " + String(navigator.platform || ""); const isKaiOS = /kaios/i.test(ua) || !!navigator.b2g; const response = await fetch("/_bliss/notifications/subscribe", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ structure_id: structureId, client_id: clientId(), subscription: sub, content_encoding: isKaiOS ? "aesgcm" : "aes128gcm", }), }); if (!response.ok) { const detail = await response.text().catch(() => ""); throw new Error(detail || "Could not register notifications."); } await postVisibility("open"); return true; }, }, }; })();