From 0c3b4d8adfa6a2d4157a633e66d3b151fbaa74f8 Mon Sep 17 00:00:00 2001 From: Valentin Perignon Date: Mon, 19 Jun 2023 08:38:45 +0200 Subject: [PATCH] refactor: Remove old compenents --- Mail/Components/RecipientField.swift | 159 ------------------ Mail/Views/New Message/AddRecipientCell.swift | 57 ------- .../New Message/AutocompletionView.swift | 65 ------- Mail/Views/New Message/NewMessageCell.swift | 97 ----------- .../New Message/RecipientsTextField.swift | 97 ----------- .../New Message/V2/RecipientFieldV2.swift | 19 ++- .../V2/RecipientsTextFieldV2.swift | 30 ++++ 7 files changed, 42 insertions(+), 482 deletions(-) delete mode 100644 Mail/Components/RecipientField.swift delete mode 100644 Mail/Views/New Message/AddRecipientCell.swift delete mode 100644 Mail/Views/New Message/AutocompletionView.swift delete mode 100644 Mail/Views/New Message/NewMessageCell.swift delete mode 100644 Mail/Views/New Message/RecipientsTextField.swift diff --git a/Mail/Components/RecipientField.swift b/Mail/Components/RecipientField.swift deleted file mode 100644 index 06abdd0d3..000000000 --- a/Mail/Components/RecipientField.swift +++ /dev/null @@ -1,159 +0,0 @@ -/* - 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 RealmSwift -import SwiftUI -import WrappingHStack - -struct RecipientField: View { - @State private var currentText = "" - @State private var keyboardHeight: CGFloat = 0 - - @Binding var recipients: RealmSwift.List - @Binding var autocompletion: [Recipient] - @Binding var unknownRecipientAutocompletion: String - @MainActor @Binding var addRecipientHandler: ((Recipient) -> Void)? - - @FocusState var focusedField: ComposeViewFieldType? - - let type: ComposeViewFieldType - - /// A trimmed view on `currentText` - private var trimmedInputText: String { - currentText.trimmingCharacters(in: .whitespacesAndNewlines) - } - - var body: some View { - VStack { - if !recipients.isEmpty { - WrappingHStack(recipients.indices, spacing: .constant(8), lineSpacing: 8) { i in - RecipientChip(recipient: recipients[i], fieldType: type, focusedField: _focusedField) { - remove(recipientAt: i) - } switchFocusHandler: { - switchFocus() - } - .focused($focusedField, equals: .chip(type.hashValue, recipients[i])) - } - .alignmentGuide(.newMessageCellAlignment) { d in d[.top] + 21 } - } - - RecipientsTextFieldView(text: $currentText, onSubmit: submitTextField, onBackspace: handleBackspaceTextField) - .focused($focusedField, equals: type) - } - .onChange(of: currentText) { _ in - updateAutocompletion() - addRecipientHandler = add(recipient:) - } - .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardDidShowNotification)) { output in - if let userInfo = output.userInfo, - let keyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect { - keyboardHeight = keyboardFrame.height - } - } - .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardDidHideNotification)) { _ in - keyboardHeight = 0 - } - } - - @MainActor private func submitTextField() { - // use first autocompletion result or try to validate current input - guard let recipient = autocompletion.first else { - let guessRecipient = Recipient(email: trimmedInputText, name: "") - add(recipient: guessRecipient) - return - } - - add(recipient: recipient) - } - - @MainActor private func add(recipient: Recipient) { - @InjectService var matomo: MatomoUtils - matomo.track(eventWithCategory: .newMessage, action: .input, name: "addNewRecipient") - - if Constants.isEmailAddress(recipient.email) { - withAnimation { - $recipients.append(recipient) - } - currentText = "" - } else { - IKSnackBar.showSnackBar( - message: MailResourcesStrings.Localizable.addUnknownRecipientInvalidEmail, - anchor: keyboardHeight - ) - } - } - - @MainActor private func remove(recipientAt: Int) { - withAnimation { - $recipients.remove(at: recipientAt) - } - } - - private func handleBackspaceTextField(isTextEmpty: Bool) { - if let recipient = recipients.last, isTextEmpty { - focusedField = .chip(type.hashValue, recipient) - } - } - - private func updateAutocompletion() { - let trimmedCurrentText = trimmedInputText - - let contactManager = AccountManager.instance.currentContactManager - let autocompleteContacts = contactManager?.contacts(matching: trimmedCurrentText) ?? [] - let autocompleteRecipients = autocompleteContacts.map { Recipient(email: $0.email, name: $0.name) } - - withAnimation { - autocompletion = autocompleteRecipients.filter { !recipients.map(\.email).contains($0.email) } - - if !trimmedCurrentText.isEmpty && !autocompletion - .contains(where: { $0.email.caseInsensitiveCompare(trimmedCurrentText) == .orderedSame }) { - unknownRecipientAutocompletion = trimmedCurrentText - } else { - unknownRecipientAutocompletion = "" - } - } - } - - private func switchFocus() { - guard case .chip(let hash, let recipient) = focusedField else { return } - - if recipient == recipients.last { - focusedField = type - } else if let recipientIndex = recipients.firstIndex(of: recipient) { - focusedField = .chip(hash, recipients[recipientIndex + 1]) - } - } -} - -struct RecipientField_Previews: PreviewProvider { - static var previews: some View { - RecipientField(recipients: .constant([ - PreviewHelper.sampleRecipient1, PreviewHelper.sampleRecipient2, PreviewHelper.sampleRecipient3 - ].toRealmList()), - autocompletion: .constant([]), - unknownRecipientAutocompletion: .constant(""), - addRecipientHandler: .constant { _ in /* Preview */ }, - focusedField: .init(), - type: .to) - } -} diff --git a/Mail/Views/New Message/AddRecipientCell.swift b/Mail/Views/New Message/AddRecipientCell.swift deleted file mode 100644 index ba8c6c650..000000000 --- a/Mail/Views/New Message/AddRecipientCell.swift +++ /dev/null @@ -1,57 +0,0 @@ -/* - 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 MailCore -import SwiftUI -import MailResources - -struct AddRecipientCell: View { - @AppStorage(UserDefaults.shared.key(.accentColor)) private var accentColor = DefaultPreferences.accentColor - - let recipientEmail: String - - var body: some View { - HStack(spacing: 8) { - Circle() - .fill(accentColor.primary.swiftUIColor) - .frame(width: 40, height: 40) - .overlay { - MailResourcesAsset.userBold.swiftUIImage - .resizable() - .foregroundColor(accentColor.onAccent.swiftUIColor) - .frame(width: 24, height: 24) - } - - VStack(alignment: .leading, spacing: 0) { - Text(MailResourcesStrings.Localizable.addUnknownRecipientTitle) - .textStyle(.bodyMedium) - Text(recipientEmail) - .textStyle(.bodySecondary) - } - - Spacer() - } - .lineLimit(1) - } -} - -struct AddRecipientCell_Previews: PreviewProvider { - static var previews: some View { - AddRecipientCell(recipientEmail: "") - } -} diff --git a/Mail/Views/New Message/AutocompletionView.swift b/Mail/Views/New Message/AutocompletionView.swift deleted file mode 100644 index 4acecb730..000000000 --- a/Mail/Views/New Message/AutocompletionView.swift +++ /dev/null @@ -1,65 +0,0 @@ -/* - 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 MailCore -import MailResources -import SwiftUI - -struct AutocompletionView: View { - @Binding var autocompletion: [Recipient] - @Binding var unknownRecipientAutocompletion: String - - let onSelect: (Recipient) -> Void - - var body: some View { - LazyVStack { - ForEach(autocompletion) { recipient in - VStack(alignment: .leading, spacing: 8) { - Button { - onSelect(recipient) - } label: { - RecipientCell(recipient: recipient) - } - .padding(.horizontal, 8) - - IKDivider() - } - } - - if !unknownRecipientAutocompletion.isEmpty { - Button { - onSelect(Recipient(email: unknownRecipientAutocompletion, name: "")) - } label: { - AddRecipientCell(recipientEmail: unknownRecipientAutocompletion) - } - .padding(.horizontal, 8) - } - } - .padding(.top, 8) - .padding(.horizontal, 8) - } -} - -struct AutocompletionView_Previews: PreviewProvider { - static var previews: some View { - AutocompletionView(autocompletion: .constant([ - PreviewHelper.sampleRecipient1, PreviewHelper.sampleRecipient2, PreviewHelper.sampleRecipient3 - ]), - unknownRecipientAutocompletion: .constant("")) { _ in /* Preview */ } - } -} diff --git a/Mail/Views/New Message/NewMessageCell.swift b/Mail/Views/New Message/NewMessageCell.swift deleted file mode 100644 index fdd8f1520..000000000 --- a/Mail/Views/New Message/NewMessageCell.swift +++ /dev/null @@ -1,97 +0,0 @@ -/* - 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 MailCore -import MailResources -import RealmSwift -import SwiftUI - -extension VerticalAlignment { - private struct NewMessageCellAlignment: AlignmentID { - static func defaultValue(in context: ViewDimensions) -> CGFloat { - context[.firstTextBaseline] - } - } - - static let newMessageCellAlignment = VerticalAlignment(NewMessageCellAlignment.self) -} - -struct NewMessageCell: View where Content: View { - let type: ComposeViewFieldType - let focusedField: FocusState? - let showCc: Binding? - let isFirstCell: Bool - let content: Content - - let verticalPadding: CGFloat = 12 - - init(type: ComposeViewFieldType, - focusedField: FocusState? = nil, - showCc: Binding? = nil, - isFirstCell: Bool = false, - @ViewBuilder _ content: () -> Content) { - self.type = type - self.focusedField = focusedField - self.showCc = showCc - self.isFirstCell = isFirstCell - self.content = content() - } - - var body: some View { - HStack(alignment: .newMessageCellAlignment) { - Text(type.title) - .textStyle(.bodySecondary) - - content - - Spacer() - - if let showCc = showCc { - ChevronButton(isExpanded: showCc) - } - } - .padding(.horizontal, 16) - .padding(.top, isFirstCell ? 0 : verticalPadding) - .padding(.bottom, verticalPadding) - .onTapGesture { - focusedField?.wrappedValue = type - } - - IKDivider() - .padding(.horizontal, 8) - } -} - -struct NewMessageCell_Previews: PreviewProvider { - static var previews: some View { - VStack { - NewMessageCell(type: .to, - showCc: .constant(false)) { - RecipientField(recipients: .constant([PreviewHelper.sampleRecipient1].toRealmList()), - autocompletion: .constant([]), - unknownRecipientAutocompletion: .constant(""), - addRecipientHandler: .constant { _ in /* Preview */ }, - focusedField: .init(), - type: .to) - } - NewMessageCell(type: .subject) { - TextField("", text: .constant("")) - } - } - } -} diff --git a/Mail/Views/New Message/RecipientsTextField.swift b/Mail/Views/New Message/RecipientsTextField.swift deleted file mode 100644 index 4f9c71653..000000000 --- a/Mail/Views/New Message/RecipientsTextField.swift +++ /dev/null @@ -1,97 +0,0 @@ -/* - 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 SwiftUI -import UIKit - -struct RecipientsTextFieldView: UIViewRepresentable { - @Binding var text: String - - let onSubmit: () -> Void - let onBackspace: (Bool) -> Void - - func makeUIView(context: Context) -> UITextField { - let textField = RecipientsTextField() - textField.delegate = context.coordinator - textField.addTarget(context.coordinator, action: #selector(context.coordinator.textDidChanged(_:)), for: .editingChanged) - textField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) - textField.setContentHuggingPriority(.defaultHigh, for: .vertical) - textField.onBackspace = onBackspace - return textField - } - - func updateUIView(_ textField: UITextField, context: Context) { - guard textField.text != text else { return } - textField.text = text - } - - func makeCoordinator() -> Coordinator { - return Coordinator(self) - } - - class Coordinator: NSObject, UITextFieldDelegate { - let parent: RecipientsTextFieldView - - init(_ parent: RecipientsTextFieldView) { - self.parent = parent - } - - func textFieldShouldReturn(_ textField: UITextField) -> Bool { - guard textField.text?.isEmpty == false else { - textField.resignFirstResponder() - return true - } - - parent.onSubmit() - return true - } - - @objc func textDidChanged(_ textField: UITextField) { - parent.text = textField.text ?? "" - } - } -} - -/* - * We need to create our own UITextField to benefit from the `deleteBackward()` function - */ -class RecipientsTextField: UITextField { - var onBackspace: ((Bool) -> Void)? - - override init(frame: CGRect) { - super.init(frame: frame) - setUpView() - } - - required init?(coder: NSCoder) { - super.init(coder: coder) - setUpView() - } - - private func setUpView() { - textContentType = .emailAddress - keyboardType = .emailAddress - autocapitalizationType = .none - autocorrectionType = .no - } - - override func deleteBackward() { - onBackspace?(text?.isEmpty == true) - super.deleteBackward() - } -} diff --git a/Mail/Views/New Message/V2/RecipientFieldV2.swift b/Mail/Views/New Message/V2/RecipientFieldV2.swift index 3630513af..8d694ad8e 100644 --- a/Mail/Views/New Message/V2/RecipientFieldV2.swift +++ b/Mail/Views/New Message/V2/RecipientFieldV2.swift @@ -25,6 +25,16 @@ import RealmSwift import SwiftUI import WrappingHStack +extension VerticalAlignment { + private struct NewMessageCellAlignment: AlignmentID { + static func defaultValue(in context: ViewDimensions) -> CGFloat { + context[.firstTextBaseline] + } + } + + static let newMessageCellAlignment = VerticalAlignment(NewMessageCellAlignment.self) +} + struct RecipientFieldV2: View { @State private var keyboardHeight: CGFloat = 0 @@ -94,13 +104,8 @@ struct RecipientFieldV2: View { struct RecipientFieldV2_Previews: PreviewProvider { static var previews: some View { - RecipientField(recipients: .constant([ + RecipientFieldV2(currentText: .constant(""), recipients: .constant([ PreviewHelper.sampleRecipient1, PreviewHelper.sampleRecipient2, PreviewHelper.sampleRecipient3 - ].toRealmList()), - autocompletion: .constant([]), - unknownRecipientAutocompletion: .constant(""), - addRecipientHandler: .constant { _ in /* Preview */ }, - focusedField: .init(), - type: .to) + ].toRealmList()), type: .to) } } diff --git a/Mail/Views/New Message/V2/RecipientsTextFieldV2.swift b/Mail/Views/New Message/V2/RecipientsTextFieldV2.swift index 19151c10f..f31b84f90 100644 --- a/Mail/Views/New Message/V2/RecipientsTextFieldV2.swift +++ b/Mail/Views/New Message/V2/RecipientsTextFieldV2.swift @@ -66,3 +66,33 @@ struct RecipientsTextFieldV2View: UIViewRepresentable { } } } + +/* + * We need to create our own UITextField to benefit from the `deleteBackward()` function + */ +class RecipientsTextField: UITextField { + var onBackspace: ((Bool) -> Void)? + + override init(frame: CGRect) { + super.init(frame: frame) + setUpView() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setUpView() + } + + private func setUpView() { + textContentType = .emailAddress + keyboardType = .emailAddress + autocapitalizationType = .none + autocorrectionType = .no + } + + override func deleteBackward() { + onBackspace?(text?.isEmpty == true) + super.deleteBackward() + } +} +