import { del, get, post, put } from "./api.js"; import { errorText, h, replace, showAlert } from "./dom.js"; import { on, state } from "./state.js"; const kinds = [ { key: "blocking_questions", label: "Blocking questions" }, { key: "questions", label: "Non-blocking questions" }, { key: "reports", label: "Turn summaries" }, { key: "errors", label: "Session errors" }, { key: "rate_limits", label: "Rate limits" }, ]; export function mountSettings(root) { const device = h("div", { class: "push-device" }); const prefs = h("fieldset", { class: "push-prefs" }); const renderPrefs = () => { const me = state.me; if (!me) { replace(prefs); return; } replace(prefs, h("legend", {}, "Notify me about"), ...kinds.map(({ key, label }) => { const box = h("input", { type: "checkbox", checked: me.notification_prefs[key] }); box.onchange = async () => { try { state.me = await put("/api/me/notifications", { body: { ...me.notification_prefs, [key]: box.checked } }); } catch (err) { void showAlert(errorText(err)); } renderPrefs(); }; return h("label", {}, box, " ", label); })); }; let disposed = false; const renderDevice = async () => { const status = await deviceStatus(); if (disposed) { return; } if (status.kind === "unsupported") { replace(device, h("p", { class: "hint" }, "This browser doesn't support web push.")); return; } if (status.kind === "denied") { replace(device, h("p", { class: "hint" }, "Notifications are blocked for this site in the browser's settings.")); return; } const button = h("button", {}, status.sub ? "Turn off on this browser" : "Turn on for this browser"); button.onclick = async () => { button.disabled = true; try { if (status.sub) { await disable(status.sub); } else { await enable(status.reg); } } catch (err) { void showAlert(errorText(err)); } await renderDevice(); }; replace(device, h("p", {}, status.sub ? "This browser receives notifications." : "This browser doesn't receive notifications."), button); }; replace(root, h("div", { class: "settings-view" }, h("h2", {}, "Notifications"), device, prefs)); const off = on("me", renderPrefs); renderPrefs(); void renderDevice().catch((err) => replace(device, h("p", { class: "error-text" }, errorText(err)))); return () => { disposed = true; off(); }; } async function deviceStatus() { if (!("serviceWorker" in navigator) || !("PushManager" in window) || !("Notification" in window)) { return { kind: "unsupported" }; } if (Notification.permission === "denied") { return { kind: "denied" }; } const reg = await navigator.serviceWorker.ready; const sub = await reg.pushManager.getSubscription(); if (sub) { await register(sub); } return { kind: "ready", reg, sub }; } async function enable(reg) { const permission = await Notification.requestPermission(); if (permission !== "granted") { return; } const key = await get("/api/push/key"); const sub = await reg.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: base64url(key.public_key) }); await register(sub); } async function disable(sub) { await del("/api/me/push-subscriptions", { query: { endpoint: sub.endpoint } }); await sub.unsubscribe(); } async function register(sub) { const keys = sub.toJSON().keys ?? {}; await post("/api/me/push-subscriptions", { body: { endpoint: sub.endpoint, p256dh: keys["p256dh"] ?? "", auth: keys["auth"] ?? "" } }); } function base64url(s) { const bin = atob(s.replace(/-/g, "+").replace(/_/g, "/")); const out = new Uint8Array(bin.length); for (let i = 0; i < bin.length; i++) { out[i] = bin.charCodeAt(i); } return out; } //# sourceMappingURL=settings.js.map