Replace Web Push and its service worker with page notifications over SSE

This commit is contained in:
Ian Gulliver
2026-10-15 18:34:53 -07:00
parent c7f19f2537
commit a1615693ef
32 changed files with 429 additions and 1175 deletions
+15 -68
View File
@@ -1,4 +1,4 @@
import { del, get, post, put } from "./api.js";
import { put } from "./api.js";
import { errorText, h, openPanel, replace, showAlert } from "./dom.js";
import { on, state } from "./state.js";
const kinds = [
@@ -34,84 +34,31 @@ function mountSettings(root) {
return h("label", {}, box, " ", label);
}));
};
let disposed = false;
const renderDevice = async () => {
const status = await deviceStatus();
if (disposed) {
const renderDevice = () => {
if (!("Notification" in window)) {
replace(device, h("p", { class: "hint" }, "This browser doesn't support notifications."));
return;
}
if (status.kind === "unsupported") {
replace(device, h("p", { class: "hint" }, "This browser doesn't support web push."));
return;
}
if (status.kind === "denied") {
if (Notification.permission === "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");
if (Notification.permission === "granted") {
replace(device, h("p", {}, "This browser shows notifications while a byre page is open."));
return;
}
const button = h("button", {}, "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();
await Notification.requestPermission();
renderDevice();
};
replace(device, h("p", {}, status.sub ? "This browser receives notifications." : "This browser doesn't receive notifications."), button);
replace(device, h("p", {}, "This browser doesn't show 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;
renderDevice();
return off;
}
//# sourceMappingURL=settings.js.map