From 1bf760121df26ce22a5a67d3245fef68470ad263 Mon Sep 17 00:00:00 2001 From: Jack Kingsman Date: Tue, 10 Mar 2026 15:40:26 -0700 Subject: [PATCH] Preserve repeater values when browsing away --- .../repeater/repeaterPaneShared.tsx | 36 ++++- frontend/src/hooks/useRepeaterDashboard.ts | 124 ++++++++++++++++-- frontend/src/test/repeaterDashboard.test.tsx | 14 ++ .../src/test/useRepeaterDashboard.test.ts | 41 +++++- frontend/src/types.ts | 1 + 5 files changed, 204 insertions(+), 12 deletions(-) diff --git a/frontend/src/components/repeater/repeaterPaneShared.tsx b/frontend/src/components/repeater/repeaterPaneShared.tsx index 3fad438..4dc182d 100644 --- a/frontend/src/components/repeater/repeaterPaneShared.tsx +++ b/frontend/src/components/repeater/repeaterPaneShared.tsx @@ -80,6 +80,28 @@ export function formatAdvertInterval(val: string | null): string { return `${trimmed}h`; } +function formatFetchedRelative(fetchedAt: number): string { + const elapsedSeconds = Math.max(0, Math.floor((Date.now() - fetchedAt) / 1000)); + + if (elapsedSeconds < 60) return 'Just now'; + + const elapsedMinutes = Math.floor(elapsedSeconds / 60); + if (elapsedMinutes < 60) { + return `${elapsedMinutes} minute${elapsedMinutes === 1 ? '' : 's'} ago`; + } + + const elapsedHours = Math.floor(elapsedMinutes / 60); + return `${elapsedHours} hour${elapsedHours === 1 ? '' : 's'} ago`; +} + +function formatFetchedTime(fetchedAt: number): string { + return new Date(fetchedAt).toLocaleTimeString([], { + hour: 'numeric', + minute: '2-digit', + second: '2-digit', + }); +} + // --- Generic Pane Wrapper --- export function RepeaterPane({ @@ -99,10 +121,22 @@ export function RepeaterPane({ className?: string; contentClassName?: string; }) { + const fetchedAt = state.fetched_at ?? null; + return (
-

{title}

+
+

{title}

+ {fetchedAt && ( +

+ Fetched {formatFetchedTime(fetchedAt)} ({formatFetchedRelative(fetchedAt)}) +

+ )} +
{onRefresh && (