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

feat: Save threads in kdrive #1585

Merged
merged 11 commits into from
Jan 9, 2025
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
Loading