Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Use transaction based APIs for Mail #1398

Merged
merged 10 commits into from
Apr 30, 2024
5 changes: 1 addition & 4 deletions Mail/Proxy/Implementation/MessageActionHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,7 @@ public struct MessageActionHandler: MessageActionHandlable {

/// Silently move mail to a specified folder
private func moveMessage(uid: String, to folderRole: FolderRole, mailboxManager: MailboxManager) async throws {
let realm = mailboxManager.getRealm()
realm.refresh()

guard let notificationMessage = realm.object(ofType: Message.self, forPrimaryKey: uid) else {
guard let notificationMessage = mailboxManager.fetchObject(ofType: Message.self, forPrimaryKey: uid) else {
throw ErrorDomain.messageNotFoundInDatabase
}

Expand Down
4 changes: 2 additions & 2 deletions Mail/Utils/DraftUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ enum DraftUtils {
) {
guard let message = thread.messages.first else { return }
// If we already have the draft locally, present it directly
if let draft = mailboxManager.draft(messageUid: message.uid, using: nil)?.detached() {
if let draft = mailboxManager.draft(messageUid: message.uid)?.detached() {
matomoOpenDraft(isLoadedRemotely: false)
composeMessageIntent.wrappedValue = ComposeMessageIntent.existing(draft: draft, originMailboxManager: mailboxManager)
} else {
Expand All @@ -45,7 +45,7 @@ enum DraftUtils {
composeMessageIntent: Binding<ComposeMessageIntent?>
) {
// If we already have the draft locally, present it directly
if let draft = mailboxManager.draft(messageUid: message.uid, using: nil)?.detached() {
if let draft = mailboxManager.draft(messageUid: message.uid)?.detached() {
matomoOpenDraft(isLoadedRemotely: false)
composeMessageIntent.wrappedValue = ComposeMessageIntent.existing(draft: draft, originMailboxManager: mailboxManager)
// Draft comes from API, we will update it after showing the ComposeMessageView
Expand Down
2 changes: 1 addition & 1 deletion Mail/Views/AI Writer/AIModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ extension AIModel {

extension AIModel {
private func getLiveDraft() -> Draft? {
return mailboxManager.getRealm().object(ofType: Draft.self, forPrimaryKey: draft.localUUID)
return mailboxManager.fetchObject(ofType: Draft.self, forPrimaryKey: draft.localUUID)
}

private func shouldOverrideSubject() -> Bool {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ struct ContactActionsView: View {
let recipient: Recipient

private var actions: [Action] {
let contact = mailboxManager.contactManager.getContact(for: recipient, realm: nil)
let contact = mailboxManager.contactManager.getContact(for: recipient)

if contact?.isRemote == true {
return [.writeEmailAction, .copyEmailAction]
Expand Down
2 changes: 1 addition & 1 deletion Mail/Views/Menu Drawer/FolderListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ struct FolderListView: View {
UserFoldersListView(folders: viewModel.userFolders)
}
.onChange(of: mailboxManager) { newMailboxManager in
viewModel.updateFolderListForMailboxManager(newMailboxManager, animateInitialChanges: true)
viewModel.updateFolderListForMailboxManager(transactionable: newMailboxManager, animateInitialChanges: true)
}
}
}
37 changes: 20 additions & 17 deletions Mail/Views/Menu Drawer/FolderListViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import Combine
import Foundation
import InfomaniakCoreDB
import MailCore
import RealmSwift
import SwiftUI
Expand All @@ -38,7 +39,7 @@ import SwiftUI

init(mailboxManager: MailboxManageable, foldersQuery: @escaping (Query<Folder>) -> Query<Bool> = { $0.toolType == nil }) {
self.foldersQuery = foldersQuery
updateFolderListForMailboxManager(mailboxManager, animateInitialChanges: false)
updateFolderListForMailboxManager(transactionable: mailboxManager, animateInitialChanges: false)

searchQueryObservation = $searchQuery
.debounce(for: .milliseconds(150), scheduler: DispatchQueue.main)
Expand All @@ -49,23 +50,25 @@ import SwiftUI
}
}

func updateFolderListForMailboxManager(_ mailboxManager: MailboxManageable, animateInitialChanges: Bool) {
foldersObservationToken = mailboxManager.getRealm()
.objects(Folder.self).where(foldersQuery)
.observe(on: DispatchQueue.main) { [weak self] results in
guard let self else {
return
}

switch results {
case .initial(let folders):
processObservedFolders(folders, animated: animateInitialChanges)
case .update(let folders, _, _, _):
processObservedFolders(folders, animated: true)
case .error:
break
}
func updateFolderListForMailboxManager(transactionable: Transactionable, animateInitialChanges: Bool) {
let objects = transactionable.fetchResults(ofType: Folder.self) { partial in
partial.where(foldersQuery)
}

foldersObservationToken = objects.observe(on: DispatchQueue.main) { [weak self] results in
guard let self else {
return
}

switch results {
case .initial(let folders):
processObservedFolders(folders, animated: animateInitialChanges)
case .update(let folders, _, _, _):
processObservedFolders(folders, animated: true)
case .error:
break
}
}
}

private func processObservedFolders(_ folders: Results<Folder>, animated: Bool) {
Expand Down
10 changes: 6 additions & 4 deletions Mail/Views/New Message/ComposeMessageIntentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import InfomaniakCoreDB
import InfomaniakDI
import MailCore
import MailCoreUI
Expand Down Expand Up @@ -98,7 +99,8 @@ struct ComposeMessageIntentView: View, IntentViewable {
case .writeTo(let recipient):
draftToWrite = Draft.writing(to: recipient)
case .reply(let messageUid, let replyMode):
if let frozenMessage = mailboxManager.getRealm().object(ofType: Message.self, forPrimaryKey: messageUid)?.freeze() {
// TODO: Can we move this transaction away from the main actor ?
if let frozenMessage = mailboxManager.fetchObject(ofType: Message.self, forPrimaryKey: messageUid)?.freeze() {
let messageReply = MessageReply(frozenMessage: frozenMessage, replyMode: replyMode)
maybeMessageReply = messageReply
draftToWrite = Draft.replying(
Expand All @@ -114,7 +116,7 @@ struct ComposeMessageIntentView: View, IntentViewable {

if let draftToWrite {
let draftLocalUUID = draftToWrite.localUUID
writeDraftToRealm(mailboxManager.getRealm(), draft: draftToWrite)
writeDraftToRealm(mailboxManager, draft: draftToWrite)

Task { @MainActor [maybeMessageReply] in
guard let liveDraft = mailboxManager.draft(localUuid: draftLocalUUID) else {
Expand All @@ -135,8 +137,8 @@ struct ComposeMessageIntentView: View, IntentViewable {
}
}

func writeDraftToRealm(_ realm: Realm, draft: Draft) {
try? realm.write {
func writeDraftToRealm(_ transactionable: Transactionable, draft: Draft) {
try? transactionable.writeTransaction { realm in
draft.action = draft.action == nil && draft.remoteUUID.isEmpty ? .initialSave : .save
draft.delay = UserDefaults.shared.cancelSendDelay.rawValue

Expand Down
5 changes: 3 additions & 2 deletions Mail/Views/Search/SearchViewModel+Observation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,9 @@ extension SearchViewModel {

let allThreadsUIDs = frozenThreads.map(\.uid)
let containAnyOf = NSPredicate(format: Self.containAnyOfUIDs, allThreadsUIDs)
let realm = mailboxManager.getRealm()
let allThreads = realm.objects(Thread.self).filter(containAnyOf)
let allThreads = mailboxManager.fetchResults(ofType: Thread.self) { partial in
partial.filter(containAnyOf)
}

observationSearchResultsChangesToken = allThreads.observe(on: observeQueue) { [weak self] changes in
guard let self else {
Expand Down
2 changes: 1 addition & 1 deletion Mail/Views/Search/SearchViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ enum SearchState {

frozenRealFolder = folder.freezeIfNeeded()
frozenSearchFolder = mailboxManager.initSearchFolder().freezeIfNeeded()
frozenFolderList = mailboxManager.getFrozenFolders(using: nil)
frozenFolderList = mailboxManager.getFrozenFolders()

searchFieldObservation = $searchValue
.debounce(for: .seconds(0.3), scheduler: DispatchQueue.main)
Expand Down
8 changes: 4 additions & 4 deletions Mail/Views/SplitView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ struct SplitView: View {
try await mailboxManager.refreshAllFolders()

let selectedFolderId = mainViewState.selectedFolder.remoteId
guard mailboxManager.getRealm().object(ofType: Folder.self, forPrimaryKey: selectedFolderId) == nil else {
guard mailboxManager.fetchObject(ofType: Folder.self, forPrimaryKey: selectedFolderId) == nil else {
return
}

Expand Down Expand Up @@ -306,10 +306,10 @@ struct SplitView: View {
try? await Task.sleep(nanoseconds: UInt64(0.5 * Double(NSEC_PER_SEC)))
}

let realm = notificationMailboxManager.getRealm()

let tappedNotificationMessage = realm.object(ofType: Message.self, forPrimaryKey: notificationPayload.messageId)?
let tappedNotificationMessage = notificationMailboxManager.fetchObject(ofType: Message.self,
forPrimaryKey: notificationPayload.messageId)?
.freezeIfNeeded()

if notification.name == .onUserTappedNotification {
// Original parent should always be in the inbox but maybe change in a later stage to always find the parent in
// inbox
Expand Down
25 changes: 13 additions & 12 deletions Mail/Views/Switch User/AccountListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,21 +39,22 @@ final class AccountListViewModel: ObservableObject {
private var mailboxObservationToken: NotificationToken?

init() {
mailboxObservationToken = mailboxInfosManager.getRealm()
.objects(Mailbox.self)
.sorted(by: \.mailboxId)
.observe(on: DispatchQueue.main) { [weak self] results in
switch results {
case .initial(let mailboxes):
let mailboxes = mailboxInfosManager.fetchResults(ofType: Mailbox.self) { partial in
partial.sorted(by: \.mailboxId)
}

mailboxObservationToken = mailboxes.observe(on: DispatchQueue.main) { [weak self] results in
switch results {
case .initial(let mailboxes):
self?.handleMailboxChanged(Array(mailboxes))
case .update(let mailboxes, _, _, _):
withAnimation {
self?.handleMailboxChanged(Array(mailboxes))
case .update(let mailboxes, _, _, _):
withAnimation {
self?.handleMailboxChanged(Array(mailboxes))
}
case .error:
break
}
case .error:
break
}
}
}

private func handleMailboxChanged(_ mailboxes: [Mailbox]) {
Expand Down
9 changes: 5 additions & 4 deletions Mail/Views/Thread List/ThreadListViewModel+Observation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,11 @@ extension ThreadListViewModel {
}

let containAnyOf = NSPredicate(format: Self.containAnyOfUIDs, allThreadsUIDs)
let realm = mailboxManager.getRealm()
let allThreads = realm.objects(Thread.self)
.filter(containAnyOf)
.sorted(by: \.date, ascending: false)
let allThreads = mailboxManager.fetchResults(ofType: Thread.self) { partial in
partial
.filter(containAnyOf)
.sorted(by: \.date, ascending: false)
}

observeFilteredThreadsToken = allThreads.observe(on: observeQueue) { [weak self] changes in
guard let self else { return }
Expand Down
4 changes: 1 addition & 3 deletions Mail/Views/Thread/Message/MessageView+Preprocessing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,7 @@ final class InlineAttachmentWorker {

/// Private accessor on the message
private var frozenMessage: Message? {
let realm = mailboxManager.getRealm()
let message = realm.object(ofType: Message.self, forPrimaryKey: messageUid)?.freezeIfNeeded()
return message
mailboxManager.fetchObject(ofType: Message.self, forPrimaryKey: messageUid)?.freezeIfNeeded()
}

/// A binding on the `PresentableBody` from `MessageView`
Expand Down
8 changes: 4 additions & 4 deletions Mail/Views/Thread/OpenThreadIntentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,10 @@ struct OpenThreadIntentView: View, IntentViewable {
}

Task { @MainActor in
let realm = mailboxManager.getRealm()
guard let folder = realm.object(ofType: Folder.self, forPrimaryKey: openThreadIntent.folderId),
let thread = mailboxManager.getRealm().object(ofType: Thread.self, forPrimaryKey: openThreadIntent.threadUid)
else {
guard let folder = mailboxManager.fetchObject(ofType: Folder.self,
forPrimaryKey: openThreadIntent.folderId),
let thread = mailboxManager.fetchObject(ofType: Thread.self,
forPrimaryKey: openThreadIntent.threadUid) else {
snackbarPresenter.show(message: MailError.localMessageNotFound.errorDescription ?? "")
return
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import CocoaLumberjackSwift
import Foundation
import InfomaniakConcurrency
import InfomaniakCore
import InfomaniakCoreDB
import UniformTypeIdentifiers

/// Abstracts that some attachment was updated
Expand Down Expand Up @@ -69,6 +70,8 @@ public final class AttachmentsManagerWorker {
private let backgroundRealm: BackgroundRealm
private let draftLocalUUID: String

public let transactionExecutor: Transactionable

private lazy var filenameDateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyyMMdd_HHmmssSS"
Expand All @@ -88,8 +91,7 @@ public final class AttachmentsManagerWorker {
return
}

let realm = backgroundRealm.getRealm()
guard let draft = realm.object(ofType: Draft.self, forPrimaryKey: draftLocalUUID),
guard let draft = transactionExecutor.fetchObject(ofType: Draft.self, forPrimaryKey: draftLocalUUID),
!draft.isInvalidated,
draft.subject.isEmpty else {
return
Expand All @@ -99,8 +101,11 @@ public final class AttachmentsManagerWorker {
return
}

try? realm.write {
draft.subject = attachmentTitle
try? transactionExecutor.writeTransaction { writableRealm in
guard let liveDraft = writableRealm.object(ofType: Draft.self, forPrimaryKey: draftLocalUUID) else {
return
}
liveDraft.subject = attachmentTitle
}
}
}
Expand All @@ -109,6 +114,7 @@ public final class AttachmentsManagerWorker {
self.backgroundRealm = backgroundRealm
self.draftLocalUUID = draftLocalUUID
self.mailboxManager = mailboxManager
transactionExecutor = TransactionExecutor(realmAccessible: backgroundRealm)
}

func addLocalAttachment(attachment: Attachment) async -> Attachment? {
Expand Down Expand Up @@ -295,7 +301,7 @@ public final class AttachmentsManagerWorker {

extension AttachmentsManagerWorker: AttachmentsManagerWorkable {
public var liveDraft: Draft? {
guard let liveDraft = backgroundRealm.getRealm().object(ofType: Draft.self, forPrimaryKey: draftLocalUUID),
guard let liveDraft = transactionExecutor.fetchObject(ofType: Draft.self, forPrimaryKey: draftLocalUUID),
!liveDraft.isInvalidated else {
return nil
}
Expand Down
Loading
Loading