feat(tracker): prefetch adjacent month on nav hover for instant switching (P2)
usePrefetchTracker() warms the ['tracker', y, m] cache when the user hovers/ focuses the prev/next month buttons, so clicking is instant (no round-trip). No-op if already cached and fresh. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
2793927a5c
commit
e941f05cd6
|
|
@ -68,6 +68,19 @@ export function useInvalidateTrackerData() {
|
|||
}, [queryClient]);
|
||||
}
|
||||
|
||||
// Prefetch a tracker month into the cache (e.g. on month-nav hover) so switching
|
||||
// to it is instant. No-op if it's already cached and fresh.
|
||||
export function usePrefetchTracker() {
|
||||
const queryClient = useQueryClient();
|
||||
return useCallback((year, month) => {
|
||||
queryClient.prefetchQuery({
|
||||
queryKey: ['tracker', year, month],
|
||||
queryFn: () => api.tracker(year, month),
|
||||
staleTime: 1000 * 60 * 5,
|
||||
});
|
||||
}, [queryClient]);
|
||||
}
|
||||
|
||||
// ── Page data (migrated off manual useEffect + load()) ───────────────────────
|
||||
// The queryKey encodes the params, so React Query handles caching, request
|
||||
// dedup, cancellation, and out-of-order responses — no manual sequence guards.
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { useSearchParams } from 'react-router-dom';
|
|||
import { ChevronLeft, ChevronRight, AlertCircle, CheckCircle2, Plus, Search, RefreshCw, Landmark, ArrowUpToLine, ArrowUp, ArrowDown, BellOff, EyeOff, Settings2 } from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
import { api } from '@/api.js';
|
||||
import { useTracker, useDriftReport, useInvalidateTrackerData } from '@/hooks/useQueries';
|
||||
import { useTracker, useDriftReport, useInvalidateTrackerData, usePrefetchTracker } from '@/hooks/useQueries';
|
||||
import { useSearchPanelPreference } from '@/hooks/useSearchPanelPreference';
|
||||
import BillModal from '@/components/BillModal';
|
||||
import { makeBillDraft } from '@/lib/billDrafts';
|
||||
|
|
@ -301,12 +301,23 @@ export default function TrackerPage() {
|
|||
return () => window.removeEventListener('tracker:late-attributions', handler);
|
||||
}, []);
|
||||
|
||||
function navigate(delta) {
|
||||
function adjacentMonth(delta) {
|
||||
let nm = month + delta;
|
||||
let ny = year;
|
||||
if (nm > 12) { ny += 1; nm = 1; }
|
||||
if (nm < 1) { ny -= 1; nm = 12; }
|
||||
updateParams({ year: ny, month: nm });
|
||||
return { year: ny, month: nm };
|
||||
}
|
||||
|
||||
function navigate(delta) {
|
||||
updateParams(adjacentMonth(delta));
|
||||
}
|
||||
|
||||
// Prefetch the adjacent month on hover/focus so clicking prev/next is instant.
|
||||
const prefetchTracker = usePrefetchTracker();
|
||||
function prefetchAdjacent(delta) {
|
||||
const { year: ny, month: nm } = adjacentMonth(delta);
|
||||
prefetchTracker(ny, nm);
|
||||
}
|
||||
|
||||
function bankSyncMessage(result) {
|
||||
|
|
@ -651,6 +662,8 @@ export default function TrackerPage() {
|
|||
<Button
|
||||
size="icon" variant="ghost"
|
||||
onClick={() => navigate(-1)}
|
||||
onMouseEnter={() => prefetchAdjacent(-1)}
|
||||
onFocus={() => prefetchAdjacent(-1)}
|
||||
className="h-7 w-7 hover:bg-white/5"
|
||||
aria-label="Previous month"
|
||||
>
|
||||
|
|
@ -662,6 +675,8 @@ export default function TrackerPage() {
|
|||
<Button
|
||||
size="icon" variant="ghost"
|
||||
onClick={() => navigate(1)}
|
||||
onMouseEnter={() => prefetchAdjacent(1)}
|
||||
onFocus={() => prefetchAdjacent(1)}
|
||||
className="h-7 w-7 hover:bg-white/5"
|
||||
aria-label="Next month"
|
||||
>
|
||||
|
|
|
|||
Loading…
Reference in New Issue