fix(push): stop handling the clear push in the service worker

The worker answered a `type: "clear"` push by closing the dose
reminder for that slot and showing nothing. Browsers do not allow a push
without a visible notification on a subscription that promised one:
Safari removes the subscription after a few, Chromium shows its own
"updated in the background" notice. The server no longer sends it, so
the branch and its tests go; a reminder stays until it is tapped or the
next reminder with the same tag replaces it.
This commit is contained in:
Marc-André Bombeck committed 2026-11-11 20:39:08 +01:00
1 parent b3b4c9684e
commit 25d087aaa2
3 files changed
+5 -67

No files matched your search

-21
View File
@@ -530,27 +530,6 @@ self.addEventListener("push", (event) => {
payload = { title: "HealthLog", body: event.data.text() };
}
// v1.18.4 — `type:"clear"` is the PWA equivalent of ending an iOS Live
// Activity: the server sends it when a dose is logged so the still-pending
// dose-due reminder for that slot is closed here (matched on its stable
// `tag`), and the app badge re-reflects the outstanding-dose count. No new
// notification is shown.
if (payload && payload.type === "clear") {
event.waitUntil(
(async () => {
const tagToClear = payload.tag;
if (tagToClear) {
const matches = await self.registration.getNotifications({
tag: tagToClear,
});
for (const n of matches) n.close();
}
applyAppBadge(payload.badge);
})(),
);
return;
}
const {
title = "HealthLog",
body = "",
@@ -1,7 +1,7 @@
{
"$comment": "Comment lines that open with a release version, per file under src/. Maintained by src/__tests__/version-stamp-ratchet.test.ts.",
"count": 2888,
"total": 9204,
"count": 2887,
"total": 9203,
"entries": {
"__tests__/account-payload-consumer-guard.test.ts": 2,
"__tests__/acting-account-boundary-guard.test.ts": 7,
@@ -55,7 +55,6 @@
"__tests__/recurrence-course-anchor-guard.test.ts": 1,
"__tests__/relation-links-backup-completeness.test.ts": 1,
"__tests__/resolved-field-consumer-guard.test.ts": 1,
"__tests__/service-worker.test.ts": 1,
"__tests__/session-surface-guard.test.ts": 7,
"__tests__/settings-legacy-routes.test.ts": 1,
"__tests__/shared-record-navigation.test.tsx": 1,
+3 -43
View File
@@ -158,8 +158,8 @@ function bootServiceWorker(): SwHarness {
navPreload.enabled = true;
},
};
// v1.18.4 — push-handler fakes: a notification store the `getNotifications`
// / `showNotification` calls read/write, and a Badging-API spy.
// Push-handler fakes: the notifications `showNotification` shows, and a
// Badging-API spy.
const shown: Array<{ title: string; tag?: string; closed: boolean }> = [];
const badge = { value: undefined as number | undefined, cleared: false };
const selfObj = {
@@ -180,14 +180,6 @@ function bootServiceWorker(): SwHarness {
showNotification: async (title: string, opts: { tag?: string }) => {
shown.push({ title, tag: opts?.tag, closed: false });
},
getNotifications: async ({ tag }: { tag?: string }) =>
shown
.filter((n) => !n.closed && (tag === undefined || n.tag === tag))
.map((n) => ({
close: () => {
n.closed = true;
},
})),
},
navigator: {
setAppBadge: (n: number) => {
@@ -1080,7 +1072,7 @@ describe("sw.js — trimCache", () => {
});
});
// ── push handler (v1.18.4: clear-on-taken tag close + app badge) ─────────────
// ── push handler ─────────────────────────────────────────────────────────────
async function dispatchPush(harness: SwHarness, data: unknown): Promise<void> {
let settled: Promise<unknown> = Promise.resolve();
harness.listeners.get("push")!({
@@ -1117,36 +1109,4 @@ describe("sw.js — push handler", () => {
expect(shown[0].tag).toBe(tag);
expect(badge.value).toBe(3);
});
it("a type:clear push closes the matching-tag notification (no new one) and updates the badge", async () => {
const harness = bootServiceWorker();
const { shown, badge } = swFakes(harness);
const tag = "med:med-1:2026-06-18T07:00:00.000Z";
await dispatchPush(harness, { title: "dose", tag, badge: 1 });
expect(shown.filter((n) => !n.closed)).toHaveLength(1);
await dispatchPush(harness, { type: "clear", tag, badge: 0 });
// The pending reminder for that slot is closed; no new notification shown.
expect(shown).toHaveLength(1);
expect(shown[0].closed).toBe(true);
// badge: 0 clears the app badge.
expect(badge.cleared).toBe(true);
});
it("a type:clear push only closes its own slot's tag", async () => {
const harness = bootServiceWorker();
const { shown } = swFakes(harness);
await dispatchPush(harness, { title: "a", tag: "med:a:t1" });
await dispatchPush(harness, { title: "b", tag: "med:b:t2" });
await dispatchPush(harness, { type: "clear", tag: "med:a:t1" });
const a = shown.find((n) => n.tag === "med:a:t1")!;
const b = shown.find((n) => n.tag === "med:b:t2")!;
expect(a.closed).toBe(true);
expect(b.closed).toBe(false);
});
});