Skip to content

Commit

Permalink
Merge pull request #685 from Infomaniak/download-body
Browse files Browse the repository at this point in the history
feat(ComposeView): Download body for reply / forward
  • Loading branch information
Ambrdctr committed Apr 11, 2023
2 parents 537e75e + 6255941 commit a538ef5
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 84 deletions.
25 changes: 3 additions & 22 deletions Mail/Views/Bottom sheets/Actions/ActionsViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ enum ActionsTarget: Equatable {
case .archive:
try await move(to: .archive)
case .forward:
try await reply(mode: .forward([]))
try await reply(mode: .forward)
case .markAsRead, .markAsUnread:
try await toggleRead()
case .move:
Expand Down Expand Up @@ -380,33 +380,14 @@ enum ActionsTarget: Equatable {
}

private func reply(mode: ReplyMode) async throws {
var completeMode = mode
switch target {
case let .threads(threads, _):
// We don't handle this action in multiple selection
guard threads.count == 1, let thread = threads.first,
let message = thread.messages.last(where: { !$0.isDraft }) else { break }
// Download message if needed to get body
if !message.fullyDownloaded {
try await mailboxManager.message(message: message)
}
if mode == .forward([]) {
let attachments = try await mailboxManager.apiFetcher.attachmentsToForward(
mailbox: mailboxManager.mailbox,
message: message
).attachments
completeMode = .forward(attachments)
}
replyHandler?(message, completeMode)
replyHandler?(message, mode)
case let .message(message):
if mode == .forward([]) {
let attachments = try await mailboxManager.apiFetcher.attachmentsToForward(
mailbox: mailboxManager.mailbox,
message: message
).attachments
completeMode = .forward(attachments)
}
replyHandler?(message, completeMode)
replyHandler?(message, mode)
}
}

Expand Down
7 changes: 3 additions & 4 deletions Mail/Views/New Message/Attachments/AttachmentsManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,14 @@ class AttachmentsManager: ObservableObject {
init(draft: Draft, mailboxManager: MailboxManager) {
self.draft = draft
self.mailboxManager = mailboxManager

completeUploadedAttachments()
}

private func completeUploadedAttachments() {
func completeUploadedAttachments() {
for attachment in attachments {
var uploadTask = attachmentUploadTaskFor(uuid: attachment.uuid)
let uploadTask = attachmentUploadTaskFor(uuid: attachment.uuid)
uploadTask.progress = 1
}
objectWillChange.send()
}

private func updateAttachment(oldAttachment: Attachment, newAttachment: Attachment) {
Expand Down
92 changes: 74 additions & 18 deletions Mail/Views/New Message/ComposeMessageView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ struct ComposeMessageView: View {
@StateRealmObject var draft: Draft
@State private var editor = RichTextEditorModel()
@State private var showCc = false
@State private var isLoadingContent: Bool
@FocusState private var focusedField: ComposeViewFieldType?

@State private var addRecipientHandler: ((Recipient) -> Void)?
Expand All @@ -78,6 +79,8 @@ struct ComposeMessageView: View {

@StateObject private var alert = NewMessageAlert()

let messageReply: MessageReply?

private var isSendButtonDisabled: Bool {
return draft.identityId?.isEmpty == true
|| (draft.to.isEmpty && draft.cc.isEmpty && draft.bcc.isEmpty)
Expand All @@ -88,7 +91,8 @@ struct ComposeMessageView: View {
return (!autocompletion.isEmpty || !unknownRecipientAutocompletion.isEmpty) && focusedField != nil
}

private init(mailboxManager: MailboxManager, draft: Draft) {
private init(mailboxManager: MailboxManager, draft: Draft, messageReply: MessageReply? = nil) {
self.messageReply = messageReply
_mailboxManager = State(initialValue: mailboxManager)
if draft.identityId == nil || draft.identityId?.isEmpty == true,
let signature = mailboxManager.getSignatureResponse() {
Expand All @@ -105,6 +109,7 @@ struct ComposeMessageView: View {
_draft = StateRealmObject(wrappedValue: draft)
_showCc = State(initialValue: !draft.bcc.isEmpty || !draft.cc.isEmpty)
_attachmentsManager = StateObject(wrappedValue: AttachmentsManager(draft: draft, mailboxManager: mailboxManager))
_isLoadingContent = State(initialValue: (draft.messageUid != nil && draft.remoteUUID.isEmpty) || messageReply != nil)
}

var body: some View {
Expand Down Expand Up @@ -155,7 +160,7 @@ struct ComposeMessageView: View {
}
}
.overlay {
if draft.messageUid != nil && draft.remoteUUID.isEmpty {
if isLoadingContent {
ProgressView()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(MailResourcesAsset.backgroundColor.swiftUIColor)
Expand Down Expand Up @@ -214,9 +219,9 @@ struct ComposeMessageView: View {
}
.customAlert(isPresented: $alert.isShowing) {
switch alert.state {
case let .link(handler):
case .link(let handler):
AddLinkView(actionHandler: handler)
case let .emptySubject(handler):
case .emptySubject(let handler):
EmptySubjectView(actionHandler: handler)
case .none:
EmptyView()
Expand All @@ -228,16 +233,10 @@ struct ComposeMessageView: View {
}
}
.task {
guard draft.messageUid != nil && draft.remoteUUID.isEmpty else { return }

do {
if let fetchedDraft = try await mailboxManager.draft(partialDraft: draft),
let liveFetchedDraft = fetchedDraft.thaw() {
self.draft = liveFetchedDraft
}
} catch {
// Fail silently
}
await prepareCompleteDraft()
}
.task {
await prepareReplyForwardBodyAndAttachments()
}
.navigationViewStyle(.stack)
.defaultAppStorage(.shared)
Expand Down Expand Up @@ -304,6 +303,65 @@ struct ComposeMessageView: View {
}
dismiss()
}

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 prepareBodyTask = Task {
try await prepareBody(message: messageReply.message, replyMode: messageReply.replyMode)
}

let prepareAttachmentsTask = Task {
try await prepareAttachments(message: messageReply.message, replyMode: messageReply.replyMode)
}

do {
_ = try await prepareBodyTask.value
_ = try await prepareAttachmentsTask.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()
}
}

extension ComposeMessageView {
Expand All @@ -312,12 +370,10 @@ extension ComposeMessageView {
}

static func replyOrForwardMessage(messageReply: MessageReply, mailboxManager: MailboxManager) -> ComposeMessageView {
let message = messageReply.message
// If message doesn't exist anymore try to show the frozen one
let freshMessage = message.thaw() ?? message
return ComposeMessageView(
mailboxManager: mailboxManager,
draft: .replying(to: freshMessage, mode: messageReply.replyMode, localDraftUUID: messageReply.localDraftUUID)
draft: .replying(reply: messageReply),
messageReply: messageReply
)
}

Expand Down
18 changes: 6 additions & 12 deletions Mail/Views/Thread/ThreadView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ struct ThreadView: View {
@StateObject private var moveSheet = MoveSheet()
@StateObject private var bottomSheet = MessageBottomSheet()
@StateObject private var threadBottomSheet = ThreadBottomSheet()

@State private var showEmptyView = false

@EnvironmentObject var globalBottomSheet: GlobalBottomSheet
Expand Down Expand Up @@ -136,20 +136,20 @@ struct ThreadView: View {
ComposeMessageView.replyOrForwardMessage(messageReply: messageReply, mailboxManager: mailboxManager)
}
.sheet(isPresented: $moveSheet.isShowing) {
if case let .move(folderId, handler) = moveSheet.state {
if case .move(let folderId, let handler) = moveSheet.state {
MoveEmailView.sheetView(mailboxManager: mailboxManager, from: folderId, moveHandler: handler)
}
}
.floatingPanel(state: bottomSheet) {
switch bottomSheet.state {
case let .contact(recipient, isRemote):
case .contact(let recipient, let isRemote):
ContactActionsView(
recipient: recipient,
isRemoteContact: isRemote,
bottomSheet: bottomSheet,
mailboxManager: mailboxManager
)
case let .replyOption(message, isThread):
case .replyOption(let message, let isThread):
ReplyActionsView(
mailboxManager: mailboxManager,
target: isThread ? .threads([thread], false) : .message(message),
Expand All @@ -164,7 +164,7 @@ struct ThreadView: View {
}
}
.floatingPanel(state: threadBottomSheet, halfOpening: true) {
if case let .actions(target) = threadBottomSheet.state, !target.isInvalidated {
if case .actions(let target) = threadBottomSheet.state, !target.isInvalidated {
ActionsView(mailboxManager: mailboxManager,
target: target,
state: threadBottomSheet,
Expand Down Expand Up @@ -206,13 +206,7 @@ struct ThreadView: View {
}
case .forward:
guard let message = thread.messages.last else { return }
Task {
let attachments = try await mailboxManager.apiFetcher.attachmentsToForward(
mailbox: mailboxManager.mailbox,
message: message
).attachments
messageReply = MessageReply(message: message, replyMode: .forward(attachments))
}
messageReply = MessageReply(message: message, replyMode: .forward)
case .archive:
Task {
await tryOrDisplayError {
Expand Down
51 changes: 23 additions & 28 deletions MailCore/Models/Draft.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,11 @@ public enum SaveDraftOption: String, Codable, PersistableEnum {

public enum ReplyMode: Equatable {
case reply, replyAll
case forward([Attachment])
case forward

var isReply: Bool {
return self == .reply || self == .replyAll
}

public static func == (lhs: ReplyMode, rhs: ReplyMode) -> Bool {
switch (lhs, rhs) {
case (.reply, .reply):
return true
case (.replyAll, .replyAll):
return true
case (.forward(_), .forward(_)):
return true
default:
return false
}
}
}

public struct DraftResponse: Codable {
Expand Down Expand Up @@ -194,22 +181,33 @@ public class Draft: Object, Decodable, Identifiable, Encodable {
return Draft(to: [recipient.detached()])
}

public static func replying(to message: Message, mode: ReplyMode, localDraftUUID: String) -> Draft {
var subject = "\(message.formattedSubject)"
public static func replyingBody(message: Message, replyMode: ReplyMode) -> String {
let unsafeQuote: String
var attachments: [Attachment] = []
switch replyMode {
case .reply, .replyAll:
unsafeQuote = Constants.replyQuote(message: message)
case .forward:
unsafeQuote = Constants.forwardQuote(message: message)
}

let quote = MessageBodyUtils.cleanHtmlContent(rawHtml: unsafeQuote) ?? ""

return "<br><br>" + quote
}

public static func replying(reply: MessageReply) -> Draft {
let message = reply.message
let mode = reply.replyMode
var subject = "\(message.formattedSubject)"
switch mode {
case .reply, .replyAll:
if !subject.starts(with: "Re: ") {
subject = "Re: \(subject)"
}
unsafeQuote = Constants.replyQuote(message: message)
case .forward(let attachmentsToForward):
case .forward:
if !subject.starts(with: "Fwd: ") {
subject = "Fwd: \(subject)"
}
unsafeQuote = Constants.forwardQuote(message: message)
attachments = attachmentsToForward.map { Attachment(value: $0) }
}

var recipientHolder = RecipientHolder()
Expand All @@ -218,18 +216,15 @@ public class Draft: Object, Decodable, Identifiable, Encodable {
recipientHolder = message.recipientsForReplyTo(replyAll: mode == .replyAll)
}

let quote = MessageBodyUtils.cleanHtmlContent(rawHtml: unsafeQuote) ?? ""

return Draft(localUUID: localDraftUUID,
return Draft(localUUID: reply.localDraftUUID,
inReplyToUid: mode.isReply ? message.uid : nil,
forwardedUid: mode == .forward([]) ? message.uid : nil,
forwardedUid: mode == .forward ? message.uid : nil,
references: "\(message.references ?? "") \(message.messageId ?? "")",
inReplyTo: message.messageId,
subject: subject,
body: "<br><br>\(quote)",
body: "",
to: recipientHolder.to,
cc: recipientHolder.cc,
attachments: attachments)
cc: recipientHolder.cc)
}

public func setSignature(_ signatureResponse: SignatureResponse) {
Expand Down

0 comments on commit a538ef5

Please sign in to comment.