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.
This commit is contained in:
Marc-André Bombeck committed 2026-11-11 19:42:17 +01:00
1 parent 2e73b0ffec
commit 7f54a820b0
2 files changed
+41 -4

No files matched your search

@@ -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();
});
});
+17 -3
View File
@@ -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<unknown> {
return Promise.all([
qc.invalidateQueries({ queryKey: queryKeys.measurementReminders() }),
invalidateKeys(qc, [
queryKeys.measurementReminders(),
queryKeys.dailyDigest(),
queryKeys.dashboardSnapshot(),
queryKeys.dayRoot(),
]),
refetchInactiveDailyReads(qc),
]);
}