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

fix: Folder origin for actions #944

Merged
merged 10 commits into from
Aug 28, 2023
16 changes: 5 additions & 11 deletions Mail/Components/ActionsPanelButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,18 @@ import MailResources
import SwiftUI

struct ActionsPanelButton<Content: View>: View {
@State private var messages: [Message]?
@State private var actionMessages: [Message]?

var message: Message?
var threads: [Thread]?
let messages: [Message]
let originFolder: Folder?
@ViewBuilder var label: () -> Content

var body: some View {
Button {
if let message {
messages = [message]
} else if let threads {
messages = threads.flatMap(\.messages)
} else {
DDLogWarn("MoreButton has no action target, did you forget to set message or threads ?")
}
actionMessages = messages
} label: {
label()
}
.actionsPanel(messages: $messages)
.actionsPanel(messages: $actionMessages, originFolder: originFolder)
}
}
13 changes: 10 additions & 3 deletions Mail/Views/Bottom sheets/Actions/ActionsPanelViewModifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,13 @@ import MailCore
import SwiftUI

extension View {
func actionsPanel(messages: Binding<[Message]?>, completionHandler: (() -> Void)? = nil) -> some View {
return modifier(ActionsPanelViewModifier(messages: messages, completionHandler: completionHandler))
func actionsPanel(messages: Binding<[Message]?>, originFolder: Folder?,
completionHandler: (() -> Void)? = nil) -> some View {
return modifier(ActionsPanelViewModifier(
messages: messages,
originFolder: originFolder,
completionHandler: completionHandler
))
}
}

Expand All @@ -35,11 +40,13 @@ struct ActionsPanelViewModifier: ViewModifier {
@State private var messagesToMove: [Message]?

@Binding var messages: [Message]?
let originFolder: Folder?

var completionHandler: (() -> Void)?

private var origin: ActionOrigin {
.floatingPanel(
originFolder: originFolder?.freezeIfNeeded(),
nearestMessagesToMoveSheet: $messagesToMove,
nearestReportJunkMessageActionsPanel: $reportForJunkMessage,
nearestReportedForPhishingMessageAlert: $reportedForPhishingMessage,
Expand All @@ -52,7 +59,7 @@ struct ActionsPanelViewModifier: ViewModifier {
ActionsView(mailboxManager: mailboxManager, target: messages, origin: origin, completionHandler: completionHandler)
}
.sheet(item: $messagesToMove) { messages in
MoveEmailView(movedMessages: messages)
MoveEmailView(movedMessages: messages, originFolder: originFolder)
.sheetViewStyle()
}
.floatingPanel(item: $reportForJunkMessage) { reportForJunkMessage in
Expand Down
4 changes: 2 additions & 2 deletions Mail/Views/Bottom sheets/Actions/ActionsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ struct ActionsView: View {
origin: ActionOrigin,
completionHandler: (() -> Void)? = nil) {
let userIsStaff = mailboxManager.account.user.isStaff ?? false
let actions = Action.actionsForMessages(messages, userIsStaff: userIsStaff)
let actions = Action.actionsForMessages(messages, originFolder: origin.folder, userIsStaff: userIsStaff)
quickActions = actions.quickActions
listActions = actions.listActions

Expand Down Expand Up @@ -83,7 +83,7 @@ struct ActionsView_Previews: PreviewProvider {
ActionsView(
mailboxManager: PreviewHelper.sampleMailboxManager,
target: PreviewHelper.sampleThread.messages.toArray(),
origin: .toolbar
origin: .toolbar(originFolder: nil)
)
.accentColor(AccentColor.pink.primary.swiftUIColor)
}
Expand Down
5 changes: 3 additions & 2 deletions Mail/Views/Thread List/ThreadListModifiers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ struct ThreadListToolbar: ViewModifier {
) {
let allMessages = multipleSelectionViewModel.selectedItems.flatMap(\.messages)
multipleSelectionViewModel.isEnabled = false
let originFolder = viewModel.folder.freezeIfNeeded()
Task {
matomo.trackBulkEvent(
eventWithCategory: .threadActions,
Expand All @@ -155,7 +156,7 @@ struct ThreadListToolbar: ViewModifier {
try await actionsManager.performAction(
target: allMessages,
action: action,
origin: .multipleSelection(nearestFlushAlert: $flushAlert)
origin: .multipleSelection(originFolder: originFolder, nearestFlushAlert: $flushAlert)
)
}
}
Expand All @@ -171,7 +172,7 @@ struct ThreadListToolbar: ViewModifier {
}
.disabled(multipleSelectionViewModel.selectedItems.isEmpty)
}
.actionsPanel(messages: $multipleSelectedMessages) {
.actionsPanel(messages: $multipleSelectedMessages, originFolder: viewModel.folder) {
multipleSelectionViewModel.isEnabled = false
}
.navigationTitle(
Expand Down
3 changes: 2 additions & 1 deletion Mail/Views/Thread List/ThreadListSwipeAction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ private struct SwipeActionView: View {
target: thread.messages.toArray(),
action: action,
origin: .swipe(
originFolder: thread.folder,
nearestMessagesActionsPanel: $actionPanelMessages,
nearestMessagesToMoveSheet: $moveSheetMessages
)
Expand Down Expand Up @@ -85,7 +86,7 @@ struct ThreadListSwipeActions: ViewModifier {
edgeActions([swipeFullTrailing, swipeTrailing])
}
}
.actionsPanel(messages: $actionPanelMessages)
.actionsPanel(messages: $actionPanelMessages, originFolder: thread.folder)
}

@MainActor @ViewBuilder
Expand Down
2 changes: 1 addition & 1 deletion Mail/Views/Thread/MessageHeaderSummaryView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ struct MessageHeaderSummaryView: View {
.adaptivePanel(item: $replyOrReplyAllMessage) { message in
ReplyActionsView(message: message)
}
ActionsPanelButton(message: message) {
ActionsPanelButton(messages: [message], originFolder: message.folder) {
MailResourcesAsset.plusActions.swiftUIImage
.resizable()
.scaledToFit()
Expand Down
8 changes: 5 additions & 3 deletions Mail/Views/Thread/MoveEmailView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ struct MoveEmailView: View {
@State private var searchFilter = ""

let movedMessages: [Message]
let originFolder: Folder?

var body: some View {
ScrollView {
Expand Down Expand Up @@ -84,15 +85,16 @@ struct MoveEmailView: View {
}

private func move(to folder: Folder) {
let frozenOriginFolder = originFolder?.freezeIfNeeded()
Task {
try await actionsManager.performMove(messages: movedMessages, to: folder)
try await actionsManager.performMove(messages: movedMessages, from: frozenOriginFolder, to: folder)
}
dismissModal()
}

private func listOfFolders(nestableFolders: [NestableFolder]) -> some View {
ForEach(nestableFolders) { nestableFolder in
FolderCell(folder: nestableFolder, currentFolderId: movedMessages.first?.folderId) { folder in
FolderCell(folder: nestableFolder, currentFolderId: originFolder?.id) { folder in
move(to: folder)
}
}
Expand All @@ -101,7 +103,7 @@ struct MoveEmailView: View {

struct MoveMessageView_Previews: PreviewProvider {
static var previews: some View {
MoveEmailView(movedMessages: [PreviewHelper.sampleMessage])
MoveEmailView(movedMessages: [PreviewHelper.sampleMessage], originFolder: nil)
.environmentObject(PreviewHelper.sampleMailboxManager)
}
}
20 changes: 16 additions & 4 deletions Mail/Views/Thread/ThreadView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,12 @@ struct ThreadView: View {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
let messages = thread.messages.freeze().toArray()
let originFolder = thread.folder?.freezeIfNeeded()
Task {
try await actionsManager.performAction(
target: messages,
action: thread.flagged ? .unstar : .star,
origin: .toolbar
origin: .toolbar(originFolder: originFolder)
)
}
} label: {
Expand All @@ -132,7 +133,7 @@ struct ThreadView: View {
}
Spacer()
}
ActionsPanelButton(threads: [thread]) {
ActionsPanelButton(messages: thread.messages.toArray(), originFolder: thread.folder) {
ToolbarButtonLabel(text: MailResourcesStrings.Localizable.buttonMore,
icon: MailResourcesAsset.plusActions.swiftUIImage)
}
Expand All @@ -151,7 +152,13 @@ struct ThreadView: View {

private func markThreadAsReadIfNeeded(thread: Thread) async {
guard thread.hasUnseenMessages else { return }
try? await actionsManager.performAction(target: thread.messages.toArray(), action: .markAsRead, origin: .toolbar)

let originFolder = thread.folder?.freezeIfNeeded()
try? await actionsManager.performAction(
target: thread.messages.toArray(),
action: .markAsRead,
origin: .toolbar(originFolder: originFolder)
)
}

private func didTap(action: Action) {
Expand All @@ -166,8 +173,13 @@ struct ThreadView: View {
return
}

let originFolder = thread.folder?.freezeIfNeeded()
Task {
try await actionsManager.performAction(target: messages, action: action, origin: .toolbar)
try await actionsManager.performAction(
target: messages,
action: action,
origin: .toolbar(originFolder: originFolder)
)
if action == .archive || action == .delete {
dismiss()
}
Expand Down
23 changes: 12 additions & 11 deletions MailCore/Cache/Actions/Action+List.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,12 @@ extension Action: CaseIterable {
return (Action.quickActions, tempListActions.compactMap { $0 })
}

private static func actionsForMessagesInDifferentThreads(_ messages: [Message])
private static func actionsForMessagesInDifferentThreads(_ messages: [Message], originFolder: Folder?)
-> (quickActions: [Action], listActions: [Action]) {
let unread = messages.allSatisfy(\.seen)
let quickActions: [Action] = [.openMovePanel, unread ? .markAsUnread : .markAsRead, .archive, .delete]
let quickActions: [Action] = [.openMovePanel, unread ? .markAsUnread : .markAsRead, .archive, .delete]

let spam = messages.allSatisfy { $0.folder?.role == .spam }
let spam = originFolder?.role == .spam
let star = messages.allSatisfy(\.flagged)

let listActions: [Action] = [
Expand All @@ -92,34 +92,35 @@ extension Action: CaseIterable {
return (quickActions, listActions)
}

private static func actionsForMessagesInSameThreads(_ messages: [Message])
private static func actionsForMessagesInSameThreads(_ messages: [Message], originFolder: Folder?)
-> (quickActions: [Action], listActions: [Action]) {
let archive = messages.first?.folder?.role != .archive
let archive = originFolder?.role != .archive
let unread = messages.allSatisfy(\.seen)
let star = messages.allSatisfy(\.flagged)
let showUnstar = messages.contains { $0.flagged }

let spam = messages.first?.folder?.role == .spam
let spam = originFolder?.role == .spam
let spamAction: Action? = spam ? .nonSpam : .spam

let tempListActions: [Action?] = [
.openMovePanel,
spamAction,
unread ? .markAsUnread : .markAsRead,
archive ? .archive : .moveToInbox,
star ? .unstar : .star
showUnstar ? .unstar : .star
]

return (Action.quickActions, tempListActions.compactMap { $0 })
}

public static func actionsForMessages(_ messages: [Message],
originFolder: Folder?,
userIsStaff: Bool) -> (quickActions: [Action], listActions: [Action]) {
if messages.count == 1, let message = messages.first {
return actionsForMessage(message, userIsStaff: userIsStaff)
} else if Set(messages.compactMap(\.originalThread?.id)).count > 1 {
return actionsForMessagesInDifferentThreads(messages)
} else if messages.uniqueThreadsInFolder(originFolder).count > 1 {
return actionsForMessagesInDifferentThreads(messages, originFolder: originFolder)
} else {
return actionsForMessagesInSameThreads(messages)
return actionsForMessagesInSameThreads(messages, originFolder: originFolder)
}
}
}
Expand Down
27 changes: 20 additions & 7 deletions MailCore/Cache/Actions/ActionOrigin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,34 +27,47 @@ public struct ActionOrigin {
case multipleSelection
}

private(set) var type: ActionOriginType
public private(set) var type: ActionOriginType
public private(set) var folder: Folder?
private(set) var nearestMessagesActionsPanel: Binding<[Message]?>?
private(set) var nearestFlushAlert: Binding<FlushAlertState?>?
private(set) var nearestMessagesToMoveSheet: Binding<[Message]?>?
private(set) var nearestReportJunkMessageActionsPanel: Binding<Message?>?
private(set) var nearestReportedForPhishingMessageAlert: Binding<Message?>?
private(set) var nearestReportedForDisplayProblemMessageAlert: Binding<Message?>?

public static let toolbar = ActionOrigin(type: .toolbar)
public static func toolbar(originFolder: Folder? = nil) -> ActionOrigin {
return ActionOrigin(type: .toolbar, folder: originFolder)
}

public static func floatingPanel(nearestMessagesToMoveSheet: Binding<[Message]?>? = nil,
public static func floatingPanel(originFolder: Folder? = nil,
nearestMessagesToMoveSheet: Binding<[Message]?>? = nil,
nearestReportJunkMessageActionsPanel: Binding<Message?>? = nil,
nearestReportedForPhishingMessageAlert: Binding<Message?>? = nil,
nearestReportedForDisplayProblemMessageAlert: Binding<Message?>? = nil) -> ActionOrigin {
return ActionOrigin(
type: .floatingPanel,
folder: originFolder,
nearestMessagesToMoveSheet: nearestMessagesToMoveSheet,
nearestReportJunkMessageActionsPanel: nearestReportJunkMessageActionsPanel,
nearestReportedForPhishingMessageAlert: nearestReportedForPhishingMessageAlert,
nearestReportedForDisplayProblemMessageAlert: nearestReportedForDisplayProblemMessageAlert
)
}

public static func multipleSelection(nearestFlushAlert: Binding<FlushAlertState?>? = nil) -> ActionOrigin {
return ActionOrigin(type: .multipleSelection, nearestFlushAlert: nearestFlushAlert)
public static func multipleSelection(originFolder: Folder? = nil,
nearestFlushAlert: Binding<FlushAlertState?>? = nil) -> ActionOrigin {
return ActionOrigin(type: .multipleSelection, folder: originFolder, nearestFlushAlert: nearestFlushAlert)
}

public static func swipe(nearestMessagesActionsPanel: Binding<[Message]?>? = nil, nearestMessagesToMoveSheet: Binding<[Message]?>? = nil) -> ActionOrigin {
return ActionOrigin(type: .swipe, nearestMessagesActionsPanel: nearestMessagesActionsPanel, nearestMessagesToMoveSheet: nearestMessagesToMoveSheet)
public static func swipe(
originFolder: Folder? = nil,
nearestMessagesActionsPanel: Binding<[Message]?>? = nil,
nearestMessagesToMoveSheet: Binding<[Message]?>? = nil
) -> ActionOrigin {
return ActionOrigin(type: .swipe,
folder: originFolder,
nearestMessagesActionsPanel: nearestMessagesActionsPanel,
nearestMessagesToMoveSheet: nearestMessagesToMoveSheet)
}
}
Loading
Loading