diff --git a/Mail/Views/Thread List/ThreadListView.swift b/Mail/Views/Thread List/ThreadListView.swift index ba207ce29..c1e216d97 100644 --- a/Mail/Views/Thread List/ThreadListView.swift +++ b/Mail/Views/Thread List/ThreadListView.swift @@ -257,7 +257,10 @@ struct ThreadListView: View { guard let folder = newFolder else { return } viewModel.isLoadingPage = false + Task { + await viewModel.mailboxManager.cancelRefresh() + fetchingTask?.cancel() _ = await fetchingTask?.result fetchingTask = nil diff --git a/Mail/Views/Thread List/ThreadListViewModel.swift b/Mail/Views/Thread List/ThreadListViewModel.swift index 55385fafe..419e126b7 100644 --- a/Mail/Views/Thread List/ThreadListViewModel.swift +++ b/Mail/Views/Thread List/ThreadListViewModel.swift @@ -201,15 +201,8 @@ final class DateSection: Identifiable, Equatable { isLoadingPage = true } - await tryOrDisplayError { - try await mailboxManager.threads(folder: folder.freezeIfNeeded()) { - Task { - withAnimation { - self.isLoadingPage = false - } - } - } - } + await mailboxManager.refresh(folder: folder.freezeIfNeeded()) + withAnimation { isLoadingPage = false } diff --git a/MailCore/Cache/DraftManager.swift b/MailCore/Cache/DraftManager.swift index 9f142f9cc..c4fa34812 100644 --- a/MailCore/Cache/DraftManager.swift +++ b/MailCore/Cache/DraftManager.swift @@ -156,7 +156,7 @@ public class DraftManager { private func refreshDraftFolder(latestSendDate: Date?, mailboxManager: MailboxManager) async throws { if let draftFolder = mailboxManager.getFolder(with: .draft)?.freeze() { - try await mailboxManager.threads(folder: draftFolder) + await mailboxManager.refresh(folder: draftFolder) if let latestSendDate = latestSendDate { /* @@ -165,7 +165,7 @@ public class DraftManager { */ let delay = latestSendDate.timeIntervalSinceNow try await Task.sleep(nanoseconds: UInt64(1_000_000_000 * max(Double(delay), 1.5))) - try await mailboxManager.threads(folder: draftFolder) + await mailboxManager.refresh(folder: draftFolder) } await mailboxManager.deleteOrphanDrafts() @@ -179,7 +179,7 @@ public class DraftManager { try await mailboxManager.delete(draft: liveDraft.freeze()) await IKSnackBar.showSnackBar(message: MailResourcesStrings.Localizable.snackbarDraftDeleted) if let draftFolder = mailboxManager.getFolder(with: .draft)?.freeze() { - try await mailboxManager.threads(folder: draftFolder) + await mailboxManager.refresh(folder: draftFolder) } } } diff --git a/MailCore/Cache/MailboxManager.swift b/MailCore/Cache/MailboxManager.swift index efabebae5..5d6c22211 100644 --- a/MailCore/Cache/MailboxManager.swift +++ b/MailCore/Cache/MailboxManager.swift @@ -66,6 +66,8 @@ public class MailboxManager: ObservableObject { public private(set) var apiFetcher: MailApiFetcher private let backgroundRealm: BackgroundRealm + private lazy var refreshActor = RefreshActor(mailboxManager: self) + public init(mailbox: Mailbox, apiFetcher: MailApiFetcher) { self.mailbox = mailbox self.apiFetcher = apiFetcher @@ -222,7 +224,7 @@ public class MailboxManager: ObservableObject { public func flushFolder(folder: Folder) async throws -> Bool { let response = try await apiFetcher.flushFolder(mailbox: mailbox, folderId: folder.id) - try await threads(folder: folder) + await refresh(folder: folder) return response } @@ -236,7 +238,7 @@ public class MailboxManager: ObservableObject { for folder in orderedSet { guard let impactedFolder = folder as? Folder else { continue } - try await threads(folder: impactedFolder) + await refresh(folder: impactedFolder) } } @@ -637,7 +639,10 @@ public class MailboxManager: ObservableObject { public func messages(folder: Folder) async throws { guard !Task.isCancelled else { return } - let previousCursor = folder.cursor + let realm = getRealm() + let freshFolder = folder.fresh(using: realm) + + let previousCursor = freshFolder?.cursor var messagesUids: MessagesUids if previousCursor == nil { @@ -892,7 +897,7 @@ public class MailboxManager: ObservableObject { let folders = Set(threads.compactMap(\.folder)) for thread in threads { do { - try thread.recomputeOrFail() + try thread.recomputeOrFail() } catch { SentrySDK.capture(message: "Thread has nil lastMessageFromFolderDate") { scope in scope.setContext(value: ["dates": "\(thread.messages.map { $0.date })", @@ -1173,6 +1178,16 @@ public class MailboxManager: ObservableObject { } } + // MARK: - RefreshActor + + public func refresh(folder: Folder) async { + await refreshActor.refresh(folder: folder) + } + + public func cancelRefresh() async { + await refreshActor.cancelRefresh() + } + // MARK: - Utilities struct MessagePropertiesOptions: OptionSet { diff --git a/MailCore/Cache/RefreshActor.swift b/MailCore/Cache/RefreshActor.swift new file mode 100644 index 000000000..ae30f9aa2 --- /dev/null +++ b/MailCore/Cache/RefreshActor.swift @@ -0,0 +1,47 @@ +/* + Infomaniak Mail - iOS App + Copyright (C) 2022 Infomaniak Network SA + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +import Foundation + +public actor RefreshActor { + weak var mailboxManager: MailboxManager? + + private var refreshTask: Task? + + public init(mailboxManager: MailboxManager) { + self.mailboxManager = mailboxManager + } + + public func refresh(folder: Folder) async { + await cancelRefresh() + + refreshTask = Task { + await tryOrDisplayError { + try await mailboxManager?.threads(folder: folder) + refreshTask = nil + } + } + _ = await refreshTask?.result + } + + public func cancelRefresh() async { + refreshTask?.cancel() + _ = await refreshTask?.result + refreshTask = nil + } +} diff --git a/MailNotificationServiceExtension/NotificationService.swift b/MailNotificationServiceExtension/NotificationService.swift index 35f3cce8a..b80bed709 100644 --- a/MailNotificationServiceExtension/NotificationService.swift +++ b/MailNotificationServiceExtension/NotificationService.swift @@ -57,7 +57,7 @@ class NotificationService: UNNotificationServiceExtension { // We do nothing if we don't have an initial cursor return nil } - try await mailboxManager.threads(folder: inboxFolder.freezeIfNeeded()) + await mailboxManager.refresh(folder: inboxFolder.freezeIfNeeded()) @ThreadSafe var message = mailboxManager.getRealm().object(ofType: Message.self, forPrimaryKey: uid)