mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 22:26:13 +00:00
fix(glucose): show targets in the new unit right after a unit change
After switching between mg/dL and mmol/L, the target panels on the glucose page kept the old unit for up to ten minutes. Their unit comes from the cached targets payload, and the save only refreshed the account payload. The select now invalidates the targets query, and the route evicts the profile-derived server caches when the unit changes, the same way the thresholds and source-priority writes do.
This commit is contained in:
1 parent
0c07764097
commit
08e6229142
4 files changed
+26
-7
No files matched your search
@@ -34,6 +34,7 @@ import { checkRateLimit, rateLimitHeaders } from "@/lib/rate-limit";
|
||||
import { resolveGlucoseUnit, type GlucoseUnit } from "@/lib/glucose";
|
||||
import { glucoseUnitPatchSchema } from "@/lib/validations/user-prefs";
|
||||
import { refreshTextsAfterUnitChange } from "@/lib/insights/unit-change-refresh";
|
||||
import { invalidateUserProfile } from "@/lib/cache/invalidate";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
@@ -103,9 +104,11 @@ export const PATCH = apiHandler(async (req: Request) => {
|
||||
},
|
||||
});
|
||||
|
||||
// The status notes and narratives quote glucose in this unit; re-warm
|
||||
// them so today's stored text does not keep the old one.
|
||||
// The targets payload and the status notes and narratives carry glucose in
|
||||
// this unit: evict the cached payloads and re-warm today's stored text so
|
||||
// neither keeps the old one.
|
||||
if (resolveGlucoseUnit(previous?.glucoseUnit) !== next) {
|
||||
invalidateUserProfile(user.id);
|
||||
void refreshTextsAfterUnitChange(user.id);
|
||||
}
|
||||
|
||||
|
||||
@@ -134,6 +134,19 @@ describe("Settings — GlucoseUnitSelect", () => {
|
||||
expect(componentSource).toMatch(/queryKey: queryKeys\.authMe\(\)/);
|
||||
});
|
||||
|
||||
// The targets panel takes its unit from the targets payload, not from the
|
||||
// account, so a save that only refreshed `/me` left it on the old unit
|
||||
// until the server cache turned over (up to ten minutes). Both ends have
|
||||
// to let go of the old payload.
|
||||
it("refreshes the targets payload on both ends", () => {
|
||||
expect(componentSource).toMatch(/queryKey: queryKeys\.insightsTargets\(\)/);
|
||||
const route = readFileSync(
|
||||
join(process.cwd(), "src/app/api/auth/me/glucose-unit/route.ts"),
|
||||
"utf8",
|
||||
);
|
||||
expect(route).toMatch(/!== next\) \{\s*invalidateUserProfile\(user\.id\);/);
|
||||
});
|
||||
|
||||
it("is actually mounted on the settings page", () => {
|
||||
// A control nobody can reach is the same defect as no control.
|
||||
const accountSection = readFileSync(
|
||||
|
||||
@@ -31,7 +31,8 @@ import { resolveGlucoseUnit, type GlucoseUnit } from "@/lib/glucose";
|
||||
* Persistence mirrors the unit-system select: a change PATCHes
|
||||
* `/api/auth/me/glucose-unit` immediately and invalidates `queryKeys.authMe()`,
|
||||
* so the dashboard tiles and the glucose insight page re-render on the next
|
||||
* /me refetch.
|
||||
* /me refetch, and `queryKeys.insightsTargets()`, whose payload states the
|
||||
* target ranges in the unit it was built with.
|
||||
*/
|
||||
export function GlucoseUnitSelect({
|
||||
isAuthenticated,
|
||||
@@ -66,6 +67,7 @@ export function GlucoseUnitSelect({
|
||||
onSuccess: () => {
|
||||
toastWrittenOutcome("success", t("settings.dashboard.glucoseUnit.saved"));
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.authMe() });
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.insightsTargets() });
|
||||
setOptimistic(null);
|
||||
},
|
||||
onError: (err) => {
|
||||
|
||||
Vendored
+5
-4
@@ -242,10 +242,11 @@ export function invalidateUserHealthScore(userId: string): void {
|
||||
* on the very next read.
|
||||
*
|
||||
* Callers: `/api/user/thresholds` PUT + DELETE, `applyProfileUpdate`
|
||||
* (only when heightCm / dateOfBirth / gender is in the update set), and
|
||||
* `/api/auth/me/source-priority` PUT. The unit-preference toggle is NOT
|
||||
* wired in: it only drives the client-side display transform, no
|
||||
* server-cached payload reads it.
|
||||
* (only when heightCm / dateOfBirth / gender is in the update set),
|
||||
* `/api/auth/me/source-priority` PUT, and `/api/auth/me/glucose-unit` PATCH
|
||||
* (the targets payload states glucose in the account's unit). The
|
||||
* unit-preference toggle is NOT wired in: it only drives the client-side
|
||||
* display transform, no server-cached payload reads it.
|
||||
*/
|
||||
export function invalidateUserProfile(userId: string): void {
|
||||
caches.insightsTargets.deleteByPrefix(userId);
|
||||
|
||||
Reference in new issue
Block a user