diff --git a/apps/yaak-client/init/sync.ts b/apps/yaak-client/init/sync.ts index 92685084..61412711 100644 --- a/apps/yaak-client/init/sync.ts +++ b/apps/yaak-client/init/sync.ts @@ -1,4 +1,4 @@ -import { debounce } from "@yaakapp-internal/lib"; +import { debounce, eagerDebounceAsync } from "@yaakapp-internal/lib"; import type { AnyModel, ModelPayload } from "@yaakapp-internal/models"; import { watchWorkspaceFiles } from "@yaakapp-internal/sync"; import { syncWorkspace } from "../commands/commands"; @@ -25,41 +25,8 @@ export async function sync({ force }: { force?: boolean } = {}) { }); } -const debouncedSync = debounce(async () => { - await sync(); -}, 1000); - -let modelSyncTimer: ReturnType | null = null; -let modelSyncInFlight = false; - -function scheduleModelSync() { - if (modelSyncTimer == null) { - // No timer means this is the first model change in a burst, so sync immediately. - void syncModelChanges(); - } else { - // Keep pushing the trailing sync out until model writes have been quiet for a bit. - clearTimeout(modelSyncTimer); - } - - modelSyncTimer = setTimeout(async () => { - modelSyncTimer = null; - // Catch any final state that was written while the immediate sync was running. - await syncModelChanges(); - }, 1000); -} - -async function syncModelChanges() { - if (modelSyncInFlight) return; - - modelSyncInFlight = true; - try { - await sync(); - } catch (e) { - console.error(e); - } finally { - modelSyncInFlight = false; - } -} +const syncAfterFileChange = debounce(sync, 1000); +const syncAfterModelWrite = eagerDebounceAsync(sync, 1000); /** * Subscribe to model change events. Since we check the workspace ID on sync, we can @@ -67,7 +34,7 @@ async function syncModelChanges() { */ function initModelListeners() { listenToTauriEvent("model_write", (p) => { - if (isModelRelevant(p.payload.model)) scheduleModelSync(); + if (isModelRelevant(p.payload.model)) syncAfterModelWrite(); }); } @@ -82,11 +49,11 @@ function initFileChangeListeners() { await unsub?.(); // Unsub to previous const workspaceMeta = jotaiStore.get(activeWorkspaceMetaAtom); if (workspaceMeta == null || workspaceMeta.settingSyncDir == null) return; - debouncedSync(); // Perform an initial sync when switching workspace + syncAfterFileChange(); // Perform an initial sync when switching workspace unsub = watchWorkspaceFiles( workspaceMeta.workspaceId, workspaceMeta.settingSyncDir, - debouncedSync, + syncAfterFileChange, ); }); } diff --git a/packages/common-lib/eagerDebounceAsync.ts b/packages/common-lib/eagerDebounceAsync.ts new file mode 100644 index 00000000..09e2c2b1 --- /dev/null +++ b/packages/common-lib/eagerDebounceAsync.ts @@ -0,0 +1,36 @@ +export function eagerDebounceAsync(fn: () => Promise, delay: number) { + let timer: ReturnType | null = null; + let inFlight: Promise | null = null; + let runAfterInFlight = false; + + const run = async () => { + if (inFlight != null) { + runAfterInFlight = true; + return; + } + + runAfterInFlight = false; + inFlight = fn() + .catch(console.error) + .finally(() => { + inFlight = null; + if (runAfterInFlight && timer == null) { + void run(); + } + }); + await inFlight; + }; + + return () => { + if (timer == null) { + void run(); + } else { + clearTimeout(timer); + } + + timer = setTimeout(() => { + timer = null; + void run(); + }, delay); + }; +} diff --git a/packages/common-lib/index.ts b/packages/common-lib/index.ts index 01983b42..245e43a4 100644 --- a/packages/common-lib/index.ts +++ b/packages/common-lib/index.ts @@ -1,3 +1,4 @@ export * from "./debounce"; +export * from "./eagerDebounceAsync"; export * from "./formatSize"; export * from "./templateFunction";