Skip to content

Commit

Permalink
feat: Save threads in kdrive (#1585)
Browse files Browse the repository at this point in the history
  • Loading branch information
lebojo authored Jan 9, 2025
2 parents 235ee81 + d82fcf2 commit 76c1e0c
Show file tree
Hide file tree
Showing 15 changed files with 149 additions and 38 deletions.
47 changes: 47 additions & 0 deletions Mail/Views/Alerts/ConfirmationSaveThreadInKdrive.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
Infomaniak Mail - iOS App
Copyright (C) 2024 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 <http://www.gnu.org/licenses/>.
*/

import InfomaniakCore
import InfomaniakCoreSwiftUI
import MailCore
import MailResources
import SwiftUI

struct ConfirmationSaveThreadInKdrive: View {
@EnvironmentObject private var mailboxManager: MailboxManager

let targetMessages: [Message]

var body: some View {
VStack(spacing: 0) {
Text(Action.saveThreadInkDrive.title)
.textStyle(.bodyMedium)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.bottom, IKPadding.alertTitleBottom)
Text(MailResourcesStrings.Localizable.downloadAlertDescription)
.textStyle(.bodySecondary)
.padding(.bottom, IKPadding.alertDescriptionBottom)
ModalButtonsView(primaryButtonTitle: MailResourcesStrings.Localizable.buttonConfirm) {
await tryOrDisplayError {
let filesURL = try await mailboxManager.apiFetcher.download(messages: targetMessages)
try DeeplinkService().shareFilesToKdrive(filesURL)
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ struct ActionsPanelViewModifier: ViewModifier {
@ModalState private var messagesToMove: [Message]?
@ModalState private var flushAlert: FlushAlertState?
@ModalState private var shareMailLink: ShareMailLinkResult?
@ModalState private var messagesToDownload: [Message]?

@Binding var messages: [Message]?
let originFolder: Folder?
Expand All @@ -71,7 +72,8 @@ struct ActionsPanelViewModifier: ViewModifier {
nearestReportJunkMessageActionsPanel: $reportForJunkMessages,
nearestReportedForPhishingMessageAlert: $reportedForPhishingMessage,
nearestReportedForDisplayProblemMessageAlert: $reportedForDisplayProblemMessage,
nearestShareMailLinkPanel: $shareMailLink
nearestShareMailLinkPanel: $shareMailLink,
messagesToDownload: $messagesToDownload
)
}

Expand Down Expand Up @@ -116,6 +118,9 @@ struct ActionsPanelViewModifier: ViewModifier {
.customAlert(item: $flushAlert) { item in
FlushFolderAlertView(flushAlert: item, folder: originFolder)
}
.customAlert(item: $messagesToDownload) { messages in
ConfirmationSaveThreadInKdrive(targetMessages: messages)
}
.sheet(item: $shareMailLink) { shareMailLinkResult in
if #available(iOS 16.0, *) {
ActivityView(activityItems: [shareMailLinkResult.url])
Expand Down
4 changes: 1 addition & 3 deletions Mail/Views/Bottom sheets/Actions/ActionsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,7 @@ struct ActionButtonLabel: View {
var body: some View {
HStack(spacing: IKPadding.medium) {
action.icon
.resizable()
.scaledToFit()
.frame(width: 24, height: 24)
.iconSize(.large)
.foregroundStyle(iconColor)
Text(action.title)
.foregroundStyle(titleColor)
Expand Down
7 changes: 4 additions & 3 deletions Mail/Views/Bottom sheets/ReportDisplayProblemView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,11 @@ struct ReportDisplayProblemView: View {
private func report() async {
await tryOrDisplayError {
// Download message
let fileURL = try await mailboxManager.apiFetcher.download(message: message)
let fileURL = try await mailboxManager.apiFetcher.download(messages: [message])
guard let firstFileURL = fileURL.first else { throw MailError.unknownError }
// Send it via Sentry
let fileAttachment = Attachment(path: fileURL.path,
filename: fileURL.lastPathComponent,
let fileAttachment = Attachment(path: firstFileURL.path,
filename: firstFileURL.lastPathComponent,
contentType: "message/rfc822")
_ = SentrySDK.capture(message: "Message display problem reported") { scope in
scope.addAttachment(fileAttachment)
Expand Down
2 changes: 1 addition & 1 deletion Mail/Views/Thread List/ThreadListModifiers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ struct ThreadListToolbar: ViewModifier {
panelSource: .threadList,
popoverArrowEdge: .bottom
) { action in
guard action != .openMovePanel else { return }
guard action.shouldDisableMultipleSelection else { return }
multipleSelectionViewModel.disable()
}
}
Expand Down
2 changes: 1 addition & 1 deletion MailCore/API/MailApiFetcher/MailApiFetchable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public protocol MailApiCommonFetchable {

func threads(from resource: String, searchFilter: [URLQueryItem]) async throws -> ThreadResult

func download(message: Message) async throws -> URL
func download(messages: [Message]) async throws -> [URL]

func quotas(mailbox: Mailbox) async throws -> Quotas

Expand Down
24 changes: 17 additions & 7 deletions MailCore/API/MailApiFetcher/MailApiFetcher+Common.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import Alamofire
import Foundation
import InfomaniakConcurrency
import InfomaniakCore
import InfomaniakLogin

Expand Down Expand Up @@ -85,13 +86,22 @@ public extension MailApiFetcher {
try await perform(request: authenticatedRequest(.resource(resource, queryItems: searchFilter)))
}

func download(message: Message) async throws -> URL {
let destination = DownloadRequest.suggestedDownloadDestination(options: [
.createIntermediateDirectories,
.removePreviousFile
])
let download = authenticatedSession.download(Endpoint.resource(message.downloadResource).url, to: destination)
return try await download.serializingDownloadedFileURL().value
func download(messages: [Message]) async throws -> [URL] {
let temporaryDirectory = FileManager.default.temporaryDirectory

return try await messages.concurrentMap { message in
let directoryURL = temporaryDirectory.appendingPathComponent(message.uid, isDirectory: true)
let destination: DownloadRequest.Destination = { _, response in
(
directoryURL.appendingPathComponent(response.suggestedFilename!),
[.createIntermediateDirectories, .removePreviousFile]
)
}

let download = self.authenticatedSession.download(Endpoint.resource(message.downloadResource).url, to: destination)
let messageUrl = try await download.serializingDownloadedFileURL().value
return messageUrl
}
}

func quotas(mailbox: Mailbox) async throws -> Quotas {
Expand Down
22 changes: 16 additions & 6 deletions MailCore/Cache/Actions/Action+List.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ extension Action: CaseIterable {
}
}

public var shouldDismiss: Bool {
return ![.saveThreadInkDrive].contains(self)
}

public var shouldDisableMultipleSelection: Bool {
return ![.openMovePanel, .saveThreadInkDrive].contains(self)
}

private static func actionsForMessage(_ message: Message, origin: ActionOrigin,
userIsStaff: Bool) -> (quickActions: [Action], listActions: [Action]) {
@LazyInjectService var platformDetector: PlatformDetectable
Expand All @@ -95,7 +103,7 @@ extension Action: CaseIterable {
star ? .unstar : .star,
print ? .print : nil,
.shareMailLink,
platformDetector.isMac ? nil : .saveMailInkDrive,
platformDetector.isMac ? nil : .saveThreadInkDrive,
userIsStaff ? .reportDisplayProblem : nil
]

Expand All @@ -118,7 +126,8 @@ extension Action: CaseIterable {

let listActions: [Action] = [
spam ? .nonSpam : .reportJunk,
star ? .unstar : .star
star ? .unstar : .star,
.saveThreadInkDrive
]

return (quickActions, listActions)
Expand All @@ -138,7 +147,8 @@ extension Action: CaseIterable {
spamAction,
unread ? .markAsUnread : .markAsRead,
archive ? .archive : .moveToInbox,
showUnstar ? .unstar : .star
showUnstar ? .unstar : .star,
.saveThreadInkDrive
]

return (Action.quickActions, tempListActions.compactMap { $0 })
Expand Down Expand Up @@ -361,11 +371,11 @@ public extension Action {
iconResource: MailResourcesAsset.emailActionShare,
matomoName: "shareLink"
)
static let saveMailInkDrive = Action(
id: "saveMailInkDrive",
static let saveThreadInkDrive = Action(
id: "saveThreadInkDrive",
title: MailResourcesStrings.Localizable.saveMailInkDrive,
iconResource: MailResourcesAsset.kdriveLogo,
matomoName: "saveInkDrive"
matomoName: "saveThreadInkDrive"
)

// MARK: Account Actions
Expand Down
11 changes: 8 additions & 3 deletions MailCore/Cache/Actions/ActionOrigin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public struct ActionOrigin {
private(set) var nearestReportedForPhishingMessageAlert: Binding<Message?>?
private(set) var nearestReportedForDisplayProblemMessageAlert: Binding<Message?>?
private(set) var nearestShareMailLinkPanel: Binding<ShareMailLinkResult?>?
private(set) var messagesToDownload: Binding<[Message]?>?

init(
type: ActionOriginType,
Expand All @@ -57,7 +58,8 @@ public struct ActionOrigin {
nearestReportJunkMessageActionsPanel: Binding<[Message]?>? = nil,
nearestReportedForPhishingMessageAlert: Binding<Message?>? = nil,
nearestReportedForDisplayProblemMessageAlert: Binding<Message?>? = nil,
nearestShareMailLinkPanel: Binding<ShareMailLinkResult?>? = nil
nearestShareMailLinkPanel: Binding<ShareMailLinkResult?>? = nil,
messagesToDownload: Binding<[Message]?>? = nil
) {
self.type = type
frozenFolder = folder?.freezeIfNeeded()
Expand All @@ -70,6 +72,7 @@ public struct ActionOrigin {
self.nearestReportedForPhishingMessageAlert = nearestReportedForPhishingMessageAlert
self.nearestReportedForDisplayProblemMessageAlert = nearestReportedForDisplayProblemMessageAlert
self.nearestShareMailLinkPanel = nearestShareMailLinkPanel
self.messagesToDownload = messagesToDownload
}

public static func toolbar(originFolder: Folder? = nil,
Expand All @@ -86,7 +89,8 @@ public struct ActionOrigin {
nearestReportJunkMessageActionsPanel: Binding<[Message]?>? = nil,
nearestReportedForPhishingMessageAlert: Binding<Message?>? = nil,
nearestReportedForDisplayProblemMessageAlert: Binding<Message?>? = nil,
nearestShareMailLinkPanel: Binding<ShareMailLinkResult?>? = nil) -> ActionOrigin {
nearestShareMailLinkPanel: Binding<ShareMailLinkResult?>? = nil,
messagesToDownload: Binding<[Message]?>? = nil) -> ActionOrigin {
return ActionOrigin(
type: .floatingPanel(source: source),
folder: originFolder,
Expand All @@ -97,7 +101,8 @@ public struct ActionOrigin {
nearestReportJunkMessageActionsPanel: nearestReportJunkMessageActionsPanel,
nearestReportedForPhishingMessageAlert: nearestReportedForPhishingMessageAlert,
nearestReportedForDisplayProblemMessageAlert: nearestReportedForDisplayProblemMessageAlert,
nearestShareMailLinkPanel: nearestShareMailLinkPanel
nearestShareMailLinkPanel: nearestShareMailLinkPanel,
messagesToDownload: messagesToDownload
)
}

Expand Down
16 changes: 3 additions & 13 deletions MailCore/Cache/Actions/ActionsManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,6 @@ public class ActionsManager: ObservableObject {
snackbarPresenter.show(message: MailResourcesStrings.Localizable.snackbarSenderBlacklisted(1))
case .blockList:
Task { @MainActor in

let uniqueRecipient = self.getUniqueRecipients(reportedMessages: messages)
if uniqueRecipient.count > 1 {
origin.nearestBlockSendersList?.wrappedValue = BlockRecipientState(recipientsToMessage: uniqueRecipient)
Expand All @@ -199,18 +198,9 @@ public class ActionsManager: ObservableObject {
)
}
}
case .saveMailInkDrive:
guard !platformDetector.isMac else {
return
}
Task { @MainActor in
do {
let fileURL = try await mailboxManager.apiFetcher.download(message: messages.first!)
try DeeplinkService().shareFileToKdrive(fileURL)
} catch {
SentrySDK.capture(error: error)
}
}
case .saveThreadInkDrive:
guard !platformDetector.isMac else { return }
origin.messagesToDownload?.wrappedValue = messages
case .shareMailLink:
guard let message = messagesWithDuplicates.first else { return }
let result = try await mailboxManager.apiFetcher.shareMailLink(message: message)
Expand Down
9 changes: 9 additions & 0 deletions MailResources/Localizable/de.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,15 @@
/* loco:66cdadf9bc780d2bbd033462 */
"deleteAccountWarning" = "Die Löschung Ihres Kontos ist endgültig. Sie werden nicht in der Lage sein, Ihr Konto zu reaktivieren.";

/* loco:672e3534c133d192fb0acc92 */
"disabledFeatureFlagDescription" = "In Kürze können Sie die Beschränkungen Ihrer ik.me-E-Mail-Adresse entsperren\n(Speicherlimit, benutzerdefinierter Zeitplan, Senden und mehr…)";

/* loco:672e34e12d859bafeb03be32 */
"disabledFeatureFlagTitle" = "Benötigen Sie mehr Speicherplatz und Funktionen?";

/* loco:6777dbf1dc5a5d9dbf09db92 */
"downloadAlertDescription" = "Der Download kann einige Zeit dauern.";

/* loco:627e3fa5774ef45bfd4d3b92 */
"draftFolder" = "Entwürfe";

Expand Down
9 changes: 9 additions & 0 deletions MailResources/Localizable/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,15 @@
/* loco:66cdadf9bc780d2bbd033462 */
"deleteAccountWarning" = "The deletion of your account will be final. You will not be able to reactivate your account.";

/* loco:672e3534c133d192fb0acc92 */
"disabledFeatureFlagDescription" = "You will soon be able to unlock the limits of your ik.me email address\n(storage limit, custom schedule, sending and more…)";

/* loco:672e34e12d859bafeb03be32 */
"disabledFeatureFlagTitle" = "Need more storage and features?";

/* loco:6777dbf1dc5a5d9dbf09db92 */
"downloadAlertDescription" = "The download may take some time.";

/* loco:627e3fa5774ef45bfd4d3b92 */
"draftFolder" = "Drafts";

Expand Down
9 changes: 9 additions & 0 deletions MailResources/Localizable/es.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,15 @@
/* loco:66cdadf9bc780d2bbd033462 */
"deleteAccountWarning" = "La eliminación de su cuenta será definitiva. No podrá reactivar su cuenta.";

/* loco:672e3534c133d192fb0acc92 */
"disabledFeatureFlagDescription" = "Pronto podrás desbloquear los límites de tu dirección de correo electrónico ik.me\n(límite de almacenamiento, programación personalizada, envío y más…)";

/* loco:672e34e12d859bafeb03be32 */
"disabledFeatureFlagTitle" = "¿Necesita más almacenamiento y funciones?";

/* loco:6777dbf1dc5a5d9dbf09db92 */
"downloadAlertDescription" = "La descarga puede tardar algún tiempo.";

/* loco:627e3fa5774ef45bfd4d3b92 */
"draftFolder" = "Borradores";

Expand Down
9 changes: 9 additions & 0 deletions MailResources/Localizable/fr.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,15 @@
/* loco:66cdadf9bc780d2bbd033462 */
"deleteAccountWarning" = "La suppression de votre compte sera définitive. Vous ne pourrez pas réactiver votre compte.";

/* loco:672e3534c133d192fb0acc92 */
"disabledFeatureFlagDescription" = "Vous pourrez prochainement débloquer les limites de votre adresse mail ik.me\n(limite de stockage, horaire personnalisé, envoi et plus encore…)";

/* loco:672e34e12d859bafeb03be32 */
"disabledFeatureFlagTitle" = "Besoin de plus de stockage et de fonctionnalités ?";

/* loco:6777dbf1dc5a5d9dbf09db92 */
"downloadAlertDescription" = "Le téléchargement peut prendre un certain temps.";

/* loco:627e3fa5774ef45bfd4d3b92 */
"draftFolder" = "Brouillons";

Expand Down
9 changes: 9 additions & 0 deletions MailResources/Localizable/it.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,15 @@
/* loco:66cdadf9bc780d2bbd033462 */
"deleteAccountWarning" = "La cancellazione dell’account sarà definitiva. Non sarà possibile riattivare l’account.";

/* loco:672e3534c133d192fb0acc92 */
"disabledFeatureFlagDescription" = "Presto sarai in grado di sbloccare i limiti del tuo indirizzo email ik.me\n(limite di archiviazione, pianificazione personalizzata, invio e altro…)";

/* loco:672e34e12d859bafeb03be32 */
"disabledFeatureFlagTitle" = "Hai bisogno di più spazio di archiviazione e funzionalità?";

/* loco:6777dbf1dc5a5d9dbf09db92 */
"downloadAlertDescription" = "Il download potrebbe richiedere del tempo.";

/* loco:627e3fa5774ef45bfd4d3b92 */
"draftFolder" = "Bozze";

Expand Down

0 comments on commit 76c1e0c

Please sign in to comment.