Skip to content

Commit

Permalink
Merge pull request #945 from Infomaniak/external-message
Browse files Browse the repository at this point in the history
feat: External tag
  • Loading branch information
adrien-coye committed Aug 29, 2023
2 parents 6cd6c80 + 3e43f34 commit ac105ee
Show file tree
Hide file tree
Showing 19 changed files with 349 additions and 24 deletions.
27 changes: 21 additions & 6 deletions Mail/Components/RecipientChipLabel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,20 @@

import Foundation
import MailCore
import MailResources
import SwiftUI
import UIKit

struct RecipientChipLabelView: UIViewRepresentable {
@Environment(\.isEnabled) private var isEnabled: Bool
@EnvironmentObject private var mailboxManager: MailboxManager

let recipient: Recipient
var removeHandler: (() -> Void)?
var switchFocusHandler: (() -> Void)?

func makeUIView(context: Context) -> RecipientChipLabel {
let label = RecipientChipLabel(recipient: recipient)
let label = RecipientChipLabel(recipient: recipient, external: recipient.isExternal(mailboxManager: mailboxManager))
label.removeHandler = removeHandler
label.switchFocusHandler = switchFocusHandler
label.setContentHuggingPriority(.defaultHigh, for: .horizontal)
Expand All @@ -40,7 +42,9 @@ struct RecipientChipLabelView: UIViewRepresentable {

func updateUIView(_ uiLabel: RecipientChipLabel, context: Context) {
uiLabel.text = recipient.name.isEmpty ? recipient.email : recipient.name
uiLabel.isExternal = recipient.isExternal(mailboxManager: mailboxManager)
uiLabel.isUserInteractionEnabled = isEnabled
uiLabel.updateColors(isFirstResponder: uiLabel.isFirstResponder)
}
}

Expand All @@ -58,19 +62,23 @@ class RecipientChipLabel: UILabel, UIKeyInput {
override var canBecomeFirstResponder: Bool { return isUserInteractionEnabled }

var hasText = false
var isExternal = false

init(recipient: Recipient) {
init(recipient: Recipient, external: Bool) {
super.init(frame: .zero)

text = recipient.name.isEmpty ? recipient.email : recipient.name
textAlignment = .center
numberOfLines = 1

font = .systemFont(ofSize: 16)
updateColors(isFirstResponder: false)

layer.cornerRadius = intrinsicContentSize.height / 2
layer.borderWidth = 1
layer.masksToBounds = true

isExternal = external
updateColors(isFirstResponder: false)
}

@available(*, unavailable)
Expand Down Expand Up @@ -102,8 +110,15 @@ class RecipientChipLabel: UILabel, UIKeyInput {
removeHandler?()
}

private func updateColors(isFirstResponder: Bool) {
textColor = isFirstResponder ? UserDefaults.shared.accentColor.secondary.color : .tintColor
backgroundColor = isFirstResponder ? .tintColor : UserDefaults.shared.accentColor.secondary.color
public func updateColors(isFirstResponder: Bool) {
if isExternal {
textColor = isFirstResponder ? MailResourcesAsset.onTagColor.color : MailResourcesAsset.textPrimaryColor.color
borderColor = MailResourcesAsset.yellowColor.color
backgroundColor = isFirstResponder ? MailResourcesAsset.yellowColor.color : MailResourcesAsset.textFieldColor.color
} else {
textColor = isFirstResponder ? UserDefaults.shared.accentColor.secondary.color : .tintColor
borderColor = isFirstResponder ? UserDefaults.shared.accentColor.primary.color : UserDefaults.shared.accentColor.secondary.color
backgroundColor = isFirstResponder ? .tintColor : UserDefaults.shared.accentColor.secondary.color
}
}
}
4 changes: 3 additions & 1 deletion Mail/Helpers/PreviewHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ enum PreviewHelper {
mdaVersion: "",
isLimited: false,
isFree: false,
dailyLimit: 999)
dailyLimit: 999,
aliases: ["test@example.com", "test@example.ch"].toRealmList(),
externalMailFlagEnabled: true)

static let sampleFolder = Folder(id: "",
path: "Folder",
Expand Down
60 changes: 60 additions & 0 deletions Mail/Views/Alerts/ExternalRecipientView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
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 <http://www.gnu.org/licenses/>.
*/

import MailCore
import MailResources
import RealmSwift
import SwiftUI

struct ExternalRecipientView: View {
@Environment(\.dismiss) private var dismiss

public var externalTagSate: DisplayExternalRecipientStatus.State
public var isDraft: Bool

var body: some View {
VStack(alignment: .leading, spacing: 24) {
isDraft ? Text(MailResourcesStrings.Localizable.externalDialogTitleRecipient)
.textStyle(.bodyMedium) : Text(MailResourcesStrings.Localizable.externalDialogTitleExpeditor)
.textStyle(.bodyMedium)

switch externalTagSate {
case .none:
EmptyView()
case .one(let recipient):
isDraft ? Text(MailResourcesStrings.Localizable.externalDialogDescriptionRecipient(recipient.email))
.textStyle(.bodySecondary) : Text(MailResourcesStrings.Localizable.externalDialogDescriptionExpeditor(recipient.email))
.textStyle(.bodySecondary)
case .many:
isDraft ? Text(MailResourcesStrings.Localizable.externalDialogDescriptionRecipientPlural)
.textStyle(.bodySecondary) : Text(MailResourcesStrings.Localizable.externalDialogDescriptionExpeditorPlural)
.textStyle(.bodySecondary)
}

ModalButtonsView(primaryButtonTitle: MailResourcesStrings.Localizable.externalDialogConfirmButton, secondaryButtonTitle: nil) {
dismiss()
}
}
}
}

struct ExternalRecipientView_Previews: PreviewProvider {
static var previews: some View {
ExternalRecipientView(externalTagSate: .many, isDraft: false)
}
}
54 changes: 53 additions & 1 deletion Mail/Views/New Message/ComposeMessageView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ final class NewMessageAlert: SheetState<NewMessageAlert.State> {
enum State {
case link(handler: (String) -> Void)
case emptySubject(handler: () -> Void)
case externalRecipient(state: DisplayExternalRecipientStatus.State)
}
}

Expand All @@ -72,6 +73,7 @@ struct ComposeMessageView: View {
@State private var editorFocus = false
@State private var currentSignature: Signature?
@State private var initialAttachments = [Attachable]()
@State private var isShowingExternalTag = true

@State private var editorModel = RichTextEditorModel()
@State private var scrollView: UIScrollView?
Expand Down Expand Up @@ -159,6 +161,8 @@ struct ComposeMessageView: View {
AddLinkView(actionHandler: handler)
case .emptySubject(let handler):
EmptySubjectView(actionHandler: handler)
case .externalRecipient(let state):
ExternalRecipientView(externalTagSate: state, isDraft: true)
case .none:
EmptyView()
}
Expand Down Expand Up @@ -240,6 +244,46 @@ struct ComposeMessageView: View {
.disabled(isSendButtonDisabled)
}
}
.safeAreaInset(edge: .bottom) {
if isShowingExternalTag {
let externalTag = draft.displayExternalTag(mailboxManager: mailboxManager)
switch externalTag {
case .many, .one:
HStack(spacing: 24) {
Text(MailResourcesStrings.Localizable.externalDialogTitleRecipient)
.foregroundColor(MailResourcesAsset.onTagColor)
.textStyle(.bodySmall)

Spacer()

Button {
matomo.track(eventWithCategory: .externals, name: "bannerInfo")
alert.state = .externalRecipient(state: externalTag)
} label: {
MailResourcesAsset.info.swiftUIImage
.resizable()
.foregroundColor(MailResourcesAsset.onTagColor)
.frame(width: 16, height: 16)
}

Button {
matomo.track(eventWithCategory: .externals, name: "bannerManuallyClosed")
isShowingExternalTag = false
} label: {
MailResourcesAsset.closeSmall.swiftUIImage
.resizable()
.foregroundColor(MailResourcesAsset.onTagColor)
.frame(width: 16, height: 16)
}
}
.frame(maxWidth: .infinity)
.padding(16)
.background(MailResourcesAsset.yellowColor.swiftUIColor)
case .none:
EmptyView()
}
}
}
}

/// Progress view
Expand Down Expand Up @@ -275,7 +319,15 @@ struct ComposeMessageView: View {
}

private func sendDraft() {
matomo.trackSendMessage(numberOfTo: draft.to.count, numberOfCc: draft.cc.count, numberOfBcc: draft.bcc.count)
let sentWithExternals: Bool
switch draft.displayExternalTag(mailboxManager: mailboxManager) {
case .one, .many:
sentWithExternals = true
case .none:
sentWithExternals = false
}

matomo.trackSendMessage(draft: draft, sentWithExternals: sentWithExternals)
if let liveDraft = draft.thaw() {
try? liveDraft.realm?.write {
liveDraft.action = .send
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ struct ComposeMessageCellRecipients: View {
}

withAnimation {
recipient.isAddedByMe = true
$recipients.append(recipient)
}
textDebounce.text = ""
Expand Down
3 changes: 3 additions & 0 deletions Mail/Views/New Message/Recipients/FullRecipientsList.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ struct FullRecipientsList: View {
@MainActor private func remove(recipientAt: Int) {
@InjectService var matomo: MatomoUtils
matomo.track(eventWithCategory: .newMessage, name: "deleteRecipient")
if recipients[recipientAt].isExternal(mailboxManager: mailboxManager) {
matomo.track(eventWithCategory: .externals, name: "deleteRecipient")
}

withAnimation {
$recipients.remove(at: recipientAt)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ struct SettingsNotificationsView: View {
func updateTopicsForCurrentUserIfNeeded() {
Task {
guard let subscribedTopics else { return }

await notificationService.updateTopicsIfNeeded(subscribedTopics, userApiFetcher: mailboxManager.apiFetcher)
}
}
Expand Down
46 changes: 38 additions & 8 deletions Mail/Views/Thread/ThreadView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ struct ThreadView: View {
@State private var displayNavigationTitle = false
@State private var replyOrReplyAllMessage: Message?

@StateObject private var alert = NewMessageAlert()

@ObservedRealmObject var thread: Thread

private let toolbarActions: [Action] = [.reply, .forward, .archive, .delete]
Expand All @@ -60,14 +62,34 @@ struct ThreadView: View {
}
.frame(width: 0, height: 0)

Text(thread.formattedSubject)
.textStyle(.header2)
.frame(maxWidth: .infinity, alignment: .leading)
.multilineTextAlignment(.leading)
.lineSpacing(8)
.padding(.top, 8)
.padding(.bottom, 16)
.padding(.horizontal, 16)
VStack(alignment: .leading, spacing: 8) {
Text(thread.formattedSubject)
.textStyle(.header2)
.frame(maxWidth: .infinity, alignment: .leading)
.multilineTextAlignment(.leading)
.lineSpacing(8)

let externalTag = thread.displayExternalRecipientState(mailboxManager: mailboxManager, recipientsList: thread.from)
switch externalTag {
case .many, .one:
Button {
matomo.track(eventWithCategory: .externals, name: "threadTag")
alert.state = .externalRecipient(state: externalTag)
} label: {
Text(MailResourcesStrings.Localizable.externalTag)
.foregroundColor(MailResourcesAsset.onTagColor)
.textStyle(.labelMedium)
.padding(4)
.background(MailResourcesAsset.yellowColor.swiftUIColor)
.cornerRadius(2)
}
case .none:
EmptyView()
}
}
.padding(.top, 8)
.padding(.bottom, 16)
.padding(.horizontal, 16)

MessageListView(messages: thread.messages)
}
Expand Down Expand Up @@ -147,6 +169,14 @@ struct ThreadView: View {
// Dismiss on iPhone only
dismiss()
}
.customAlert(isPresented: $alert.isShowing) {
switch alert.state {
case .externalRecipient(let state):
ExternalRecipientView(externalTagSate: state, isDraft: false)
default:
EmptyView()
}
}
.matomoView(view: [MatomoUtils.View.threadView.displayName, "Main"])
}

Expand Down
2 changes: 1 addition & 1 deletion MailCore/API/Endpoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public extension Endpoint {
static var mailboxes: Endpoint {
return .base.appending(
path: "/mailbox",
queryItems: [URLQueryItem(name: "with", value: "unseen")]
queryItems: [URLQueryItem(name: "with", value: "unseen,aliases,external_mail_flag_enabled")]
)
}

Expand Down
2 changes: 1 addition & 1 deletion MailCore/Cache/MailboxManager/MailboxManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public final class MailboxManager: ObservableObject, MailboxManageable {
let realmName = "\(mailbox.userId)-\(mailbox.mailboxId).realm"
realmConfiguration = Realm.Configuration(
fileURL: MailboxManager.constants.rootDocumentsURL.appendingPathComponent(realmName),
schemaVersion: 18,
schemaVersion: 19,
migrationBlock: { migration, oldSchemaVersion in
// No migration needed from 0 to 16
if oldSchemaVersion < 17 {
Expand Down
16 changes: 16 additions & 0 deletions MailCore/Models/Draft.swift
Original file line number Diff line number Diff line change
Expand Up @@ -298,3 +298,19 @@ public extension Draft {
return available
}
}

public extension Draft {
/// Compute if the draft has external recipients
func displayExternalTag(mailboxManager: MailboxManager) -> DisplayExternalRecipientStatus.State {
var recipientsList = List<Recipient>()
recipientsList.append(objectsIn: cc)
recipientsList.append(objectsIn: bcc)
recipientsList.append(objectsIn: to)
return displayExternalRecipientState(mailboxManager: mailboxManager, recipientsList: recipientsList)
}

func displayExternalRecipientState(mailboxManager: MailboxManager, recipientsList: List<Recipient>) -> DisplayExternalRecipientStatus.State {
let externalDisplayStatus = DisplayExternalRecipientStatus(mailboxManager: mailboxManager, recipientsList: recipientsList)
return externalDisplayStatus.state
}
}
Loading

0 comments on commit ac105ee

Please sign in to comment.