mathly / mathly2 /static /notifications.js
3v324v23's picture
Claude Sonnet 5.5
mathly2: written work, rounds, caching, one course for all classes, lesson schedule
0815e01
Raw History Blame Contribute Delete
2.79 kB
// Notifications for photo requests, on a phone only: a switch on home. The service worker shows the notification when the app is closed and, on a
// tap, tells the open app to look for requests. Without a push key on the server the switch is not offered.
import { api } from "./api.js";
import { isTouch } from "./media.js";
const bytes = base64 => Uint8Array.from(atob(base64.replace(/-/g, "+").replace(/_/g, "/") + "=".repeat((4 - base64.length % 4) % 4)), c => c.charCodeAt(0));
/** Draw the switch into `box` (nothing at all on a computer, without notification support, or when the server has no key). */
export async function setup(box) {
if (!isTouch() || !("serviceWorker" in navigator) || !("PushManager" in window) || !("Notification" in window)) return;
let server;
try { server = await api("/api/push/key"); } catch { return; }
if (!server.enabled) return;
let registration;
try { registration = await navigator.serviceWorker.register("/service-worker.js", { scope: "/" }); } catch { return; }
let subscription = await registration.pushManager.getSubscription();
const draw = (note = "") => {
box.hidden = false;
box.innerHTML = `<h3>ื”ืชืจืื•ืช</h3><p class="muted">ื›ืฉื”ืžื—ืฉื‘ ืžื‘ืงืฉ ืชืžื•ื ื” ืฉืœ ืชืฉื•ื‘ื”, ื”ื˜ืœืคื•ืŸ ื™ืงื‘ืœ ื”ืชืจืื” ื’ื ื›ืฉื”ืืคืœื™ืงืฆื™ื” ืกื’ื•ืจื”. ื‘ืื™ื™ืคื•ืŸ ื–ื” ืขื•ื‘ื“ ืจืง ืžื”ืืคืœื™ืงืฆื™ื” ืฉื”ื•ืกืคืชื ืœืžืกืš ื”ื‘ื™ืช.</p>
<p>${subscription ? `<span class="tag" id="notif-state"><span aria-hidden="true">โœ“</span> ื”ืชืจืื•ืช ืคื•ืขืœื•ืช</span> <button type="button" id="notif-off">ื›ื™ื‘ื•ื™</button>`
: `<button type="button" class="primary" id="notif-on">ื”ืคืขืœืช ื”ืชืจืื•ืช</button>`}</p><p class="muted" id="notif-note" role="status">${note}</p>`;
box.querySelector("#notif-on")?.addEventListener("click", enable);
box.querySelector("#notif-off")?.addEventListener("click", disable);
};
async function enable() {
try {
if (await Notification.requestPermission() !== "granted") return draw("ื”ื”ืชืจืื•ืช ื—ืกื•ืžื•ืช ื‘ื“ืคื“ืคืŸ. ืืคืฉืจ ืœืืฉืจ ืื•ืชืŸ ื‘ื”ื’ื“ืจื•ืช.");
subscription = await registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: bytes(server.key) });
await api("/api/push/subscribe", { method: "POST", body: subscription.toJSON() });
draw();
} catch (error) { subscription = null; draw(error.message || "ืœื ื”ืฆืœื—ื ื• ืœื”ืคืขื™ืœ ื”ืชืจืื•ืช."); }
}
async function disable() {
const endpoint = subscription?.endpoint;
try { await subscription?.unsubscribe(); if (endpoint) await api("/api/push/unsubscribe", { method: "POST", body: { endpoint } }); } catch { /* it is off here either way */ }
subscription = null;
draw();
}
draw();
}