fix double sync race: remove duplicate syncNow calls, add sync guard

- 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) <noreply@anthropic.com>
This commit is contained in:
2026-03-15 12:22:39 +01:00
parent df78397870
commit 0433ed1f7e
2 changed files with 11 additions and 5 deletions

View File

@@ -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

View File

@@ -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)")