From 7f54a820b0659d5687e2c576b65881450f6bc997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Wed, 11 Nov 2026 19:42:17 +0100 Subject: [PATCH] fix(checkups): clear a done check-up from the Today rail at once Marking a check-up done on the dashboard updated its tile, while the Today rail above it kept listing it as due until the 120 s poll. The reminder writes refreshed the two daily reads only through the inactive pass, which fetches nothing for a query that is on screen. They are now invalidated the ordinary way too, so a mounted rail refetches, and the day root joins them because a check-up marked done is listed on its day. The test fake pinned only the inactive call; a real query client now shows the mounted rail refetching. --- .../use-measurement-reminders-refetch.test.ts | 25 ++++++++++++++++++- src/hooks/use-measurement-reminders.ts | 20 ++++++++++++--- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/hooks/__tests__/use-measurement-reminders-refetch.test.ts b/src/hooks/__tests__/use-measurement-reminders-refetch.test.ts index 56f32ef2f..4c4f01db1 100644 --- a/src/hooks/__tests__/use-measurement-reminders-refetch.test.ts +++ b/src/hooks/__tests__/use-measurement-reminders-refetch.test.ts @@ -1,7 +1,8 @@ import { describe, it, expect, vi } from "vitest"; -import type { QueryClient } from "@tanstack/react-query"; +import { QueryClient, QueryObserver } from "@tanstack/react-query"; import { invalidateReminderReads } from "@/hooks/use-measurement-reminders"; +import { queryKeys } from "@/lib/query-keys"; /** * v1.32.19 — a Vorsorge reminder marked done (or created / edited / deleted) @@ -16,6 +17,7 @@ import { invalidateReminderReads } from "@/hooks/use-measurement-reminders"; function fakeQueryClient(): QueryClient { return { invalidateQueries: vi.fn().mockResolvedValue(undefined), + getQueryCache: () => ({ findAll: () => [] }), } as unknown as QueryClient; } @@ -47,4 +49,25 @@ describe("invalidateReminderReads — the Vorsorge daily-rail freshness contract expect(inactiveKeys).toContain(JSON.stringify(["dashboard", "snapshot"])); expect(inactiveKeys).toContain(JSON.stringify(["daily", "digest"])); }); + + it("refetches the Today rail while it is on screen", async () => { + // The dashboard: the check-up tile and the Today rail are both mounted, + // where the inactive pass alone fetches nothing. + const client = new QueryClient({ + defaultOptions: { queries: { retry: false, staleTime: Infinity } }, + }); + let reads = 0; + const observer = new QueryObserver(client, { + queryKey: queryKeys.dailyDigest(), + queryFn: async () => ++reads, + }); + const unsubscribe = observer.subscribe(() => {}); + await vi.waitFor(() => expect(reads).toBe(1)); + + await invalidateReminderReads(client); + + expect(reads).toBe(2); + unsubscribe(); + client.clear(); + }); }); diff --git a/src/hooks/use-measurement-reminders.ts b/src/hooks/use-measurement-reminders.ts index b42a564c8..b2376dddc 100644 --- a/src/hooks/use-measurement-reminders.ts +++ b/src/hooks/use-measurement-reminders.ts @@ -17,7 +17,11 @@ import { } from "@tanstack/react-query"; import { apiDelete, apiGet, apiPatch, apiPost } from "@/lib/api/api-fetch"; -import { queryKeys, refetchInactiveDailyReads } from "@/lib/query-keys"; +import { + invalidateKeys, + queryKeys, + refetchInactiveDailyReads, +} from "@/lib/query-keys"; /** * v1.32.19 — the read fan-out every Vorsorge reminder mutation runs on @@ -31,11 +35,21 @@ import { queryKeys, refetchInactiveDailyReads } from "@/lib/query-keys"; * (`refetchOnMount: false`) — the reminder lingered on the Today rail until * the 120 s poll. Force the inactive daily reads to refetch alongside the * reminders invalidation so the rail clears at once, the same freshness - * contract the medication intake surfaces use. + * contract the medication intake surfaces use. The inactive pass fetches + * nothing for a read that is on screen, though: marked done from the + * dashboard tile, the check-up stayed due on the Today rail above it. So + * the two daily reads are invalidated the ordinary way as well, which + * refetches them where they are mounted. A check-up marked done is also + * listed on its day. */ export function invalidateReminderReads(qc: QueryClient): Promise { return Promise.all([ - qc.invalidateQueries({ queryKey: queryKeys.measurementReminders() }), + invalidateKeys(qc, [ + queryKeys.measurementReminders(), + queryKeys.dailyDigest(), + queryKeys.dashboardSnapshot(), + queryKeys.dayRoot(), + ]), refetchInactiveDailyReads(qc), ]); }