Preserve unchanged habit renders and memoize calendar geometry

This commit is contained in:
syntaxbullet
2026-09-08 16:17:19 +02:00
parent b4619dec33
commit 9a0fc108a9
6 changed files with 52 additions and 22 deletions

View File

@@ -2,7 +2,7 @@ import { CalendarCacheProvider } from "../components/CalendarCache";
import { Archive, Plus, Settings2 } from "lucide-react";
import { ArchivedHabits } from "../components/ArchivedHabits";
import { AccountSettings } from "../components/AccountSettings";
import { useCallback, useEffect, useRef, useState } from "react";
import { memo, useCallback, useEffect, useRef, useState } from "react";
import { useAuth } from "../components/AuthProvider";
import { DiscordSignInButton } from "../components/DiscordSignInButton";
import { PageLayout } from "../components/design-system/PageLayout";
@@ -17,7 +17,7 @@ import { Card, CardGrid } from "../components/design-system/Card";
import { HabitChart } from "../components/design-system/HabitChart";
import { HABIT_COLORS } from "../components/design-system/calendar-model";
import { HabitForm } from "../components/HabitForm";
import { habitRequest, scheduleLabel, type TodayHabit, type TodayResponse } from "../lib/dashboard";
import { habitRequest, reconcileToday, scheduleLabel, type TodayHabit, type TodayResponse } from "../lib/dashboard";
import { HabitHistory } from "../components/HabitHistory";
import { WelcomePanel } from "../components/design-system/WelcomePanel";
import type { PublicUser } from "../shared/user";
@@ -203,7 +203,7 @@ function Dashboard({ user, onExpired }: { user: PublicUser; onExpired: () => voi
habitRequest<TodayResponse>("/today", { signal: controller.signal })
.then((data) => {
if (!controller.signal.aborted) {
setToday(data);
setToday(current => reconcileToday(current, data));
setNeedsRefresh(false);
setRevision((value) => value + 1);
}
@@ -301,7 +301,7 @@ function Dashboard({ user, onExpired }: { user: PublicUser; onExpired: () => voi
try {
const refreshed = await habitRequest<TodayResponse>("/today");
if (mounted.current) {
setToday(refreshed);
setToday(current => reconcileToday(current, refreshed));
// Keep a habit reachable when an edit makes it unscheduled (including an empty routine).
if (refreshed.habits.some(item => item.habitId === habit.habitId && !item.due)) setHabitView("all");
setNeedsRefresh(false);
@@ -336,6 +336,13 @@ function Dashboard({ user, onExpired }: { user: PublicUser; onExpired: () => voi
}
}
const actions = useRef({ update, manage });
actions.current = { update, manage };
const updateHabit = useCallback((...args: Parameters<typeof update>) => actions.current.update(...args), []);
const manageHabit = useCallback((...args: Parameters<typeof manage>) => actions.current.manage(...args), []);
const refreshHistory = useCallback(() => setAttempt(value => value + 1), []);
const expire = useCallback(() => expired.current(), []);
return (
<>
<WelcomePanel
@@ -458,10 +465,10 @@ function Dashboard({ user, onExpired }: { user: PublicUser; onExpired: () => voi
date={today.date}
revision={habit.calendarRevision ?? habitRevisions[habit.habitId] ?? 0}
disabled={busy || pendingHabits.has(habit.habitId) || loading || needsRefresh}
onHistorySaved={() => setAttempt(value => value + 1)}
onUpdate={update}
onManage={manage}
onExpired={() => expired.current()}
onHistorySaved={refreshHistory}
onUpdate={updateHabit}
onManage={manageHabit}
onExpired={expire}
/>
))}
</div>
@@ -473,7 +480,7 @@ function Dashboard({ user, onExpired }: { user: PublicUser; onExpired: () => voi
);
}
function SavedHabit({
const SavedHabit = memo(function SavedHabit({
habit,
date,
revision,
@@ -603,7 +610,7 @@ function SavedHabit({
) : undefined}
</HabitHistory>
);
}
});
function SavedTask({
task,