diff --git a/Mail/Views/New Message/ComposeMessageHeaderView.swift b/Mail/Views/New Message/ComposeMessageHeaderView.swift index eec0ea39a..77b1ed1f9 100644 --- a/Mail/Views/New Message/ComposeMessageHeaderView.swift +++ b/Mail/Views/New Message/ComposeMessageHeaderView.swift @@ -36,6 +36,7 @@ struct ComposeMessageHeaderView: View { VStack(spacing: 0) { ComposeMessageSenderMenu( currentSignature: $currentSignature, + mailboxManager: mailboxManager, autocompletionType: autocompletionType, type: .from, text: mailboxManager.mailbox.email diff --git a/Mail/Views/New Message/ComposeMessageIntentView.swift b/Mail/Views/New Message/ComposeMessageIntentView.swift index 9dd9a3e5c..31d36cc46 100644 --- a/Mail/Views/New Message/ComposeMessageIntentView.swift +++ b/Mail/Views/New Message/ComposeMessageIntentView.swift @@ -25,50 +25,55 @@ import SwiftUI struct ComposeMessageIntentView: View, IntentViewable { typealias Intent = ResolvedIntent + struct ResolvedIntent { + let mailboxManager: MailboxManager + let draft: Draft + let messageReply: MessageReply? + } + @LazyInjectService private var accountManager: AccountManager @LazyInjectService private var snackbarPresenter: SnackBarPresentable @Environment(\.dismiss) private var dismiss + @State private var composeMessageIntent: ComposeMessageIntent let resolvedIntent = State() + var textAttachments: [TextAttachable] + var attachments: [Attachable] - struct ResolvedIntent { - let mailboxManager: MailboxManager - let draft: Draft - let messageReply: MessageReply? + init(composeMessageIntent: ComposeMessageIntent, textAttachments: [TextAttachable] = [], attachments: [Attachable] = []) { + _composeMessageIntent = State(wrappedValue: composeMessageIntent) + self.textAttachments = textAttachments + self.attachments = attachments } - let composeMessageIntent: ComposeMessageIntent - var textAttachments: [TextAttachable] = [] - var attachments: [Attachable] = [] - var body: some View { - NBNavigationStack { - if let resolvedIntent = resolvedIntent.wrappedValue { - ComposeMessageView( - draft: resolvedIntent.draft, - mailboxManager: resolvedIntent.mailboxManager, - messageReply: resolvedIntent.messageReply, - attachments: attachments, - textAttachments: textAttachments - ) - .environmentObject(resolvedIntent.mailboxManager) - } else { - ProgressView() - .progressViewStyle(.circular) - .task { - await initFromIntent() - } + if composeMessageIntent.shouldSelectMailbox { + CurrentComposeMailboxView(composeMessageIntent: $composeMessageIntent) + } else { + NavigationView { + if let resolvedIntent = resolvedIntent.wrappedValue { + ComposeMessageView( + draft: resolvedIntent.draft, + mailboxManager: resolvedIntent.mailboxManager, + messageReply: resolvedIntent.messageReply, + attachments: attachments + ) + .environmentObject(resolvedIntent.mailboxManager) + } else { + ProgressView() + .progressViewStyle(.circular) + .task { + await initFromIntent() + } + } } } - .interactiveDismissDisabled() } func initFromIntent() async { - guard let mailboxManager = accountManager.getMailboxManager( - for: composeMessageIntent.mailboxId, - userId: composeMessageIntent.userId - ) else { + guard let mailboxId = composeMessageIntent.mailboxId, let userId = composeMessageIntent.userId, + let mailboxManager = accountManager.getMailboxManager(for: mailboxId, userId: userId) else { dismiss() snackbarPresenter.show(message: MailError.unknownError.errorDescription ?? "") return @@ -98,6 +103,10 @@ struct ComposeMessageIntentView: View, IntentViewable { } } + if composeMessageIntent.isFromOutsideOfApp { + try? await mailboxManager.refreshAllSignatures() + } + if let draftToWrite { let draftLocalUUID = draftToWrite.localUUID writeDraftToRealm(mailboxManager.getRealm(), draft: draftToWrite) @@ -133,6 +142,6 @@ struct ComposeMessageIntentView: View, IntentViewable { #Preview { ComposeMessageIntentView( - composeMessageIntent: .new(originMailboxManager: PreviewHelper.sampleMailboxManager) + composeMessageIntent: .new(originMailboxManager: PreviewHelper.sampleMailboxManager), textAttachments: [], attachments: [] ) } diff --git a/Mail/Views/New Message/ComposeMessageView.swift b/Mail/Views/New Message/ComposeMessageView.swift index 02909d6b4..24f0206d3 100644 --- a/Mail/Views/New Message/ComposeMessageView.swift +++ b/Mail/Views/New Message/ComposeMessageView.swift @@ -171,6 +171,24 @@ struct ComposeMessageView: View { } } } + .navigationTitle(MailResourcesStrings.Localizable.buttonNewMessage) + .navigationBarTitleDisplayMode(.inline) + .interactiveDismissDisabled() + .navigationViewStyle(.stack) + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + if !platformDetector.isMac { + CloseButton(dismissHandler: didTouchDismiss) + } + } + + ToolbarItem(placement: .navigationBarTrailing) { + Button(action: didTouchSend) { + Label(MailResourcesStrings.Localizable.send, image: MailResourcesAsset.send.name) + } + .disabled(isSendButtonDisabled) + } + } .background(MailResourcesAsset.backgroundColor.swiftUIColor) .overlay { if isLoadingContent { @@ -198,22 +216,6 @@ struct ComposeMessageView: View { let rectTop = CGRect(x: 0, y: 0, width: 1, height: 1) scrollView?.scrollRectToVisible(rectTop, animated: true) } - .navigationTitle(MailResourcesStrings.Localizable.buttonNewMessage) - .navigationBarTitleDisplayMode(.inline) - .toolbar { - ToolbarItem(placement: .navigationBarLeading) { - if !platformDetector.isMac { - CloseButton(dismissHandler: didTouchDismiss) - } - } - - ToolbarItem(placement: .navigationBarTrailing) { - Button(action: didTouchSend) { - Label(MailResourcesStrings.Localizable.send, image: MailResourcesAsset.send.name) - } - .disabled(isSendButtonDisabled) - } - } .safeAreaInset(edge: .bottom) { ExternalTagBottomView(externalTag: draft.displayExternalTag(mailboxManager: mailboxManager)) } diff --git a/Mail/Views/New Message/Header Cells/ComposeMessageSenderMenu.swift b/Mail/Views/New Message/Header Cells/ComposeMessageSenderMenu.swift index 3dafb8ba9..8def7542f 100644 --- a/Mail/Views/New Message/Header Cells/ComposeMessageSenderMenu.swift +++ b/Mail/Views/New Message/Header Cells/ComposeMessageSenderMenu.swift @@ -25,18 +25,7 @@ import SwiftUI struct ComposeMessageSenderMenu: View { @EnvironmentObject private var draftContentManager: DraftContentManager - /// Note: - /// ObservedResults will invoke a `default.realm` store, and break (no migration block) while a migration is needed in share - /// extension. - /// - /// Therefore, I have to pass the correct realm configuration for `Signature.self`, so it can function correctly. - @ObservedResults(Signature.self, configuration: { - @InjectService var accountManager: AccountManager - guard let currentMailboxManager = accountManager.currentMailboxManager else { - return nil - } - return currentMailboxManager.realmConfiguration - }()) private var signatures + @ObservedResults(Signature.self) private var signatures @Binding var currentSignature: Signature? @@ -48,6 +37,20 @@ struct ComposeMessageSenderMenu: View { signatures.count > 1 } + init( + currentSignature: Binding, + mailboxManager: MailboxManager, + autocompletionType: ComposeViewFieldType?, + type: ComposeViewFieldType, + text: String + ) { + _currentSignature = currentSignature + _signatures = ObservedResults(Signature.self, configuration: mailboxManager.realmConfiguration) + self.autocompletionType = autocompletionType + self.type = type + self.text = text + } + var body: some View { if autocompletionType == nil { VStack(spacing: 0) { @@ -83,6 +86,11 @@ struct ComposeMessageSenderMenu: View { } #Preview { - ComposeMessageSenderMenu(currentSignature: .constant(nil), autocompletionType: nil, type: .from, text: "email@email.com") - .environmentObject(PreviewHelper.sampleMailboxManager) + ComposeMessageSenderMenu( + currentSignature: .constant(nil), + mailboxManager: PreviewHelper.sampleMailboxManager, + autocompletionType: nil, + type: .from, + text: "email@email.com" + ) } diff --git a/Mail/Views/New Message/Select Mailbox/AccountMailboxCell.swift b/Mail/Views/New Message/Select Mailbox/AccountMailboxCell.swift new file mode 100644 index 000000000..21c6cda9b --- /dev/null +++ b/Mail/Views/New Message/Select Mailbox/AccountMailboxCell.swift @@ -0,0 +1,48 @@ +/* + 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 . + */ + +import MailCore +import MailResources +import SwiftUI + +struct AccountMailboxCell: View { + let mailbox: Mailbox + var selectedMailbox: Mailbox? + let selectMailbox: (Mailbox) -> Void + + var body: some View { + Button { + withAnimation { + selectMailbox(mailbox) + } + } label: { + Label { + Text(mailbox.email) + } icon: { + if selectedMailbox?.mailboxId == mailbox.mailboxId { + MailResourcesAsset.check.swiftUIImage + } + } + .accessibilityHint(MailResourcesStrings.Localizable.composeMailboxSelectionTitle) + } + } +} + +#Preview { + AccountMailboxCell(mailbox: PreviewHelper.sampleMailbox) { _ in } +} diff --git a/Mail/Views/New Message/Select Mailbox/AccountMailboxesListView.swift b/Mail/Views/New Message/Select Mailbox/AccountMailboxesListView.swift new file mode 100644 index 000000000..3897a11aa --- /dev/null +++ b/Mail/Views/New Message/Select Mailbox/AccountMailboxesListView.swift @@ -0,0 +1,60 @@ +/* + 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 . + */ + +import InfomaniakCore +import InfomaniakDI +import MailCore +import MailResources +import SwiftUI + +struct AccountMailboxesListView: View { + @LazyInjectService private var accountManager: AccountManager + + @EnvironmentObject private var mailboxManager: MailboxManager + + let account: Account + var selectedMailbox: Mailbox? + + let selectMailbox: (Mailbox) -> Void + + private var currentMailbox: Mailbox? { + return accountManager.currentMailboxManager?.mailbox + } + + var body: some View { + Menu { + ForEachMailboxView(userId: account.userId) { mailbox in + AccountMailboxCell(mailbox: mailbox, selectedMailbox: selectedMailbox, selectMailbox: selectMailbox) + } + } label: { + AccountHeaderCell( + account: account, + mailboxManager: mailboxManager, + isSelected: .constant(false), + type: .selectComposeMailbox + ) + } + } +} + +#Preview { + AccountMailboxesListView( + account: PreviewHelper.sampleAccount, + selectedMailbox: PreviewHelper.sampleMailbox + ) { _ in } +} diff --git a/Mail/Views/New Message/Select Mailbox/CurrentComposeMailboxView.swift b/Mail/Views/New Message/Select Mailbox/CurrentComposeMailboxView.swift new file mode 100644 index 000000000..6ad24e289 --- /dev/null +++ b/Mail/Views/New Message/Select Mailbox/CurrentComposeMailboxView.swift @@ -0,0 +1,107 @@ +/* + 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 . + */ + +import InfomaniakCore +import InfomaniakCoreUI +import InfomaniakDI +import MailCore +import MailResources +import NavigationBackport +import SwiftUI + +struct CurrentComposeMailboxView: View { + @LazyInjectService private var accountManager: AccountManager + @LazyInjectService private var matomo: MatomoUtils + @LazyInjectService private var platformDetector: PlatformDetectable + + @AppStorage(UserDefaults.shared.key(.accentColor)) private var accentColor = DefaultPreferences.accentColor + + @Environment(\.dismiss) private var dismiss + @Environment(\.dismissModal) private var dismissModal + + @StateObject private var viewModel: SelectComposeMailboxViewModel + + @Binding var composeMessageIntent: ComposeMessageIntent + + init(composeMessageIntent: Binding) { + _composeMessageIntent = composeMessageIntent + _viewModel = StateObject(wrappedValue: SelectComposeMailboxViewModel(composeMessageIntent: composeMessageIntent)) + } + + var body: some View { + NavigationView { + VStack(spacing: 0) { + accentColor.mailboxImage.swiftUIImage + .padding(.bottom, value: .regular) + + Text(MailResourcesStrings.Localizable.composeMailboxCurrentTitle) + .textStyle(.header2) + .multilineTextAlignment(.center) + .padding(.bottom, value: .medium) + + if let defaultMailbox = viewModel.defaultMailbox, + let account = accountManager.account(for: defaultMailbox.userId) { + SelectedMailboxView(account: account, selectedMailbox: defaultMailbox) + .frame(maxHeight: .infinity, alignment: .top) + } + + VStack(spacing: UIPadding.regular) { + Button(MailResourcesStrings.Localizable.buttonContinue) { + viewModel.validateMailboxChoice(viewModel.defaultMailbox) + } + .buttonStyle(.ikPlain) + + NavigationLink(destination: SelectComposeMailboxView( + composeMessageIntent: $composeMessageIntent, + viewModel: viewModel + )) { + Text(MailResourcesStrings.Localizable.buttonSendWithDifferentAddress) + .textStyle(.bodyMediumAccent) + } + .buttonStyle(.ikLink()) + .padding(.bottom, UIPadding.onBoardingBottomButtons) + } + .ikButtonFullWidth(true) + .controlSize(.large) + .padding(.horizontal, value: .small) + } + .padding(.horizontal, value: .regular) + .mailboxCellStyle(.account) + .onAppear(perform: viewModel.initDefaultAccountAndMailbox) + .backButtonDisplayMode(.minimal) + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + if !platformDetector.isMac { + CloseButton(dismissHandler: dismissMessageView) + } + } + } + .matomoView(view: [MatomoUtils.View.bottomSheet.displayName, "CurrentComposeMailboxView"]) + } + } + + private func dismissMessageView() { + dismissModal() + dismiss() + } +} + +#Preview { + CurrentComposeMailboxView(composeMessageIntent: .constant(.new())) +} diff --git a/Mail/Views/New Message/Select Mailbox/SelectComposeMailboxView.swift b/Mail/Views/New Message/Select Mailbox/SelectComposeMailboxView.swift new file mode 100644 index 000000000..861e80afe --- /dev/null +++ b/Mail/Views/New Message/Select Mailbox/SelectComposeMailboxView.swift @@ -0,0 +1,89 @@ +/* + Infomaniak Mail - iOS App + Copyright (C) 2022 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 . + */ + +import InfomaniakCore +import InfomaniakCoreUI +import InfomaniakDI +import MailCore +import MailResources +import NavigationBackport +import SwiftUI + +struct SelectComposeMailboxView: View { + @LazyInjectService private var accountManager: AccountManager + @LazyInjectService private var matomo: MatomoUtils + @LazyInjectService private var platformDetector: PlatformDetectable + + @AppStorage(UserDefaults.shared.key(.accentColor)) private var accentColor = DefaultPreferences.accentColor + + @Binding var composeMessageIntent: ComposeMessageIntent + + let viewModel: SelectComposeMailboxViewModel + + var body: some View { + VStack(spacing: 0) { + accentColor.mailboxImage.swiftUIImage + .padding(.bottom, value: .regular) + + Text(MailResourcesStrings.Localizable.composeMailboxSelectionTitle) + .textStyle(.header2) + .multilineTextAlignment(.center) + .padding(.bottom, value: .regular) + + ScrollView { + VStack(spacing: 0) { + ForEach(viewModel.accounts) { account in + AccountMailboxesListView( + account: account, + selectedMailbox: viewModel.selectedMailbox, + selectMailbox: viewModel.selectMailbox + ) + .padding(.top, value: .small) + } + } + } + + if let selectedMailbox = viewModel.selectedMailbox, + let account = accountManager.account(for: selectedMailbox.userId), viewModel.selectionMade { + SelectedMailboxView(account: account, selectedMailbox: selectedMailbox) + .padding(.horizontal, value: .small) + .padding(.bottom, value: .regular) + } + + Button(MailResourcesStrings.Localizable.buttonContinue) { + viewModel.validateMailboxChoice(viewModel.selectedMailbox) + } + .buttonStyle(.ikPlain) + .controlSize(.large) + .ikButtonFullWidth(true) + .padding(.horizontal, value: .small) + .disabled(!viewModel.selectionMade) + } + .padding(.horizontal, value: .regular) + .padding(.bottom, UIPadding.onBoardingBottomButtons) + .mailboxCellStyle(.account) + .matomoView(view: [MatomoUtils.View.bottomSheet.displayName, "SelectComposeMailboxView"]) + } +} + +#Preview { + SelectComposeMailboxView( + composeMessageIntent: .constant(.new()), + viewModel: SelectComposeMailboxViewModel(composeMessageIntent: .constant(.new())) + ) +} diff --git a/Mail/Views/New Message/Select Mailbox/SelectComposeMailboxViewModel.swift b/Mail/Views/New Message/Select Mailbox/SelectComposeMailboxViewModel.swift new file mode 100644 index 000000000..f7976b3cf --- /dev/null +++ b/Mail/Views/New Message/Select Mailbox/SelectComposeMailboxViewModel.swift @@ -0,0 +1,84 @@ +/* + 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 . + */ + +import Foundation +import InfomaniakCore +import InfomaniakDI +import MailCore +import MailResources +import SwiftUI + +final class SelectComposeMailboxViewModel: ObservableObject { + @LazyInjectService private var accountManager: AccountManager + @LazyInjectService private var mailboxInfosManager: MailboxInfosManager + @LazyInjectService private var snackbarPresenter: SnackBarPresentable + + @Published private(set) var selectedMailbox: Mailbox? + @Published private(set) var defaultMailbox: Mailbox? + @Published private(set) var selectionMade = false + + private(set) var composeMessageIntent: Binding + private(set) var accounts = [Account]() + + init(composeMessageIntent: Binding) { + self.composeMessageIntent = composeMessageIntent + + accounts = accountManager.accounts.sorted { lhs, rhs in + if (lhs.userId == accountManager.currentUserId) != (rhs.userId == accountManager.currentUserId) { + return lhs.userId == accountManager.currentUserId + } else { + return lhs.user.displayName < rhs.user.displayName + } + } + } + + func initDefaultAccountAndMailbox() { + defaultMailbox = accountManager.currentMailboxManager?.mailbox + if let defaultMailbox, accountManager.accounts.count == 1 && mailboxInfosManager.getMailboxes().count == 1 { + validateMailboxChoice(defaultMailbox) + } + } + + func selectMailbox(_ mailbox: Mailbox) { + guard mailbox.isAvailable else { + snackbarPresenter.show(message: MailResourcesStrings.Localizable.errorMailboxUnavailable) + return + } + selectedMailbox = mailbox + selectionMade = true + } + + func validateMailboxChoice(_ selectedMailbox: Mailbox?) { + guard let mailbox = selectedMailbox, let mailboxManager = accountManager.getMailboxManager(for: mailbox) else { + snackbarPresenter.show(message: MailResourcesStrings.Localizable.errorUnknown) + return + } + + switch composeMessageIntent.wrappedValue.type { + case .new: + composeMessageIntent.wrappedValue = .new(originMailboxManager: mailboxManager, fromExtension: true) + case .mailTo(let mailToURLComponents): + composeMessageIntent.wrappedValue = .mailTo( + mailToURLComponents: mailToURLComponents, + originMailboxManager: mailboxManager + ) + default: + break + } + } +} diff --git a/Mail/Views/New Message/Select Mailbox/SelectedMailboxView.swift b/Mail/Views/New Message/Select Mailbox/SelectedMailboxView.swift new file mode 100644 index 000000000..7ff767bc8 --- /dev/null +++ b/Mail/Views/New Message/Select Mailbox/SelectedMailboxView.swift @@ -0,0 +1,51 @@ +/* + 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 . + */ + +import InfomaniakCore +import MailCore +import MailResources +import SwiftUI + +struct SelectedMailboxView: View { + @EnvironmentObject private var mailboxManager: MailboxManager + + let account: Account + let selectedMailbox: Mailbox + + var body: some View { + HStack(spacing: UIPadding.small) { + AvatarView(mailboxManager: mailboxManager, contactConfiguration: .user(user: account.user), size: 40) + Text(selectedMailbox.email) + .textStyle(.body) + .frame(maxWidth: .infinity, alignment: .leading) + MailResourcesAsset.checkmarkCircleFill.swiftUIImage + .foregroundStyle(MailResourcesAsset.greenColor) + } + .lineLimit(1) + .padding(.vertical, value: .small) + .padding(.horizontal, value: .regular) + .background( + RoundedRectangle(cornerRadius: 8) + .fill(MailResourcesAsset.textFieldColor.swiftUIColor) + ) + } +} + +#Preview { + SelectedMailboxView(account: PreviewHelper.sampleAccount, selectedMailbox: PreviewHelper.sampleMailbox) +} diff --git a/Mail/Views/SplitView.swift b/Mail/Views/SplitView.swift index cdc8dac12..0d77c30a7 100644 --- a/Mail/Views/SplitView.swift +++ b/Mail/Views/SplitView.swift @@ -265,7 +265,7 @@ struct SplitView: View { guard let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: true) else { return } if Constants.isMailTo(url) { - mainViewState.composeMessageIntent = .mailTo(mailToURLComponents: urlComponents, originMailboxManager: mailboxManager) + mainViewState.composeMessageIntent = .mailTo(mailToURLComponents: urlComponents) } } diff --git a/Mail/Views/Switch User/AccountCellView.swift b/Mail/Views/Switch User/AccountCellView.swift index 46451a30e..e20dcc7a6 100644 --- a/Mail/Views/Switch User/AccountCellView.swift +++ b/Mail/Views/Switch User/AccountCellView.swift @@ -54,16 +54,6 @@ struct AccountCellView: View { selectedUserId = $0 ? account.userId : nil })) } - .padding([.leading, .vertical], value: .small) - .padding(.trailing, value: .regular) - .background { - RoundedRectangle(cornerRadius: 10) - .fill(MailResourcesAsset.backgroundSecondaryColor.swiftUIColor) - } - .overlay { - RoundedRectangle(cornerRadius: 10) - .stroke(MailResourcesAsset.elementsColor.swiftUIColor, lineWidth: 1) - } } } @@ -75,8 +65,14 @@ struct AccountHeaderCell: View { @Binding var isSelected: Bool + enum AccountHeaderCellType { + case switchAccount, selectComposeMailbox + } + + var type = AccountHeaderCellType.switchAccount + var body: some View { - HStack(spacing: UIPadding.small) { + HStack { AvatarView(mailboxManager: mailboxManager, contactConfiguration: .user(user: account.user), size: 40) VStack(alignment: .leading, spacing: 0) { Text(account.user.displayName) @@ -85,14 +81,28 @@ struct AccountHeaderCell: View { .textStyle(.bodySecondary) } .lineLimit(1) - - Spacer(minLength: 0) - - if isSelected { - IKIcon(MailResourcesAsset.check) - .foregroundStyle(.tint) + .frame(maxWidth: .infinity, alignment: .leading) + + switch type { + case .switchAccount: + if isSelected { + IKIcon(MailResourcesAsset.check) + .foregroundStyle(.tint) + } + case .selectComposeMailbox: + ChevronIcon(direction: .down) } } + .padding(.vertical, value: .small) + .padding(.horizontal, value: .regular) + .background { + RoundedRectangle(cornerRadius: 8) + .fill(MailResourcesAsset.backgroundSecondaryColor.swiftUIColor) + } + .overlay { + RoundedRectangle(cornerRadius: 8) + .stroke(MailResourcesAsset.elementsColor.swiftUIColor, lineWidth: 1) + } } } diff --git a/MailCore/Models/ComposeMessageIntent.swift b/MailCore/Models/ComposeMessageIntent.swift index 6deba6ac9..cf738693d 100644 --- a/MailCore/Models/ComposeMessageIntent.swift +++ b/MailCore/Models/ComposeMessageIntent.swift @@ -20,7 +20,7 @@ import Foundation public struct ComposeMessageIntent: Codable, Identifiable, Hashable { public enum IntentType: Codable, Hashable { - case new + case new(fromExtension: Bool) case existing(draftLocalUUID: String) case existingRemote(messageUid: String) case mailTo(mailToURLComponents: URLComponents) @@ -29,22 +29,37 @@ public struct ComposeMessageIntent: Codable, Identifiable, Hashable { } public let id: UUID - public let userId: Int - public let mailboxId: Int + public let userId: Int? + public let mailboxId: Int? public let type: IntentType - init(userId: Int, mailboxId: Int, type: IntentType) { + public var shouldSelectMailbox: Bool { + userId == nil || mailboxId == nil + } + + public var isFromOutsideOfApp: Bool { + switch type { + case .new(let fromExtension) where fromExtension: + return true + case .mailTo(let mailToURLComponents): + return true + default: + return false + } + } + + init(userId: Int?, mailboxId: Int?, type: IntentType) { id = UUID() self.userId = userId self.mailboxId = mailboxId self.type = type } - public static func new(originMailboxManager: MailboxManager) -> ComposeMessageIntent { + public static func new(originMailboxManager: MailboxManager? = nil, fromExtension: Bool = false) -> ComposeMessageIntent { return ComposeMessageIntent( - userId: originMailboxManager.mailbox.userId, - mailboxId: originMailboxManager.mailbox.mailboxId, - type: .new + userId: originMailboxManager?.mailbox.userId, + mailboxId: originMailboxManager?.mailbox.mailboxId, + type: .new(fromExtension: fromExtension) ) } @@ -64,10 +79,11 @@ public struct ComposeMessageIntent: Codable, Identifiable, Hashable { ) } - public static func mailTo(mailToURLComponents: URLComponents, originMailboxManager: MailboxManager) -> ComposeMessageIntent { + public static func mailTo(mailToURLComponents: URLComponents, + originMailboxManager: MailboxManager? = nil) -> ComposeMessageIntent { return ComposeMessageIntent( - userId: originMailboxManager.mailbox.userId, - mailboxId: originMailboxManager.mailbox.mailboxId, + userId: originMailboxManager?.mailbox.userId, + mailboxId: originMailboxManager?.mailbox.mailboxId, type: .mailTo(mailToURLComponents: mailToURLComponents) ) } diff --git a/MailCore/Models/Settings/AccentColor.swift b/MailCore/Models/Settings/AccentColor.swift index 6ef73de15..ac9c0f32f 100644 --- a/MailCore/Models/Settings/AccentColor.swift +++ b/MailCore/Models/Settings/AccentColor.swift @@ -121,6 +121,15 @@ public enum AccentColor: String, CaseIterable, SettingsOptionEnum { } } + public var mailboxImage: MailResourcesImages { + switch self { + case .pink: + return MailResourcesAsset.mailboxPink + case .blue: + return MailResourcesAsset.mailboxBlue + } + } + // MARK: Swipe settings icons public var fullTrailingIcon: MailResourcesImages { diff --git a/MailResources/Assets.xcassets/mailbox-blue.imageset/Contents.json b/MailResources/Assets.xcassets/mailbox-blue.imageset/Contents.json new file mode 100644 index 000000000..22005de41 --- /dev/null +++ b/MailResources/Assets.xcassets/mailbox-blue.imageset/Contents.json @@ -0,0 +1,25 @@ +{ + "images" : [ + { + "filename" : "mail-to-blue.svg", + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "mail-to-blue-dark.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/MailResources/Assets.xcassets/mailbox-blue.imageset/mail-to-blue-dark.svg b/MailResources/Assets.xcassets/mailbox-blue.imageset/mail-to-blue-dark.svg new file mode 100644 index 000000000..aaf79fe86 --- /dev/null +++ b/MailResources/Assets.xcassets/mailbox-blue.imageset/mail-to-blue-dark.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MailResources/Assets.xcassets/mailbox-blue.imageset/mail-to-blue.svg b/MailResources/Assets.xcassets/mailbox-blue.imageset/mail-to-blue.svg new file mode 100644 index 000000000..d0d88db9e --- /dev/null +++ b/MailResources/Assets.xcassets/mailbox-blue.imageset/mail-to-blue.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MailResources/Assets.xcassets/mailbox-pink.imageset/Contents.json b/MailResources/Assets.xcassets/mailbox-pink.imageset/Contents.json new file mode 100644 index 000000000..69f48dde4 --- /dev/null +++ b/MailResources/Assets.xcassets/mailbox-pink.imageset/Contents.json @@ -0,0 +1,25 @@ +{ + "images" : [ + { + "filename" : "mail-to-pink.svg", + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "mail-to-pink-dark.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/MailResources/Assets.xcassets/mailbox-pink.imageset/mail-to-pink-dark.svg b/MailResources/Assets.xcassets/mailbox-pink.imageset/mail-to-pink-dark.svg new file mode 100644 index 000000000..1cce214a5 --- /dev/null +++ b/MailResources/Assets.xcassets/mailbox-pink.imageset/mail-to-pink-dark.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MailResources/Assets.xcassets/mailbox-pink.imageset/mail-to-pink.svg b/MailResources/Assets.xcassets/mailbox-pink.imageset/mail-to-pink.svg new file mode 100644 index 000000000..3049e19eb --- /dev/null +++ b/MailResources/Assets.xcassets/mailbox-pink.imageset/mail-to-pink.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MailResources/Localizable/de.lproj/Localizable.strings b/MailResources/Localizable/de.lproj/Localizable.strings index 2eebe52e9..116d245ce 100644 --- a/MailResources/Localizable/de.lproj/Localizable.strings +++ b/MailResources/Localizable/de.lproj/Localizable.strings @@ -4,8 +4,8 @@ * Release: Working copy * Locale: de, German * Tagged: ios - * Exported by: Charlene Hoareau - * Exported at: Mon, 11 Mar 2024 13:27:21 +0100 + * Exported by: elena willen + * Exported at: Mon, 11 Mar 2024 16:16:23 +0100 */ /* loco:62bb154d7513e127cb2e9c94 */ @@ -455,6 +455,9 @@ /* loco:626918473e1b2d7fe4329d52 */ "buttonSelectAll" = "Alle"; +/* loco:65ef191d6c54ba7fcc03cc92 */ +"buttonSendWithDifferentAddress" = "Senden mit abweichender Adresse"; + /* loco:657324e1fb3ef333a001d852 */ "buttonSetNow" = "Jetzt festlegen"; diff --git a/MailResources/Localizable/en.lproj/Localizable.strings b/MailResources/Localizable/en.lproj/Localizable.strings index 6c509913e..7a69e6fcf 100644 --- a/MailResources/Localizable/en.lproj/Localizable.strings +++ b/MailResources/Localizable/en.lproj/Localizable.strings @@ -4,8 +4,8 @@ * Release: Working copy * Locale: en, English * Tagged: ios - * Exported by: Charlene Hoareau - * Exported at: Mon, 11 Mar 2024 13:27:21 +0100 + * Exported by: elena willen + * Exported at: Mon, 11 Mar 2024 16:16:23 +0100 */ /* loco:62bb154d7513e127cb2e9c94 */ @@ -455,6 +455,9 @@ /* loco:626918473e1b2d7fe4329d52 */ "buttonSelectAll" = "All"; +/* loco:65ef191d6c54ba7fcc03cc92 */ +"buttonSendWithDifferentAddress" = "Send with a different address"; + /* loco:657324e1fb3ef333a001d852 */ "buttonSetNow" = "Set now"; @@ -1131,13 +1134,10 @@ "settingsAutoAdvanceListOfThreadsTitle" = "List of threads"; /* loco:65df003b996ecfc40c0d9962 */ -"settingsAutoAdvanceNaturalThreadDescription" = "Repeats the last action you performed"; - -/* loco:65eb1767b7ece7e666042e02 */ -"settingsAutoAdvanceNaturalThreadHint" = "Automatically selects the next or previous thread, depending on your last interaction."; +"settingsAutoAdvanceNaturalThreadDescription" = "Display thread based on natural navigation when archiving or deleting"; /* loco:65defeba6cb9206d3f0d4392 */ -"settingsAutoAdvanceNaturalThreadTitle" = "Repeat last action"; +"settingsAutoAdvanceNaturalThreadTitle" = "Natural navigation"; /* loco:65b27d1e0565d99b930ae142 */ "settingsAutoAdvancePreviousThreadDescription" = "Display previous thread when archiving or deleting"; diff --git a/MailResources/Localizable/es.lproj/Localizable.strings b/MailResources/Localizable/es.lproj/Localizable.strings index 822d5986d..17160bced 100644 --- a/MailResources/Localizable/es.lproj/Localizable.strings +++ b/MailResources/Localizable/es.lproj/Localizable.strings @@ -4,8 +4,8 @@ * Release: Working copy * Locale: es, Spanish * Tagged: ios - * Exported by: Charlene Hoareau - * Exported at: Mon, 11 Mar 2024 13:27:21 +0100 + * Exported by: elena willen + * Exported at: Mon, 11 Mar 2024 16:16:23 +0100 */ /* loco:62bb154d7513e127cb2e9c94 */ @@ -455,6 +455,9 @@ /* loco:626918473e1b2d7fe4329d52 */ "buttonSelectAll" = "Todos"; +/* loco:65ef191d6c54ba7fcc03cc92 */ +"buttonSendWithDifferentAddress" = "Enviar con otra dirección"; + /* loco:657324e1fb3ef333a001d852 */ "buttonSetNow" = "Definir ahora"; @@ -1131,13 +1134,10 @@ "settingsAutoAdvanceListOfThreadsTitle" = "Lista de temas"; /* loco:65df003b996ecfc40c0d9962 */ -"settingsAutoAdvanceNaturalThreadDescription" = "Repite la última acción realizada"; - -/* loco:65eb1767b7ece7e666042e02 */ -"settingsAutoAdvanceNaturalThreadHint" = "Selecciona automáticamente la conversación siguiente o anterior, en función de tu última interacción."; +"settingsAutoAdvanceNaturalThreadDescription" = "Hilo de visualización basado en la navegación natural al archivar o borrar"; /* loco:65defeba6cb9206d3f0d4392 */ -"settingsAutoAdvanceNaturalThreadTitle" = "Repetir la última acción"; +"settingsAutoAdvanceNaturalThreadTitle" = "Navegación natural"; /* loco:65b27d1e0565d99b930ae142 */ "settingsAutoAdvancePreviousThreadDescription" = "Mostrar el hilo anterior al archivar o borrar"; diff --git a/MailResources/Localizable/fr.lproj/Localizable.strings b/MailResources/Localizable/fr.lproj/Localizable.strings index 95a3fbf2c..9518bcc08 100644 --- a/MailResources/Localizable/fr.lproj/Localizable.strings +++ b/MailResources/Localizable/fr.lproj/Localizable.strings @@ -4,8 +4,8 @@ * Release: Working copy * Locale: fr, French * Tagged: ios - * Exported by: Charlene Hoareau - * Exported at: Mon, 11 Mar 2024 13:27:21 +0100 + * Exported by: elena willen + * Exported at: Mon, 11 Mar 2024 16:16:23 +0100 */ /* loco:62bb154d7513e127cb2e9c94 */ @@ -455,6 +455,9 @@ /* loco:626918473e1b2d7fe4329d52 */ "buttonSelectAll" = "Tout"; +/* loco:65ef191d6c54ba7fcc03cc92 */ +"buttonSendWithDifferentAddress" = "Envoyer avec une autre adresse"; + /* loco:657324e1fb3ef333a001d852 */ "buttonSetNow" = "Définir maintenant"; @@ -1131,13 +1134,10 @@ "settingsAutoAdvanceListOfThreadsTitle" = "Liste des conversations"; /* loco:65df003b996ecfc40c0d9962 */ -"settingsAutoAdvanceNaturalThreadDescription" = "Répète la dernière action que vous avez effectuée"; - -/* loco:65eb1767b7ece7e666042e02 */ -"settingsAutoAdvanceNaturalThreadHint" = "Sélectionne automatiquement la conversation suivante ou précédente, selon votre dernière interaction."; +"settingsAutoAdvanceNaturalThreadDescription" = "Afficher la conversation selon le sens naturel de navigation après archivage ou suppression"; /* loco:65defeba6cb9206d3f0d4392 */ -"settingsAutoAdvanceNaturalThreadTitle" = "Répéter la dernière action"; +"settingsAutoAdvanceNaturalThreadTitle" = "Sens naturel de navigation"; /* loco:65b27d1e0565d99b930ae142 */ "settingsAutoAdvancePreviousThreadDescription" = "Afficher la conversation précédente après archivage ou suppression"; diff --git a/MailResources/Localizable/it.lproj/Localizable.strings b/MailResources/Localizable/it.lproj/Localizable.strings index f060dacac..194a566a4 100644 --- a/MailResources/Localizable/it.lproj/Localizable.strings +++ b/MailResources/Localizable/it.lproj/Localizable.strings @@ -4,8 +4,8 @@ * Release: Working copy * Locale: it, Italian * Tagged: ios - * Exported by: Charlene Hoareau - * Exported at: Mon, 11 Mar 2024 13:27:21 +0100 + * Exported by: elena willen + * Exported at: Mon, 11 Mar 2024 16:16:23 +0100 */ /* loco:62bb154d7513e127cb2e9c94 */ @@ -455,6 +455,9 @@ /* loco:626918473e1b2d7fe4329d52 */ "buttonSelectAll" = "Tutti"; +/* loco:65ef191d6c54ba7fcc03cc92 */ +"buttonSendWithDifferentAddress" = "Invia con un indirizzo diverso"; + /* loco:657324e1fb3ef333a001d852 */ "buttonSetNow" = "Imposta ora"; @@ -513,10 +516,10 @@ "commercialFolder" = "Promozioni"; /* loco:65c25397da1981a2280677ce */ -"composeMailboxCurrentTitle" = "Invia un messaggio con l'indirizzo :"; +"composeMailboxCurrentTitle" = "Invia un messaggio con l’indirizzo :"; /* loco:65e833228a845e47d60a4612 */ -"composeMailboxSelectionTitle" = "Seleziona l'indirizzo di invio :"; +"composeMailboxSelectionTitle" = "Seleziona l’indirizzo di invio :"; /* loco:63a474bd648cd86d56444012 */ "confirmLogoutDescription" = "Sei sicuro di volerti disconnettere da %@?"; @@ -1131,13 +1134,10 @@ "settingsAutoAdvanceListOfThreadsTitle" = "Elenco delle discussioni"; /* loco:65df003b996ecfc40c0d9962 */ -"settingsAutoAdvanceNaturalThreadDescription" = "Ripete l’ultima azione eseguita"; - -/* loco:65eb1767b7ece7e666042e02 */ -"settingsAutoAdvanceNaturalThreadHint" = "Seleziona automaticamente la conversazione successiva o precedente, a seconda dell’ultima interazione."; +"settingsAutoAdvanceNaturalThreadDescription" = "Visualizzazione della conversazione nella direzione naturale di navigazione dopo l’archiviazione o la cancellazione"; /* loco:65defeba6cb9206d3f0d4392 */ -"settingsAutoAdvanceNaturalThreadTitle" = "Ripetere l’ultima azione"; +"settingsAutoAdvanceNaturalThreadTitle" = "Navigazione naturale"; /* loco:65b27d1e0565d99b930ae142 */ "settingsAutoAdvancePreviousThreadDescription" = "Visualizzazione della discussione precedente quando si archivia o si elimina"; diff --git a/MailShareExtension/ComposeMessageWrapperView.swift b/MailShareExtension/ComposeMessageWrapperView.swift index 3d3675aa3..2d656aac0 100644 --- a/MailShareExtension/ComposeMessageWrapperView.swift +++ b/MailShareExtension/ComposeMessageWrapperView.swift @@ -70,7 +70,7 @@ struct ComposeMessageWrapperView: View { MailUpdateRequiredView { dismissHandler(()) } } else if let mailboxManager = accountManager.currentMailboxManager { ComposeMessageIntentView( - composeMessageIntent: .new(originMailboxManager: mailboxManager), + composeMessageIntent: .new(fromExtension: true), textAttachments: textAttachments, attachments: attachments )