Skip to content

Commit

Permalink
refactor: Use transaction based APIs for Mail (#1398)
Browse files Browse the repository at this point in the history
  • Loading branch information
adrien-coye authored Apr 30, 2024
2 parents 860c6f3 + 2a0e28c commit 09b3664
Show file tree
Hide file tree
Showing 45 changed files with 597 additions and 261 deletions.
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

0 comments on commit 09b3664

Please sign in to comment.