mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-13 14:32:03 +00:00
fix(pwa): clear the dose count earlier releases left on the app icon
The server set the Home Screen badge only through the clear push sent for each logged dose, and that push is gone. A device whose last clear left a count (one forgotten dose, an update in the middle of the day) kept that number on the icon for good, since iOS never clears a web app's badge by itself. The worker now clears the badge once on activate. The badge handling in the push handler, the unused outstanding-dose counter and the test of a payload no server sends are removed, and the self-hosting guide no longer promises the clear push or the badge.
This commit is contained in:
1 parent
1dde3034c6
commit
38299f7c6e
6 files changed
+50
-114
No files matched your search
@@ -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": 2877,
|
||||
"total": 9165,
|
||||
"count": 2876,
|
||||
"total": 9163,
|
||||
"entries": {
|
||||
"__tests__/account-payload-consumer-guard.test.ts": 2,
|
||||
"__tests__/acting-account-boundary-guard.test.ts": 7,
|
||||
@@ -2502,7 +2502,6 @@
|
||||
"lib/medications/list-read.ts": 23,
|
||||
"lib/medications/med-target-map.ts": 1,
|
||||
"lib/medications/medication-order.ts": 1,
|
||||
"lib/medications/outstanding-doses.ts": 2,
|
||||
"lib/medications/route-guards.ts": 1,
|
||||
"lib/medications/schedule-units-dto.ts": 1,
|
||||
"lib/medications/scheduled-doses.ts": 1,
|
||||
|
||||
@@ -245,7 +245,6 @@ describe("medication intake tracking — intake-row readers honour the switch",
|
||||
"lib/jobs/coach-nudge.ts",
|
||||
"lib/insights/general-status.ts",
|
||||
"lib/analytics/correlations-fast-path.ts",
|
||||
"lib/medications/outstanding-doses.ts",
|
||||
]) {
|
||||
expect(intakeReaders).toContain(known);
|
||||
}
|
||||
|
||||
@@ -158,10 +158,9 @@ function bootServiceWorker(): SwHarness {
|
||||
navPreload.enabled = true;
|
||||
},
|
||||
};
|
||||
// Push-handler fakes: the notifications `showNotification` shows, and a
|
||||
// Badging-API spy.
|
||||
// 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 badge = { cleared: false };
|
||||
const selfObj = {
|
||||
addEventListener: (type: string, fn: (event: unknown) => void) => {
|
||||
listeners.set(type, fn);
|
||||
@@ -182,12 +181,8 @@ function bootServiceWorker(): SwHarness {
|
||||
},
|
||||
},
|
||||
navigator: {
|
||||
setAppBadge: (n: number) => {
|
||||
badge.value = n;
|
||||
},
|
||||
clearAppBadge: () => {
|
||||
clearAppBadge: async () => {
|
||||
badge.cleared = true;
|
||||
badge.value = undefined;
|
||||
},
|
||||
},
|
||||
location: { origin: ORIGIN },
|
||||
@@ -486,6 +481,15 @@ describe("sw.js — activate", () => {
|
||||
);
|
||||
expect(harness.navPreload.enabled).toBe(true);
|
||||
});
|
||||
|
||||
it("clears the dose count an earlier release left on the app icon", async () => {
|
||||
const harness = bootServiceWorker();
|
||||
const { badge } = swFakes(harness);
|
||||
|
||||
await dispatchActivate(harness);
|
||||
|
||||
expect(badge.cleared).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("sw.js — authenticated install cache isolation", () => {
|
||||
@@ -1094,27 +1098,25 @@ async function dispatchPush(harness: SwHarness, data: unknown): Promise<void> {
|
||||
function swFakes(harness: SwHarness) {
|
||||
const selfObj = harness.context.self as unknown as {
|
||||
__shown: Array<{ title: string; tag?: string; closed: boolean }>;
|
||||
__badge: { value: number | undefined; cleared: boolean };
|
||||
__badge: { cleared: boolean };
|
||||
};
|
||||
return { shown: selfObj.__shown, badge: selfObj.__badge };
|
||||
}
|
||||
|
||||
describe("sw.js — push handler", () => {
|
||||
it("shows a reminder with its stable tag and sets the app badge", async () => {
|
||||
it("shows a reminder with its stable tag", async () => {
|
||||
const harness = bootServiceWorker();
|
||||
const { shown, badge } = swFakes(harness);
|
||||
const { shown } = swFakes(harness);
|
||||
const tag = "med:med-1:2026-06-18T07:00:00.000Z";
|
||||
|
||||
await dispatchPush(harness, {
|
||||
title: "Time for your dose",
|
||||
body: "Ramipril 5mg",
|
||||
tag,
|
||||
badge: 3,
|
||||
});
|
||||
|
||||
expect(shown).toHaveLength(1);
|
||||
expect(shown[0].tag).toBe(tag);
|
||||
expect(badge.value).toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
/**
|
||||
* v1.18.4 — server-authoritative count of a user's still-outstanding doses
|
||||
* for the current local day. Drives the PWA app badge (`navigator.setAppBadge`)
|
||||
* so the installed icon mirrors how many doses still need logging — the
|
||||
* free-tier equivalent of the iOS widget's pending count.
|
||||
*
|
||||
* Outstanding = a today-scheduled `MedicationIntakeEvent` that is neither
|
||||
* taken (`takenAt == null`) nor skipped (`skipped == false`) nor swept by the
|
||||
* miss cutoff (`autoMissed == false`). Pending rows are minted by
|
||||
* `projectTodayIntakesAndRecompute`; this is a read-only count over the same
|
||||
* window, so callers that have just projected today's rows get an accurate
|
||||
* total.
|
||||
*/
|
||||
import { prisma } from "@/lib/db";
|
||||
import { getUserTodayBounds } from "@/lib/tz/local-day";
|
||||
import { TRACKED_INTAKE_EVENT_WHERE } from "@/lib/medications/intake-tracking";
|
||||
|
||||
/**
|
||||
* Count the user's outstanding (pending) doses for today in their timezone.
|
||||
* Best-effort: any failure returns 0 so a badge update never blocks a caller.
|
||||
*/
|
||||
export async function countOutstandingDosesToday(
|
||||
userId: string,
|
||||
tz: string,
|
||||
): Promise<number> {
|
||||
try {
|
||||
const { start, end } = getUserTodayBounds(new Date(), tz);
|
||||
return await prisma.medicationIntakeEvent.count({
|
||||
where: {
|
||||
userId,
|
||||
scheduledFor: { gte: start, lte: end },
|
||||
takenAt: null,
|
||||
skipped: false,
|
||||
autoMissed: false,
|
||||
// A tombstoned placeholder is gone for every read surface; it must
|
||||
// not keep a badge count alive.
|
||||
deletedAt: null,
|
||||
// v1.39.1 (#1033) — nothing of a medication with intake tracking
|
||||
// off is outstanding.
|
||||
...TRACKED_INTAKE_EVENT_WHERE,
|
||||
},
|
||||
});
|
||||
} catch {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user