From ac824cd72e0ba71e6b01a58f0447cf545dac0952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20F=C3=B6rtsch?= Date: Sat, 14 Mar 2026 09:00:14 +0100 Subject: [PATCH] add deferral resurfacing to SyncCoordinator (task 7) Import TaskStore, add optional taskStore property, add resurfaceDeferrals() that clears expired email deferrals and deferred task deferUntil on each sync. Co-Authored-By: Claude Sonnet 4.6 --- .../Sources/SyncEngine/SyncCoordinator.swift | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/Packages/MagnumOpusCore/Sources/SyncEngine/SyncCoordinator.swift b/Packages/MagnumOpusCore/Sources/SyncEngine/SyncCoordinator.swift index 2b25dad..bac9286 100644 --- a/Packages/MagnumOpusCore/Sources/SyncEngine/SyncCoordinator.swift +++ b/Packages/MagnumOpusCore/Sources/SyncEngine/SyncCoordinator.swift @@ -2,6 +2,7 @@ import Foundation import Models import IMAPClient import MailStore +import TaskStore @Observable @MainActor @@ -10,6 +11,7 @@ public final class SyncCoordinator { private let imapClient: any IMAPClientProtocol private let store: MailStore private let actionQueue: ActionQueue? + private let taskStore: TaskStore? private var syncTask: Task? public private(set) var syncState: SyncState = .idle @@ -19,12 +21,14 @@ public final class SyncCoordinator { accountConfig: AccountConfig, imapClient: any IMAPClientProtocol, store: MailStore, - actionQueue: ActionQueue? = nil + actionQueue: ActionQueue? = nil, + taskStore: TaskStore? = nil ) { self.accountConfig = accountConfig self.imapClient = imapClient self.store = store self.actionQueue = actionQueue + self.taskStore = taskStore } public func onEvent(_ handler: @escaping (SyncEvent) -> Void) { @@ -54,7 +58,31 @@ public final class SyncCoordinator { } } + private func resurfaceDeferrals() { + let now = ISO8601DateFormatter().string(from: Date()) + do { + // Resurface deferred emails: delete deferral records so messages reappear in Inbox + let expiredDeferrals = try store.expiredDeferrals(beforeDate: now) + for deferral in expiredDeferrals { + try store.deleteDeferral(id: deferral.id) + } + + // Resurface deferred tasks: clear deferUntil in cache and rewrite .ics files + let expiredTasks = try store.expiredDeferredTasks(beforeDate: now) + for task in expiredTasks { + try store.clearTaskDeferral(id: task.id) + try taskStore?.updateDeferral(id: task.id, deferUntil: nil, isSomeday: false) + } + } catch { + // Resurfacing is non-fatal; log and continue + print("[SyncCoordinator] resurfaceDeferrals error: \(error)") + } + } + private func performSync() async throws { + // Resurface any expired deferrals before processing new state + resurfaceDeferrals() + // Ensure account exists in DB let existingAccounts = try store.accounts() if !existingAccounts.contains(where: { $0.id == accountConfig.id }) {