diff --git a/enzevalos_iphone/SwiftUI/Compose/ComposeHeaderView.swift b/enzevalos_iphone/SwiftUI/Compose/ComposeHeaderView.swift index 3b2529e2ad6bb5db8f3f447ff07fcd30e483f2c5..1927a048194bbb60be8d831c04ec7abe6f2ff536 100644 --- a/enzevalos_iphone/SwiftUI/Compose/ComposeHeaderView.swift +++ b/enzevalos_iphone/SwiftUI/Compose/ComposeHeaderView.swift @@ -2,7 +2,7 @@ // ComposeHeaderView.swift // enzevalos_iphone // -// Created by Chris Offner on 23.03.21. +// Created by Chris Offner & Claire Bräuer on 23.03.21. // Copyright © 2021 fu-berlin. All rights reserved. // @@ -12,35 +12,29 @@ import SwiftUI struct ComposeViewHeader: View { @Environment(\.presentationMode) var presentationMode @ObservedObject var model: ComposeModel - @State private var encryptionOn = true var body: some View { - VStack { - // Grab handle in top center - Capsule() - .fill(Color(.lightGray)) - .frame(width: 70, height: 5) - - ZStack { - HStack { - // Cancel button - Button("Cancel") { - presentationMode.wrappedValue.dismiss() - } - - Spacer() - - Button("Send") { - model.sendMail() - presentationMode.wrappedValue.dismiss() - } - .disabled(model.recipientsModel.hasNoRecipients) + ZStack { + HStack { + // Cancel button + Button("Cancel") { + presentationMode.wrappedValue.dismiss() } - Toggle("", isOn: $encryptionOn) - .toggleStyle(EncryptionToggleStyle()) - .labelsHidden() + Spacer() + + // Send button, disabled if no recipients are set. + Button("Send") { + model.sendMail() + presentationMode.wrappedValue.dismiss() + } + .disabled(model.recipientsModel.hasNoRecipients) } + + // Encryption toggle + Toggle("", isOn: $model.encryptionOn) + .toggleStyle(EncryptionToggleStyle()) + .labelsHidden() } } @@ -65,7 +59,7 @@ struct ComposeViewHeader: View { ZStack { Circle() .stroke(Color.blue, lineWidth: 2) - + Image(systemName: "lock.fill") .font(Font.system(size: 24, weight: Font.Weight.light)) .foregroundColor(.blue) diff --git a/enzevalos_iphone/SwiftUI/Compose/ComposeModel.swift b/enzevalos_iphone/SwiftUI/Compose/ComposeModel.swift index bb110e28e097732793ce0a540d7c44e916e70b29..0d1208ab3095ba9efcc26fefa26f88bb9747c293 100644 --- a/enzevalos_iphone/SwiftUI/Compose/ComposeModel.swift +++ b/enzevalos_iphone/SwiftUI/Compose/ComposeModel.swift @@ -3,6 +3,7 @@ // enzevalos_iphone // // Created by Oliver Wiese on 12.11.20. +// modified by Chris Offner & Claire Bräuer in March 2021 // Copyright © 2020 fu-berlin. All rights reserved. // @@ -12,7 +13,7 @@ import Foundation class ComposeModel: ObservableObject { @Published var subject = "" @Published var body = "" - @Published var encryptionOff = false + @Published var encryptionOn = true @Published var recipientsModel: RecipientsModel = RecipientsModel() init(preData: PreMailData?) { @@ -26,7 +27,7 @@ class ComposeModel: ObservableObject { recipientsModel.parentComposeModel = self } - // TODO: Add security state + // TODO: Add security state functionality /// Generates mail and sends it. func sendMail() { @@ -55,7 +56,7 @@ class ComposeModel: ObservableObject { textContent: body, htmlContent: nil, textparts: 1, - sendEncryptedIfPossible: !encryptionOff, + sendEncryptedIfPossible: encryptionOn, attachments: []) } } diff --git a/enzevalos_iphone/SwiftUI/Compose/ComposeView.swift b/enzevalos_iphone/SwiftUI/Compose/ComposeView.swift index a11b1b49f8f7fb0b918c407f7b772f2bda8b43b4..aaf80ce345ea97ded34e4211cf6c4c91b59125b1 100644 --- a/enzevalos_iphone/SwiftUI/Compose/ComposeView.swift +++ b/enzevalos_iphone/SwiftUI/Compose/ComposeView.swift @@ -53,14 +53,14 @@ struct ComposeView: View { /// A view in which recipients get added or removed. struct RecipientField: View { @ObservedObject var model: RecipientFieldModel - @State var showList = false - @State var indexOfSelected: Int? + @State private var showList = false + @State private var indexOfSelected: Int? var body: some View { VStack { HStack { // Recipient text field - Text(model.type.asString) + Text(model.type.rawValue) .foregroundColor(Color(UIColor.tertiaryLabel)) // Selected recipients as blue capsules, @@ -125,8 +125,8 @@ struct RecipientField: View { // capsule in the ForEach loop above. 🤔🤔🤔 print(model.selectedContacts.count) } - .frame(minWidth: 200) - .autocapitalization(.none) + .frame(width: 200) // TODO: Stretch dynamically over available horizontal space + .autocapitalization(.none) // .frame(maxWidth: .infinity) somehow doesn't work .keyboardType(.emailAddress) } } @@ -166,11 +166,11 @@ struct RecipientCapsule: View { Text(name) .fixedSize(horizontal: true, vertical: false) - // Deletion button shown if recipient capsule is selected + // Remove button shown if recipient capsule is selected if isSelected { Button(action: removeRecipient) { Image(systemName: "xmark") - } + } // Transition: Slide in from left, slide out to left .transition(.asymmetric( insertion: AnyTransition .opacity.combined(with: .slide), @@ -179,8 +179,7 @@ struct RecipientCapsule: View { ) } } - .padding(.horizontal, 8) - .padding(.vertical, 2) + .padding(EdgeInsets(top: 2, leading: 8, bottom: 2, trailing: 8)) .foregroundColor(isSelected ? .accentColor : .secondary) } } @@ -191,6 +190,7 @@ struct RecipientCapsule: View { recipient.displayname ?? recipient.email.components(separatedBy: "@")[0] } + /// Removes recipient from selected contacts of respective RecipientFieldModel. func removeRecipient() { if let index = model.selectedContacts.firstIndex(of: recipient) { model.deselectContact(at: index) diff --git a/enzevalos_iphone/SwiftUI/Compose/RecipientFieldModel.swift b/enzevalos_iphone/SwiftUI/Compose/RecipientFieldModel.swift index 55f926d1501af9a7f52d7ba50725c9af6da36c1f..e410eb1fe4b634b15a55a60dfe9f8fd13b6e8e69 100644 --- a/enzevalos_iphone/SwiftUI/Compose/RecipientFieldModel.swift +++ b/enzevalos_iphone/SwiftUI/Compose/RecipientFieldModel.swift @@ -2,7 +2,8 @@ // TokenFieldModel.swift // enzevalos_iphone // -// Created by Oliver Wiese on 31.10.20. +// Created by Oliver Wiese on 31.10.20, +// modified by Chris Offner & Claire Bräuer in March 2021 // Copyright © 2020 fu-berlin. All rights reserved. // @@ -14,16 +15,16 @@ class RecipientFieldModel: ObservableObject { @Published var suggestions = [AddressRecord]() @Published var selectedContacts = [AddressRecord]() @Published var type: RecipientType - private var dataprovider: PersistentDataProvider + private var dataprovider: PersistentDataProvider = LetterboxModel.instance.dataProvider var parentRecipientModel: RecipientsModel? - init(type: RecipientType, dataprovider: PersistentDataProvider) { + init(type: RecipientType) { self.type = type - self.dataprovider = dataprovider } @Published var text = "" { didSet { + // Split entered text at separator characters, add first part as address if text.contains(" ") || text.contains(",") || text.contains(";") { var firstWord = text.split(separator: "\n").first firstWord?.removeLast() @@ -33,18 +34,15 @@ class RecipientFieldModel: ObservableObject { text = "" } - // TODO: Show suggestions in ComposeView. + // Generate suggestions based on entered text. if !text.isEmpty { - let frc = dataprovider - .createFetchResultController( + // Fetch addresses that match entered prefix. + let frc = dataprovider.createFetchResultController( fetchRequest: AddressRecord.lookForPrefix(prefix: text) ) - if let addresses = frc.fetchedObjects { - suggestions = addresses - } else { - suggestions = [] - } + // Set addresses as suggestions if any were found, otherwise empty suggestions + suggestions = frc.fetchedObjects ?? [] } else { suggestions = [] } @@ -60,23 +58,18 @@ class RecipientFieldModel: ObservableObject { return suggestions } - let frc = LetterboxModel - .instance - .dataProvider - .createFetchResultController( - fetchRequest: AddressRecord - .all(sortBy: sortBy) - ) + let frc = LetterboxModel.instance.dataProvider.createFetchResultController( + fetchRequest: AddressRecord.all(sortBy: sortBy) + ) - if let addresses = frc.fetchedObjects { - return addresses - } - return [] + return frc.fetchedObjects ?? [] } - /// Adds contact at index to recipients. - func selectContact(addr: AddressRecord) { - selectedContacts.append(addr) + /// Adds AddressRecord of a contact to array of recipients. + /// + /// - Parameter _: AddressRecord that gets added to array of recipients. + func selectContact(_ addressRecord: AddressRecord) { + selectedContacts.append(addressRecord) text = "" suggestions = [] @@ -89,12 +82,12 @@ class RecipientFieldModel: ObservableObject { } /// Removes contact at index from recipients. + /// + /// - Parameter at: Index of contact to be removed from array of recipients. func deselectContact(at index: Int) { - if index < selectedContacts.count { - selectedContacts.remove(at: index) - } else { - print("ERROR wrong index! \(index) but \(selectedContacts.count)") - } + selectedContacts.remove(at: index) + + // TODO: See TODO in selectContact. parentRecipientModel?.parentComposeModel?.subject += "" } @@ -104,12 +97,17 @@ class RecipientFieldModel: ObservableObject { addNewAddress(text) text = "" } + + // TODO: See TODO in selectContact. parentRecipientModel?.parentComposeModel?.subject += "" } + /// Adds new address to selected contacts. + /// + /// - Parameter _: Email address string to be added. func addNewAddress(_ address: String) { guard address.count > 0 && address.contains("@") else { - // TODO: Add email validation + warning? + // TODO: Add proper email validation + warning return } diff --git a/enzevalos_iphone/SwiftUI/Compose/RecipientListView.swift b/enzevalos_iphone/SwiftUI/Compose/RecipientListView.swift index f29165df643dc98a3f9f2408174a63777fd378a4..6663d18655be20d6bded7433e67588f40309fa25 100644 --- a/enzevalos_iphone/SwiftUI/Compose/RecipientListView.swift +++ b/enzevalos_iphone/SwiftUI/Compose/RecipientListView.swift @@ -10,7 +10,7 @@ import SwiftUI /// A view that lists contacts for selection as recipients. struct RecipientListView: View { @EnvironmentObject var model: RecipientFieldModel - @State var sortByName = true + @State private var sortByName = true var body: some View { VStack(alignment: .leading) { diff --git a/enzevalos_iphone/SwiftUI/Compose/RecipientRowView.swift b/enzevalos_iphone/SwiftUI/Compose/RecipientRowView.swift index 00986220394935fad58f9c0098c4e35a559c195e..d09c63355bb6ed077e5268d068c4031b0c0d4ecc 100644 --- a/enzevalos_iphone/SwiftUI/Compose/RecipientRowView.swift +++ b/enzevalos_iphone/SwiftUI/Compose/RecipientRowView.swift @@ -17,7 +17,7 @@ struct RecipientRowView: View { if let index = model.selectedContacts.firstIndex(where: { $0.email == contact.email }) { model.deselectContact(at: index) } else { - model.selectContact(addr: contact) + model.selectContact(contact) } } label: { HStack { diff --git a/enzevalos_iphone/SwiftUI/Compose/RecipientsModel.swift b/enzevalos_iphone/SwiftUI/Compose/RecipientsModel.swift index 223dd10a7dc53ca1a63d8c0adc71382dbddc802b..bad60591b248d7919cd821effaf2266811b650c4 100644 --- a/enzevalos_iphone/SwiftUI/Compose/RecipientsModel.swift +++ b/enzevalos_iphone/SwiftUI/Compose/RecipientsModel.swift @@ -3,6 +3,7 @@ // enzevalos_iphone // // Created by Oliver Wiese on 09.11.20. +// modified by Chris Offner & Claire Bräuer in March 2021 // Copyright © 2020 fu-berlin. All rights reserved. // @@ -10,27 +11,6 @@ import Foundation import Combine import SwiftUI -/// Type of recipient field (to, cc, bcc). -enum RecipientType { - case to - case cc - case bcc - case ccBcc - - var asString: LocalizedStringKey { - switch self { - case .to: - return "To" - case .cc: - return "Cc" - case .bcc: - return "Bcc" - case .ccBcc: - return "CcBcc" - } - } -} - /// Holds models for "To", "Cc", and "Bcc" fields. class RecipientsModel: ObservableObject { let toModel: RecipientFieldModel @@ -41,10 +21,9 @@ class RecipientsModel: ObservableObject { /// Initializes models for "To", "Cc", and "Bcc" fields. init() { - let dataprovider = LetterboxModel.instance.dataProvider - toModel = RecipientFieldModel(type: .to, dataprovider: dataprovider) - ccModel = RecipientFieldModel(type: .ccBcc, dataprovider: dataprovider) - bccModel = RecipientFieldModel(type: .bcc, dataprovider: dataprovider) + toModel = RecipientFieldModel(type: .to) + ccModel = RecipientFieldModel(type: .ccBcc) + bccModel = RecipientFieldModel(type: .bcc) toModel.parentRecipientModel = self ccModel.parentRecipientModel = self bccModel.parentRecipientModel = self @@ -69,21 +48,27 @@ class RecipientsModel: ObservableObject { /// String array of email addresses in "To" field. var toEMails: [String] { get { - toModel.selectedContacts.map({ $0.email }) + toModel.selectedContacts.map { + $0.email + } } } /// String array of email addresses in "Cc" field. var ccEMails: [String] { get { - ccModel.selectedContacts.map({ $0.email }) + ccModel.selectedContacts.map { + $0.email + } } } /// String array of email addresses in "Bcc" field. var bccEMails: [String] { get { - bccModel.selectedContacts.map({ $0.email }) + bccModel.selectedContacts.map { + $0.email + } } } @@ -95,3 +80,11 @@ class RecipientsModel: ObservableObject { ccModel.type = showBccField ? .cc : .ccBcc } } + +/// Type of recipient field (to, cc, bcc). +enum RecipientType: LocalizedStringKey { + case to = "To" + case cc = "Cc" + case bcc = "Bcc" + case ccBcc = "CcBcc" +}