fix(forms): close episode, mood and dose dialogs before the refetch

The illness episode hooks returned the refresh of the illness reads and
the dashboard from onSuccess, so every episode sheet kept spinning until
those had refetched. The mood edit dialog, the mood and measurement bulk
deletes and their Undo toasts did the same with an await, and so did the
dose edit dialog, the log-intake dialog and the ledger's add dialog.
They now answer and close as soon as the write lands and start the
refresh without waiting for it. A scheduled dose lands on its slot, so a
card that is still stale for a moment only repeats it.

The take and skip buttons on a medication card keep waiting: nothing
marks the dose in the cache meanwhile, so releasing them early would
offer a Take for a dose already recorded.
This commit is contained in:
Marc-André Bombeck committed 2026-11-11 21:36:37 +01:00
1 parent 85240ab858
commit 4485dfb3a9
9 files changed
+226 -30

No files matched your search

@@ -0,0 +1,56 @@
/**
* An episode save settles once the server stored it.
*
* Every episode hook's `onSuccess` used to return the refresh of the illness
* reads and the dashboard. A mutation waits for what `onSuccess` returns, so
* the episode sheet kept its spinner until each of those reads had refetched.
*/
import { describe, expect, it, vi } from "vitest";
const client = vi.hoisted(() => ({
// A refetch that never answers, as on a slow connection.
invalidateQueries: vi.fn(() => new Promise(() => {})),
refetchQueries: vi.fn(() => new Promise(() => {})),
getQueryCache: () => ({ findAll: () => [] }),
}));
vi.mock("@tanstack/react-query", () => ({
// The hook's options are what is under test; return them as they are.
useQuery: (options: unknown) => options,
useMutation: (options: unknown) => options,
useQueryClient: () => client,
}));
vi.mock("@/lib/api/api-fetch", () => ({
apiGet: vi.fn(),
apiGetList: vi.fn(),
apiPost: vi.fn(),
apiPatch: vi.fn(),
apiDelete: vi.fn(),
}));
import {
useCreateEpisode,
useDeleteEpisode,
useResolveEpisode,
useRestoreEpisode,
useUpdateEpisode,
useUpsertDayLog,
} from "../use-illness";
describe("episode saves", () => {
it.each([
["create", () => useCreateEpisode()],
["update", () => useUpdateEpisode()],
["resolve", () => useResolveEpisode()],
["delete", () => useDeleteEpisode()],
["restore", () => useRestoreEpisode()],
["day log", () => useUpsertDayLog("e1")],
])("%s starts the refresh and does not wait for it", (_, hook) => {
client.invalidateQueries.mockClear();
const options = hook() as unknown as { onSuccess: () => unknown };
expect(options.onSuccess()).toBeUndefined();
expect(client.invalidateQueries).toHaveBeenCalled();
});
});
+6 -3
View File
@@ -152,9 +152,12 @@ function useInvalidateIllness() {
// the Today digest — both unmounted from here, so the bundle invalidation
// is paired with the forced inactive refetch. The write routes hard-evict
// the server analytics bucket, so the refetch returns post-write context.
return async () => {
await invalidateKeys(qc, illnessDependentKeys);
await refetchInactiveDailyReads(qc);
// Not returned: a mutation waits for what `onSuccess` returns, and the
// sheet would stay open until every read had refetched.
return () => {
void invalidateKeys(qc, illnessDependentKeys).then(() =>
refetchInactiveDailyReads(qc),
);
};
}