From 7bf6db7817e30ca26e6e5307295656e0a98ef047 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 24 Mar 2026 11:33:15 +0200 Subject: [PATCH] feat(standalone/setup): add a progress bar for sync status --- apps/client/src/setup.tsx | 20 +++++++++++++------- packages/trilium-core/src/routes/api/sync.ts | 3 ++- packages/trilium-core/src/services/sync.ts | 12 ++++++++++++ 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/apps/client/src/setup.tsx b/apps/client/src/setup.tsx index 6bb94a349b..847b489102 100644 --- a/apps/client/src/setup.tsx +++ b/apps/client/src/setup.tsx @@ -70,26 +70,32 @@ function SetupOptions({ setState }: { setState: (state: State) => void }) { } function SyncInProgress() { - const { outstandingPullCount, initialized } = useOutstandingSyncInfo(); + const { outstandingPullCount, totalPullCount, initialized } = useOutstandingSyncInfo(); + + const progress = totalPullCount + ? Math.round(((totalPullCount - outstandingPullCount) / totalPullCount) * 100) + : 0; return (

{t("setup.sync-in-progress-title")}

{t("setup.sync-in-progress-description")}

- -

Outstanding sync objects: {outstandingPullCount}

+ +

{progress}% ({totalPullCount ? totalPullCount - outstandingPullCount : 0} / {totalPullCount ?? "?"})

); } function useOutstandingSyncInfo() { const [ outstandingPullCount, setOutstandingPullCount ] = useState(0); + const [ totalPullCount, setTotalPullCount ] = useState(null); const [ initialized, setInitialized ] = useState(false); async function refresh() { - const { outstandingPullCount, initialized } = await server.get("sync/stats"); - setOutstandingPullCount(outstandingPullCount); - setInitialized(initialized); + const resp = await server.get<{ outstandingPullCount: number; totalPullCount: number | null; initialized: boolean }>("sync/stats"); + setOutstandingPullCount(resp.outstandingPullCount); + setTotalPullCount(resp.totalPullCount); + setInitialized(resp.initialized); } useEffect(() => { @@ -98,7 +104,7 @@ function useOutstandingSyncInfo() { return () => clearInterval(interval); }, []); - return { outstandingPullCount, initialized }; + return { outstandingPullCount, totalPullCount, initialized }; } function Spinner() { diff --git a/packages/trilium-core/src/routes/api/sync.ts b/packages/trilium-core/src/routes/api/sync.ts index ed0c5f85b3..d32ed3fd37 100644 --- a/packages/trilium-core/src/routes/api/sync.ts +++ b/packages/trilium-core/src/routes/api/sync.ts @@ -46,7 +46,8 @@ function getStats() { const stats = { initialized: getSql().getValue("SELECT value FROM options WHERE name = 'initialized'") === "true", - outstandingPullCount: syncService.getOutstandingPullCount() + outstandingPullCount: syncService.getOutstandingPullCount(), + totalPullCount: syncService.getTotalPullCount() }; getLog().info(`Returning sync stats: ${JSON.stringify(stats)}`); diff --git a/packages/trilium-core/src/services/sync.ts b/packages/trilium-core/src/services/sync.ts index 12c76e9cf7..69ecb89a77 100644 --- a/packages/trilium-core/src/services/sync.ts +++ b/packages/trilium-core/src/services/sync.ts @@ -26,6 +26,7 @@ import { getCrypto } from "./encryption/crypto.js"; let proxyToggle = true; let outstandingPullCount = 0; +let totalPullCount: number | null = null; interface CheckResponse { maxEntityChangeId: number; @@ -172,6 +173,10 @@ async function pullChanges(syncContext: SyncContext) { outstandingPullCount = resp.outstandingPullCount; + if (totalPullCount === null) { + totalPullCount = entityChanges.length + outstandingPullCount; + } + const pulledDate = Date.now(); getSql().transactional(() => { @@ -201,6 +206,8 @@ async function pullChanges(syncContext: SyncContext) { } log.info("Finished pull"); + + totalPullCount = null; } async function pushChanges(syncContext: SyncContext) { @@ -450,6 +457,10 @@ function getOutstandingPullCount() { return outstandingPullCount; } +function getTotalPullCount() { + return totalPullCount; +} + function startSyncTimer() { becca_loader.beccaLoaded.then(() => { setInterval(cls.wrap(sync), 60000); @@ -467,6 +478,7 @@ export default { login, getEntityChangeRecords, getOutstandingPullCount, + getTotalPullCount, getMaxEntityChangeId, startSyncTimer };