From 101ef639ab09308b0f7660e169490049128f1322 Mon Sep 17 00:00:00 2001 From: Christophe Vila Date: Mon, 27 Jul 2026 08:36:31 +0200 Subject: [PATCH] feat(frontend): replace inline sync progress with SyncModal GarminConnection.tsx no longer polls sync status itself or renders any inline "syncing.../last sync..." text -- SyncModal (previous commit) is now the only place sync progress and results are shown. Auth-status polling is simplified to a fixed interval now that it no longer needs to speed up while a sync is running. --- frontend/src/components/GarminConnection.tsx | 62 +++++--------------- 1 file changed, 15 insertions(+), 47 deletions(-) diff --git a/frontend/src/components/GarminConnection.tsx b/frontend/src/components/GarminConnection.tsx index 81ec159..4918965 100644 --- a/frontend/src/components/GarminConnection.tsx +++ b/frontend/src/components/GarminConnection.tsx @@ -1,49 +1,28 @@ -import { useEffect, useRef, useState } from "react"; +import { useEffect, useState } from "react"; import { api } from "../api/client"; -import type { AuthResponse, SyncStatus } from "../types/api"; - -function syncProgressLabel(status: SyncStatus): string { - const { detail_fill_progress: fill, activities_pending_details: pending } = status; - if (fill.Total > 0) { - // pending includes the current batch, so subtract what's already - // counted in this batch's Total to avoid double-counting the "beyond - // this batch" remainder. - const beyondBatch = Math.max(0, pending - (fill.Total - fill.Done)); - return `syncing: ${fill.Done}/${fill.Total} activities${beyondBatch > 0 ? ` (+${beyondBatch} more queued)` : ""}`; - } - return "syncing..."; -} +import type { AuthResponse } from "../types/api"; +import { SyncModal } from "./SyncModal"; export function GarminConnection({ onBeforeConnect }: { onBeforeConnect?: () => Promise }) { const [auth, setAuth] = useState(null); const [code, setCode] = useState(""); const [busy, setBusy] = useState(false); const [error, setError] = useState(null); - const [syncStatus, setSyncStatus] = useState(null); // There's no real Garmin logout -- the backend session just sits idle. // "Disconnect" only hides that and shows Connect again locally; a real // login (connect()) clears it. const [disconnected, setDisconnected] = useState(false); + const [showSyncModal, setShowSyncModal] = useState(false); - function refreshStatus() { - api.authStatus().then(setAuth).catch((e) => setError(String(e))); - api.syncStatus().then(setSyncStatus).catch(() => {}); - } - - // Poll faster while a sync is actually running, so progress feels live; - // back off to a relaxed interval the rest of the time. - const syncStatusRef = useRef(syncStatus); - syncStatusRef.current = syncStatus; - + // Auth status alone -- sync status is polled by SyncModal itself while + // it's open, so this no longer needs the "poll faster while syncing" + // dynamic interval it used to have. useEffect(() => { - refreshStatus(); - let timeout: ReturnType; - const tick = () => { - refreshStatus(); - timeout = setTimeout(tick, syncStatusRef.current?.in_progress ? 1500 : 6000); - }; - timeout = setTimeout(tick, 1500); - return () => clearTimeout(timeout); + api.authStatus().then(setAuth).catch((e) => setError(String(e))); + const interval = setInterval(() => { + api.authStatus().then(setAuth).catch(() => {}); + }, 6000); + return () => clearInterval(interval); }, []); async function connect() { @@ -83,7 +62,7 @@ export function GarminConnection({ onBeforeConnect }: { onBeforeConnect?: () => setError(null); try { await api.syncRun(); - refreshStatus(); + setShowSyncModal(true); } catch (e) { setError(String(e)); } finally { @@ -174,20 +153,9 @@ export function GarminConnection({ onBeforeConnect }: { onBeforeConnect?: () => {!disconnected && auth?.message &&

{auth.message}

} - {/* Full-width so a long "last sync"/pending-details message has room - to breathe, instead of being squeezed next to the status dot. */} - {status === "authenticated" && syncStatus?.in_progress && ( -

{syncProgressLabel(syncStatus)}

- )} - {status === "authenticated" && syncStatus?.last_run && !syncStatus.in_progress && ( -

- last sync: {syncStatus.last_run.Status} ({syncStatus.last_run.ActivitiesFetched} activities) - {syncStatus.activities_pending_details > 0 && - ` — ${syncStatus.activities_pending_details} still need details, click Sync now again`} -

- )} - {error &&

{error}

} + + {showSyncModal && setShowSyncModal(false)} />} ); }