130 lines
3.9 KiB
JavaScript
130 lines
3.9 KiB
JavaScript
(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;
|
|
},
|
|
},
|
|
};
|
|
})();
|