Calendar reminders as push notifications

A job every minute finds the event reminders that fell due — repeating events
and moved occurrences expanded as the calendar view does — and pushes each
once to every device with notifications on (sdk.push, new: plugins can notify
through the new-mail push delivery). A reminder missed while the worker was down
is still sent within 10 minutes; cancelled events and declined invitations stay
quiet. Title, then "In 15 minutes, 14:00 · Room 2" in the user's zone and clock
format; it stays until dismissed and shows even with the app open; clicking it
opens that day (/p/calendar?date=…, new deep link).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-09 21:49:04 +02:00
co-authored by Claude Opus 5.5
parent 9611aedfcd
commit fa552392c8
6 changed files with 385 additions and 10 deletions
+13 -6
View File
@@ -501,11 +501,13 @@ self.addEventListener("activate", (event) => {
);
});
// ---- notifications about new mail ---------------------------------------------------
// ---- notifications: new mail, calendar reminders ------------------------------------
//
// The server (app/push/service.py) sends { type: "mail", title, body, tag, url,
// unread } encrypted to this browser. One notification per conversation (`tag`),
// replaced when more arrives in it; the app icon's badge shows unread Inbox mail.
// A plugin (sdk.push) sends { type: "plugin.<name>", title, body, tag, url,
// sticky } — a calendar reminder.
/** A window of the app the user is looking at right now. */
async function lookingAtApp() {
@@ -517,16 +519,21 @@ async function showMail(data) {
if (typeof data.unread === "number" && self.navigator.setAppBadge) {
await (data.unread ? self.navigator.setAppBadge(data.unread) : self.navigator.clearAppBadge()).catch(() => {});
}
// The app is open in front of the user: the new mail is already on screen.
// Not on Apple's push service, which withdraws permission from a site whose
// New mail while the app is open in front of the user: it is already on
// screen. (Other notifications — a calendar reminder — show regardless.) Not
// on Apple's push service, which withdraws permission from a site whose
// pushes show nothing.
const sub = await self.registration.pushManager.getSubscription();
const apple = Boolean(sub?.endpoint.includes(".push.apple.com"));
if (!apple && (await lookingAtApp())) return;
if (!data.type || data.type === "mail") {
const sub = await self.registration.pushManager.getSubscription();
const apple = Boolean(sub?.endpoint.includes(".push.apple.com"));
if (!apple && (await lookingAtApp())) return;
}
await self.registration.showNotification(data.title || "New mail", {
body: data.body || "",
tag: data.tag || "new-mail",
renotify: true,
// A reminder stays until dismissed (where the platform allows it).
requireInteraction: Boolean(data.sticky),
icon: "/icon-192.png",
badge: "/badge-96.png",
timestamp: Date.now(),