From 0433ed1f7ef05206c446d33af2955f3c10f6048b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20F=C3=B6rtsch?= Date: Sun, 15 Mar 2026 12:22:39 +0100 Subject: [PATCH] fix double sync race: remove duplicate syncNow calls, add sync guard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - connectAccount() and loadExistingAccount() no longer call syncNow() — the .task modifier on mailView handles initial sync + periodic sync - add isSyncing guard to prevent concurrent syncNow() calls that race on the IMAP connection and cause "Previous command interrupted" - reload mailboxes after each sync (needed on first sync when DB is empty) Co-Authored-By: Claude Opus 4.6 (1M context) --- Apps/MagnumOpus/ContentView.swift | 6 ++---- Apps/MagnumOpus/ViewModels/MailViewModel.swift | 10 +++++++++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Apps/MagnumOpus/ContentView.swift b/Apps/MagnumOpus/ContentView.swift index c2ef413..a18136c 100644 --- a/Apps/MagnumOpus/ContentView.swift +++ b/Apps/MagnumOpus/ContentView.swift @@ -165,11 +165,10 @@ struct ContentView: View { try viewModel.setup(config: config, credentials: credentials) try KeychainService.saveCredentials(credentials, for: config.id) Self.saveAccountConfig(config) + // syncNow + startPeriodicSync happen via .task on mailView — don't duplicate Task { - await viewModel.syncNow() await viewModel.loadMailboxes(accountId: config.id) viewModel.startObservingThreads(accountId: config.id) - viewModel.startPeriodicSync() } } catch { accountSetup.errorMessage = error.localizedDescription @@ -182,11 +181,10 @@ struct ContentView: View { else { return } do { try viewModel.setup(config: config, credentials: credentials) + // syncNow + startPeriodicSync happen via .task on mailView — don't duplicate Task { await viewModel.loadMailboxes(accountId: config.id) viewModel.startObservingThreads(accountId: config.id) - await viewModel.syncNow() - viewModel.startPeriodicSync() } } catch { // Account config exists but setup failed — show account setup diff --git a/Apps/MagnumOpus/ViewModels/MailViewModel.swift b/Apps/MagnumOpus/ViewModels/MailViewModel.swift index a00f9ee..45f00cc 100644 --- a/Apps/MagnumOpus/ViewModels/MailViewModel.swift +++ b/Apps/MagnumOpus/ViewModels/MailViewModel.swift @@ -201,11 +201,19 @@ final class MailViewModel { } } + private var isSyncing = false + func syncNow() async { - guard let coordinator else { return } + guard let coordinator, !isSyncing else { return } + isSyncing = true + defer { isSyncing = false } do { try await coordinator.syncNow() syncState = coordinator.syncState + // Reload mailboxes after sync (they may have been created on first sync) + if let accountConfig { + await loadMailboxes(accountId: accountConfig.id) + } } catch { let desc = "\(error)" print("[MailViewModel] syncNow failed: \(desc)")