diff --git a/Mail/Views/New Message/ComposeMessageBodyView.swift b/Mail/Views/New Message/ComposeMessageBodyView.swift index 2f50aa82d..d88632811 100644 --- a/Mail/Views/New Message/ComposeMessageBodyView.swift +++ b/Mail/Views/New Message/ComposeMessageBodyView.swift @@ -22,18 +22,13 @@ import RealmSwift import SwiftUI struct ComposeMessageBodyView: View { - @EnvironmentObject private var mailboxManager: MailboxManager - - /// Something to track the initial loading of a default signature - @EnvironmentObject private var signatureManager: SignaturesManager - @State private var isShowingCamera = false @State private var isShowingFileSelection = false @State private var isShowingPhotoLibrary = false @StateObject private var editorModel = RichTextEditorModel() - @StateRealmObject var draft: Draft + @ObservedRealmObject var draft: Draft @Binding var isLoadingContent: Bool @Binding var editorFocus: Bool @@ -66,24 +61,6 @@ struct ComposeMessageBodyView: View { .frame(height: editorModel.height + 20) .padding(.vertical, 8) } - .task { - await prepareCompleteDraft() - } - .task { - await prepareReplyForwardBodyAndAttachments() - } - .onChange(of: signatureManager.loadingSignatureState) { state in - switch state { - case .success: - setSignature() - case .error: - // Unable to get signatures, "An error occurred" and close modal. - IKSnackBar.showSnackBar(message: MailError.unknownError.localizedDescription) - dismiss() - case .progress: - break - } - } .fullScreenCover(isPresented: $isShowingCamera) { CameraPicker { data in attachmentsManager.importAttachments(attachments: [data]) @@ -103,84 +80,9 @@ struct ComposeMessageBodyView: View { .ignoresSafeArea() } } - - private func prepareCompleteDraft() async { - guard draft.messageUid != nil && draft.remoteUUID.isEmpty else { return } - - do { - if let fetchedDraft = try await mailboxManager.draft(partialDraft: draft), - let liveFetchedDraft = fetchedDraft.thaw() { - draft = liveFetchedDraft - } - isLoadingContent = false - } catch { - dismiss() - IKSnackBar.showSnackBar(message: MailError.unknownError.localizedDescription) - } - } - - private func prepareReplyForwardBodyAndAttachments() async { - guard let messageReply else { return } - - let prepareTask = Task.detached { - try await prepareBody(message: messageReply.message, replyMode: messageReply.replyMode) - try await prepareAttachments(message: messageReply.message, replyMode: messageReply.replyMode) - } - - do { - _ = try await prepareTask.value - - isLoadingContent = false - } catch { - dismiss() - IKSnackBar.showSnackBar(message: MailError.unknownError.localizedDescription) - } - } - - private func setSignature() { - guard draft.identityId == nil || draft.identityId?.isEmpty == true else { - return - } - - guard let defaultSignature = mailboxManager.getStoredSignatures().defaultSignature else { - return - } - - let body = $draft.body.wrappedValue - let signedBody = defaultSignature.appendSignature(to: body) - - // At this point we have signatures in base up to date, we use the default one. - $draft.identityId.wrappedValue = "\(defaultSignature.id)" - $draft.body.wrappedValue = signedBody - } - - private func prepareBody(message: Message, replyMode: ReplyMode) async throws { - if !message.fullyDownloaded { - try await mailboxManager.message(message: message) - } - - guard let freshMessage = message.thaw() else { return } - freshMessage.realm?.refresh() - $draft.body.wrappedValue = Draft.replyingBody(message: freshMessage, replyMode: replyMode) - } - - private func prepareAttachments(message: Message, replyMode: ReplyMode) async throws { - guard replyMode == .forward else { return } - let attachments = try await mailboxManager.apiFetcher.attachmentsToForward( - mailbox: mailboxManager.mailbox, - message: message - ).attachments - - for attachment in attachments { - $draft.attachments.append(attachment) - } - attachmentsManager.completeUploadedAttachments() - } } struct ComposeMessageBodyView_Previews: PreviewProvider { - static let signaturesManager = SignaturesManager(mailboxManager: PreviewHelper.sampleMailboxManager) - static var previews: some View { @Environment(\.dismiss) var dismiss @@ -194,6 +96,5 @@ struct ComposeMessageBodyView_Previews: PreviewProvider { alert: NewMessageAlert(), dismiss: dismiss, messageReply: nil) - .environmentObject(signaturesManager) } } diff --git a/Mail/Views/New Message/ComposeMessageView.swift b/Mail/Views/New Message/ComposeMessageView.swift index 666abcc15..69a910fac 100644 --- a/Mail/Views/New Message/ComposeMessageView.swift +++ b/Mail/Views/New Message/ComposeMessageView.swift @@ -149,10 +149,27 @@ struct ComposeMessageView: View { dismiss: dismiss, messageReply: messageReply ) - .environmentObject(signatureManager) } } } + .task { + await prepareCompleteDraft() + } + .task { + await prepareReplyForwardBodyAndAttachments() + } + .onChange(of: signatureManager.loadingSignatureState) { state in + switch state { + case .success: + setSignature() + case .error: + // Unable to get signatures, "An error occurred" and close modal. + IKSnackBar.showSnackBar(message: MailError.unknownError.localizedDescription) + dismiss() + case .progress: + break + } + } .background(MailResourcesAsset.backgroundColor.swiftUIColor) .onAppear { switch messageReply?.replyMode { @@ -198,7 +215,7 @@ struct ComposeMessageView: View { .background(MailResourcesAsset.backgroundColor.swiftUIColor) } - // MAK: - Func + // MARK: - Func private func didTouchDismiss() { guard attachmentsManager.allAttachmentsUploaded else { @@ -235,6 +252,76 @@ struct ComposeMessageView: View { realm.add(draft, update: .modified) } } + + private func prepareCompleteDraft() async { + guard draft.messageUid != nil && draft.remoteUUID.isEmpty else { return } + + do { + try await mailboxManager.draft(partialDraft: draft) + isLoadingContent = false + } catch { + dismiss() + IKSnackBar.showSnackBar(message: MailError.unknownError.localizedDescription) + } + } + + private func prepareReplyForwardBodyAndAttachments() async { + guard let messageReply else { return } + + let prepareTask = Task.detached { + try await prepareBody(message: messageReply.message, replyMode: messageReply.replyMode) + try await prepareAttachments(message: messageReply.message, replyMode: messageReply.replyMode) + } + + do { + _ = try await prepareTask.value + + isLoadingContent = false + } catch { + dismiss() + IKSnackBar.showSnackBar(message: MailError.unknownError.localizedDescription) + } + } + + private func prepareBody(message: Message, replyMode: ReplyMode) async throws { + if !message.fullyDownloaded { + try await mailboxManager.message(message: message) + } + + guard let freshMessage = message.thaw() else { return } + freshMessage.realm?.refresh() + $draft.body.wrappedValue = Draft.replyingBody(message: freshMessage, replyMode: replyMode) + } + + private func prepareAttachments(message: Message, replyMode: ReplyMode) async throws { + guard replyMode == .forward else { return } + let attachments = try await mailboxManager.apiFetcher.attachmentsToForward( + mailbox: mailboxManager.mailbox, + message: message + ).attachments + + for attachment in attachments { + $draft.attachments.append(attachment) + } + attachmentsManager.completeUploadedAttachments() + } + + private func setSignature() { + guard draft.identityId == nil || draft.identityId?.isEmpty == true else { + return + } + + guard let defaultSignature = mailboxManager.getStoredSignatures().defaultSignature else { + return + } + + let body = $draft.body.wrappedValue + let signedBody = defaultSignature.appendSignature(to: body) + + // At this point we have signatures in base up to date, we use the default one. + $draft.identityId.wrappedValue = "\(defaultSignature.id)" + $draft.body.wrappedValue = signedBody + } } struct ComposeMessageView_Previews: PreviewProvider { diff --git a/MailCore/Cache/MailboxManager.swift b/MailCore/Cache/MailboxManager.swift index f716ee9bd..54b012e77 100644 --- a/MailCore/Cache/MailboxManager.swift +++ b/MailCore/Cache/MailboxManager.swift @@ -1120,9 +1120,9 @@ public class MailboxManager: ObservableObject { return realm.objects(Draft.self).where { $0.action != nil } } - public func draft(partialDraft: Draft) async throws -> Draft? { + public func draft(partialDraft: Draft) async throws { guard let associatedMessage = getRealm().object(ofType: Message.self, forPrimaryKey: partialDraft.messageUid)?.freeze() - else { return nil } + else { throw MailError.localMessageNotFound } // Get from API let draft = try await apiFetcher.draft(from: associatedMessage) @@ -1140,8 +1140,6 @@ public class MailboxManager: ObservableObject { realm.add(draft.detached(), update: .modified) } } - - return getRealm().object(ofType: Draft.self, forPrimaryKey: draft.localUUID)?.freeze() } public func draft(messageUid: String, using realm: Realm? = nil) -> Draft? {