From bf9eec6a9b74025472790919fd42d09183663199 Mon Sep 17 00:00:00 2001 From: psavarmattas Date: Sat, 15 Aug 2026 15:43:00 +0100 Subject: [PATCH] fix(offline): retry pending sync with a cooloff instead of one shot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The auto-flush that runs when reconnecting (or turning Offline Mode back off) only ever tried once — any operation that still failed on that attempt sat stuck until the user manually hit Retry or connectivity changed again. Now retries every 5 minutes for as long as anything's still pending and the signal stays on, stopping on its own once the queue is empty. Full Local Sync's existing 20-minute loop is unaffected/separate. --- Outpost/Root/RootView.swift | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Outpost/Root/RootView.swift b/Outpost/Root/RootView.swift index 598475c..5fd90d4 100644 --- a/Outpost/Root/RootView.swift +++ b/Outpost/Root/RootView.swift @@ -50,12 +50,26 @@ struct RootView: View { // off — no need to wait for the user to open Settings and hit Retry. // Also catches Full Local Sync back up immediately, rather than // leaving it to wait out the rest of the periodic loop below. + // + // The flush itself gets a retry loop with a cooloff, not just one + // attempt: a single transient failure (one bad operation, a blip + // mid-flush) used to leave everything else stuck until the user + // manually hit Retry or connectivity changed again. Keeps retrying + // every 5 minutes for as long as *anything* is still pending and + // this signal stays on — stops on its own once the queue is empty, + // and `.task(id:)` cancels/restarts it automatically if + // isEffectivelyOnline flips again in the meantime. .task(id: isEffectivelyOnline) { guard isEffectivelyOnline, let cachingClient = session.cachingClient else { return } - _ = await cachingClient.flushPendingOperations() if isFullLocalSyncEnabled { _ = await cachingClient.performFullSync() } + while !Task.isCancelled { + _ = await cachingClient.flushPendingOperations() + let remaining = await cachingClient.pendingOperations() + guard !remaining.isEmpty else { break } + try? await Task.sleep(for: .seconds(300)) + } } // Fully automatic — this is the only place Full Local Sync actually // runs from (Settings' "Sync Now" is just an on-demand nudge at the