diff --git a/enzevalos_iphone.xcodeproj/project.pbxproj b/enzevalos_iphone.xcodeproj/project.pbxproj index b36c8f7c8e09beb0f0af269411c8e407db68dca9..3f551769a84444ede04fd1138aca3b62b3a9fb54 100644 --- a/enzevalos_iphone.xcodeproj/project.pbxproj +++ b/enzevalos_iphone.xcodeproj/project.pbxproj @@ -212,6 +212,9 @@ 71DF08982421520D00162B74 /* EmailStringExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71DF08972421520D00162B74 /* EmailStringExtensionTests.swift */; }; 71DFE5BA240679E80042019C /* HeaderExtractionValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71DFE5B9240679E80042019C /* HeaderExtractionValues.swift */; }; 786C2433142C638AE0605CD2 /* Pods_enzevalos_iphoneUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 434D0E988CDFB6D2D46C1EA8 /* Pods_enzevalos_iphoneUITests.framework */; }; + 7FE3F9D6260E76A40025340B /* ImagePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FE3F9D5260E76A40025340B /* ImagePicker.swift */; }; + 7FE3F9E9260F45600025340B /* AddAttachmentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FE3F9E8260F45600025340B /* AddAttachmentsView.swift */; }; + 7FE3F9FA260F467A0025340B /* Attachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FE3F9F9260F467A0025340B /* Attachment.swift */; }; 971D404A2428C87E002FCD31 /* BadgeCaseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 971D40492428C87E002FCD31 /* BadgeCaseView.swift */; }; 97BDE0432429188500B0BF03 /* BadgeProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97BDE0422429188500B0BF03 /* BadgeProgressView.swift */; }; 988C9C5D240D507A006213F0 /* UrlStringExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 988C9C5C240D507A006213F0 /* UrlStringExtensionTests.swift */; }; @@ -588,6 +591,9 @@ 678942622430C40600C746D1 /* MailComparisonTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MailComparisonTests.swift; sourceTree = "<group>"; }; 71DF08972421520D00162B74 /* EmailStringExtensionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailStringExtensionTests.swift; sourceTree = "<group>"; }; 71DFE5B9240679E80042019C /* HeaderExtractionValues.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderExtractionValues.swift; sourceTree = "<group>"; }; + 7FE3F9D5260E76A40025340B /* ImagePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagePicker.swift; sourceTree = "<group>"; }; + 7FE3F9E8260F45600025340B /* AddAttachmentsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddAttachmentsView.swift; sourceTree = "<group>"; }; + 7FE3F9F9260F467A0025340B /* Attachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Attachment.swift; sourceTree = "<group>"; }; 8EE6653EBD27D31263A36CA9 /* Pods-enzevalos_iphoneTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-enzevalos_iphoneTests.release.xcconfig"; path = "Target Support Files/Pods-enzevalos_iphoneTests/Pods-enzevalos_iphoneTests.release.xcconfig"; sourceTree = "<group>"; }; 971D40492428C87E002FCD31 /* BadgeCaseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeCaseView.swift; sourceTree = "<group>"; }; 97BDE0422429188500B0BF03 /* BadgeProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeProgressView.swift; sourceTree = "<group>"; }; @@ -1229,6 +1235,9 @@ 47FA8EB1254D93D2006883D0 /* Compose */ = { isa = PBXGroup; children = ( + 7FE3F9E8260F45600025340B /* AddAttachmentsView.swift */, + 7FE3F9F9260F467A0025340B /* Attachment.swift */, + 7FE3F9D5260E76A40025340B /* ImagePicker.swift */, 47FA8EC2254D9E01006883D0 /* RecipientFieldModel.swift */, 47E04DD22559992A00189320 /* RecipientsModel.swift */, 3FB75DC825FFA77800919925 /* RecipientRowView.swift */, @@ -1308,6 +1317,13 @@ name = Frameworks; sourceTree = "<group>"; }; + 7FE3F9C0260E18BA0025340B /* Recovered References */ = { + isa = PBXGroup; + children = ( + ); + name = "Recovered References"; + sourceTree = "<group>"; + }; 82C71EC1E0E091561B645F91 /* Pods */ = { isa = PBXGroup; children = ( @@ -1373,6 +1389,7 @@ A13526761D955BDF00D3BFE1 /* Products */, 78280F99990BFF65543B7F0B /* Frameworks */, 82C71EC1E0E091561B645F91 /* Pods */, + 7FE3F9C0260E18BA0025340B /* Recovered References */, ); sourceTree = "<group>"; }; @@ -1909,6 +1926,7 @@ 6789425F2430C3B300C746D1 /* MailComparison.swift in Sources */, A114E4321FACB23000E40243 /* StringExtension.swift in Sources */, 472F398C1E2519C8009260FB /* CNContactExtension.swift in Sources */, + 7FE3F9FA260F467A0025340B /* Attachment.swift in Sources */, 476406972416B54D00C7D426 /* InboxCoordinator.swift in Sources */, 0EF148082422572500B3C198 /* general-helpers.c in Sources */, 97BDE0432429188500B0BF03 /* BadgeProgressView.swift in Sources */, @@ -1931,6 +1949,7 @@ F14239C11F30A99C00998A83 /* QRCodeGenerator.swift in Sources */, 4764069C2416B54D00C7D426 /* VCSwiftUIView.swift in Sources */, 47BCAF26259CD52F0008FE4B /* PublicKeyListView.swift in Sources */, + 7FE3F9D6260E76A40025340B /* ImagePicker.swift in Sources */, 478154A921FF3FF400A931EC /* Invitation.swift in Sources */, 4733B1E52527196100AB5600 /* PersistentDataProvider.swift in Sources */, F1866C86201F707200B72453 /* EmailHelper.m in Sources */, @@ -1970,6 +1989,7 @@ 47EABF0A241A9C8700774A93 /* AuthenticationViewModel.swift in Sources */, 47FA8EAC254D77DE006883D0 /* MailListView.swift in Sources */, 47C8225924379EAE005BCE73 /* AttPreview.swift in Sources */, + 7FE3F9E9260F45600025340B /* AddAttachmentsView.swift in Sources */, 475B00341F7B9565006CDD41 /* Cryptography.swift in Sources */, A1EB057C1D956838008659C1 /* MailHandler.swift in Sources */, 0EFEF0952417C0B400BB2FF7 /* CHelpers.swift in Sources */, diff --git a/enzevalos_iphone/SwiftUI/Compose/AddAttachmentsView.swift b/enzevalos_iphone/SwiftUI/Compose/AddAttachmentsView.swift new file mode 100644 index 0000000000000000000000000000000000000000..dcc51473053a283d93348b80e8107682abe04610 --- /dev/null +++ b/enzevalos_iphone/SwiftUI/Compose/AddAttachmentsView.swift @@ -0,0 +1,175 @@ +// +// AddAttachmentsView.swift +// enzevalos_iphone +// 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 https://www.gnu.org/licenses/. +// + +import SwiftUI + +/// A view that enables uploading an previewing files and pictures as attachments. +struct AddAttachmentsView: View { + @ObservedObject var model: ComposeModel + @State private var attachFile = false + @State private var imageAttachment: UIImage? + + // Two sheet states possible: full screen preview OR image picker + @State private var sheetState: SheetState? + + // TODO: image import and preview are currently not fully supported + var body: some View { + ScrollView(.horizontal, showsIndicators: false) { + HStack { + // both upload buttons + VStack { + attachFileButton + Spacer() + imageUploadButton + .padding(.bottom, 3) + } + + filePreviews + } + .padding(4) + // sheet used for image import OR full screen attachment preview + .frame(maxHeight: 141) + } + .sheet(item: $sheetState) { state in + switch state { + case .imagePicker: + ImagePicker(image: $imageAttachment) + case let .fullScreenPreview(attachment): + Text(attachment.myName) + .font(Font.body.weight(.semibold)) + QuickLookView(name: attachment.myName, + data: attachment.myData, + shallDL: false) + .aspectRatio(100/141, contentMode: .fit) + } + } + } + + /// a view that contains the upload button for files + var attachFileButton: some View { + Button { + attachFile = true + } label: { + Image(systemName: "doc.badge.plus").font(.system(size: 50)) + } + // file import when button is pressed + .fileImporter(isPresented: $attachFile, allowedContentTypes: [.plainText, .image, .pdf]) { + res in + do { + // get fileURL of selected file + let fileURL = try res.get() + // get fileName from URL of that file + let fileName = fileURL.lastPathComponent + // get file data + let fileData = try Data(contentsOf: fileURL) + + // create Attachment and add in to the attachments list + let newAttachment = Attachment(myName: fileName, myData: fileData) + model.attachments.append(newAttachment) + } catch { + // Error while loading file + print("Error while importing file.") + } + } + } + + /// a view that contains the upload button for pictures + var imageUploadButton: some View { + Button { + sheetState = .imagePicker + } label: { + // try to match SF Symbol "doc.badge.plus" with "photo" + ZStack(alignment: .bottomLeading) { + Image(systemName: "photo").font(.system(size: 50)) + + Image(systemName: "plus") + .font(Font.system(size: 19, weight: .bold)) + .foregroundColor(Color(.systemBackground)) + .padding(3) + .background(Circle().fill(Color.accentColor)) + .padding(3) + .background(Circle().fill(Color(.systemBackground))) + .offset(x: -8, y: 8) + } + } + } + + /// a view that contains several file previews together with their delete buttons + var filePreviews: some View { + ForEach(model.attachments.reversed()) { attachment in + ZStack { + // file preview using Quicklook + // shallDL has to be true here, because QuickLookView will download + // the file into the Documents Directory for us + // it then uses that DD file to create a preview of the content + QuickLookView(name: attachment.myName, data: attachment.myData, shallDL: true) + .padding(3) + .background(RoundedRectangle(cornerRadius: 5) + .stroke(Color.secondary, lineWidth: 2)) + + // a window that disables the interaction with QuickLookView + Button { + sheetState = .fullScreenPreview(attachment) + } label: { + Rectangle().fill(Color(.systemBackground).opacity(0.1)) + } + + VStack(alignment: .trailing) { + // delete button in upper right corner + Button { + // remove from attachments list and remove copy from Documents Directory + // to keep DD clean + if let deleteIndex = model.attachments.firstIndex(of: attachment) { + model.attachments[deleteIndex].removeFileFromDocumentsDirectory() + model.attachments.remove(at: deleteIndex) + } + } label: { + Image(systemName: "xmark").font(.system(size: 20)) + } + .padding(5) + + Spacer() + // display file name and size + Text(attachment.myName + ", " + attachment.countData()) + .lineLimit(1) + .font(.caption) + .foregroundColor(.secondary) + .truncationMode(.middle) + .frame(maxWidth: .infinity) + .padding(3) + .background(RoundedRectangle(cornerRadius: 5) + .fill(Color(.systemBackground).opacity(0.8))) + } + .frame(maxWidth: .infinity) + } + .frame(width: 100) + } + } + + /// States to control several sheet views + enum SheetState: Identifiable { + case imagePicker // user presses imageUploadButton + case fullScreenPreview(Attachment) // user taps on a file preview + + var id: Int { + switch self { + case .imagePicker: return 0 + case .fullScreenPreview: return 1 + } + } + } +} diff --git a/enzevalos_iphone/SwiftUI/Compose/Attachment.swift b/enzevalos_iphone/SwiftUI/Compose/Attachment.swift new file mode 100644 index 0000000000000000000000000000000000000000..597f86a7436ee187e2fddd294dd7c37781415204 --- /dev/null +++ b/enzevalos_iphone/SwiftUI/Compose/Attachment.swift @@ -0,0 +1,67 @@ +// +// Attachment.swift +// enzevalos_iphone +// 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 https://www.gnu.org/licenses/. +// + +import Foundation + +struct Attachment: DisplayAttachment, Identifiable, Equatable { + // DisplayAttachment + var myName: String + var myData: Data + + // Identifiable + var id = UUID() + + /// a func that removes the calling attachment`s copy from the Documents Directory + func removeFileFromDocumentsDirectory() { + /// a func that returns the URL of the Documents Directory + func getDocumentsDirectory() -> URL { + let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) + return paths[0] + } + let wholePath = getDocumentsDirectory().appendingPathComponent(self.myName) + + do { + try FileManager.default.removeItem(at: wholePath) + } catch let error as NSError { + print("Error: \(error)") + } + } + + /// a func that returns the size of the calling attachment file as a string + func countData() -> String { + // size in byte + var sizeOfData = Double(self.myData.count) + + // smaller than 1KB + if sizeOfData < 1000 { + return String(sizeOfData) + " bytes" + } + //smaller than 1MB + else if sizeOfData < 1000000 { + sizeOfData = round(sizeOfData/1000) + return String(Int(sizeOfData)) + " KB" + } + // everything bigger than 1MB + else { + sizeOfData = round(sizeOfData/1000000) + return String(Int(sizeOfData)) + " MB" + } + + } + +} + diff --git a/enzevalos_iphone/SwiftUI/Compose/ComposeHeaderView.swift b/enzevalos_iphone/SwiftUI/Compose/ComposeHeaderView.swift index 04f9be03b122c71c95488db63658c73ff61e96ec..857dac1147386835153d2f16a14dff4997cffa87 100644 --- a/enzevalos_iphone/SwiftUI/Compose/ComposeHeaderView.swift +++ b/enzevalos_iphone/SwiftUI/Compose/ComposeHeaderView.swift @@ -37,8 +37,19 @@ struct ComposeViewHeader: View { // Send button, disabled if no recipients are set. Button("Send") { - model.sendMail() - presentationMode.wrappedValue.dismiss() + // trigger an alert to attach files + // user can then decide in the alert if he wants to + // - resume sending + // - quit sending and edit again + if model.mentionsAttachments && model.attachments == [] { + model.showAttachmentAlert = true + model.resumeSend = false + } + if model.resumeSend { + model.sendMail() + presentationMode.wrappedValue.dismiss() + } + model.resumeSend = true } .disabled(model.recipientsModel.hasNoRecipients) } diff --git a/enzevalos_iphone/SwiftUI/Compose/ComposeModel.swift b/enzevalos_iphone/SwiftUI/Compose/ComposeModel.swift index 5a92c8a2f4d665fdc13bc2e4e355b483124b80e8..e935e59ff73ddace306535ebb806f0248c5b6daa 100644 --- a/enzevalos_iphone/SwiftUI/Compose/ComposeModel.swift +++ b/enzevalos_iphone/SwiftUI/Compose/ComposeModel.swift @@ -25,8 +25,12 @@ import Foundation class ComposeModel: ObservableObject { @Published var subject = "" @Published var body = "" + @Published var attachments: [Attachment] = [] @Published var encryptionOn = true @Published var recipientsModel: RecipientsModel = RecipientsModel() + // properties for the attachment check/reminder + @Published var showAttachmentAlert = false + @Published var resumeSend = true init(preData: PreMailData?) { if let preData = preData { @@ -39,6 +43,22 @@ class ComposeModel: ObservableObject { recipientsModel.parentComposeModel = self } + /// computed property checks whether an attachment is mentioned in the mail (subject or body) + var mentionsAttachments: Bool { + let germanWords = ["Anhang", "anhang", "Angehängt", "angehängt", "Angehangen", "angehangen", "Anhänge", "anhänge"] + let englishWords = ["Attachment", "attachment", "Attachments", "attachments", "Attached", "attached", "Attach", "attach"] + // TODO: more languages? + let allWords = germanWords + englishWords + return allWords.contains(where: self.body.contains) || allWords.contains(where: self.subject.contains) + } + + /// a func that deletes all the copies of the attachment files in the Documents Directory to keep it clean + func removeAttachmentCopiesFromDocumentsDirectory() { + for current in attachments { + current.removeFileFromDocumentsDirectory() + } + } + // TODO: Add security state functionality /// Generates mail and sends it. @@ -61,14 +81,27 @@ class ComposeModel: ObservableObject { /// /// - Returns: Outgoing mail filled out with relevant information from RecipientsModel. private func generateMail() -> OutgoingMail { - OutgoingMail(toAddresses: recipientsModel.toEMails, + // before sending the mail we need to converted all attachments to MCOAttachments + var convertedAttachments: [MCOAttachment] = [] + for current in attachments{ + do { + if let newMCOAttachment = MCOAttachment.init(data: current.myData, filename: current.myName) { + convertedAttachments.append(newMCOAttachment) + print("Converted and attached file") + } + } + } + // TODO: something seems to go wrong with sending the attachments + // has to be fixed in the future + + return OutgoingMail(toAddresses: recipientsModel.toEMails, ccAddresses: recipientsModel.ccEMails, bccAddresses: recipientsModel.bccEMails, subject: subject, textContent: body, htmlContent: nil, textparts: 1, - sendEncryptedIfPossible: encryptionOn, - attachments: []) + sendEncryptedIfPossible: !encryptionOn, + attachments: convertedAttachments) } } diff --git a/enzevalos_iphone/SwiftUI/Compose/ComposeView.swift b/enzevalos_iphone/SwiftUI/Compose/ComposeView.swift index f1d63407e6db9ccb98bbaaf85c7dd8bab310c7cf..69fcd684bdb276f1c31ac18c2412c769b88dc3c3 100644 --- a/enzevalos_iphone/SwiftUI/Compose/ComposeView.swift +++ b/enzevalos_iphone/SwiftUI/Compose/ComposeView.swift @@ -22,8 +22,13 @@ import SwiftUI /// A view used to compose and send an email. struct ComposeView: View { - @State private var cc = "" + @Environment(\.presentationMode) var presentationMode @ObservedObject var model: ComposeModel + // properties regarding the RecipientFields + @State private var cc = "" + // properties regarding attachments + // used to control the floating action button + @State private var showAttachments = false /// - Parameter preData: Data of email to reply to or forward. init(preData: PreMailData? = nil) { @@ -58,12 +63,70 @@ struct ComposeView: View { Divider() - // Email body - TextEditor(text: $model.body) + // Email body with attachment floating action button + ZStack(alignment: Alignment(horizontal: .trailing, vertical: .top)) { + + TextEditor(text: $model.body) + + // floating action button + Button ( + action: {showAttachments.toggle()}, + label: { + Image(systemName: "paperclip") + } + ) + .frame(alignment: .topLeading) + } + + // optional upload attachments section + if showAttachments { + VStack { + Divider() + AddAttachmentsView(model: model) + } + .transition(.move(edge: .bottom)) + } + } + // show this alert when user sends the mail without attachments, + // but mentions them in the mail + .alert(isPresented: $model.showAttachmentAlert) { + AttachmentAlert } .padding() .animation(.easeInOut) .ignoresSafeArea(edges: /*@START_MENU_TOKEN@*/.bottom/*@END_MENU_TOKEN@*/) + .onDisappear{ + // when the ComposeView is closed, we make sure to + // clean the Documents Directory from all the remaining + // copies of attachment files + model.removeAttachmentCopiesFromDocumentsDirectory() + } + } + } + + /// an alert which is shown when the user mentions attachments in the mail, but sends it without attaching anything + var AttachmentAlert: Alert { + Alert( + title: Text("Attachment.Alert.Title"), + message: Text("Attachment.Alert.Text"), + primaryButton: .destructive(Text("Attachment.Alert.PrimaryButton").foregroundColor(Color.blue)) { + // send the mail anyway + model.sendMail() + // remove the attachments from the Documents Directory + // to keep the DD clean + model.removeAttachmentCopiesFromDocumentsDirectory() + presentationMode.wrappedValue.dismiss() + }, + secondaryButton: .destructive(Text("Attachment.Alert.SecondaryButton").foregroundColor(Color.blue)) { + // quit sending, enable further editing + } + ) + } + + /// a func that deletes all the copies of the attachment files in the Documents Directory to keep it clean + func removeAttachmentCopiesFromDocumentsDirectory() { + for current in model.attachments { + current.removeFileFromDocumentsDirectory() } } } diff --git a/enzevalos_iphone/SwiftUI/Compose/ImagePicker.swift b/enzevalos_iphone/SwiftUI/Compose/ImagePicker.swift new file mode 100644 index 0000000000000000000000000000000000000000..84549015d84434f74da7d035bd7888f2056aaf71 --- /dev/null +++ b/enzevalos_iphone/SwiftUI/Compose/ImagePicker.swift @@ -0,0 +1,54 @@ +// +// ImagePicker.swift +// enzevalos_iphone +// 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 https://www.gnu.org/licenses/. +// +/// This file is a SwiftUI wrapper for UIImagePickerController +/// it can be used it to attach pictures to a mail and preview them +import SwiftUI + +struct ImagePicker: UIViewControllerRepresentable { + @Environment(\.presentationMode) var presentationMode + @Binding var image: UIImage? + + class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate { + let parent: ImagePicker + + init(_ parent: ImagePicker) { + self.parent = parent + } + + func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) { + if let uiImage = info[.originalImage] as? UIImage { + parent.image = uiImage + } + + parent.presentationMode.wrappedValue.dismiss() + } + } + + func makeCoordinator() -> Coordinator { + Coordinator(self) + } + + func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController { + let picker = UIImagePickerController() + return picker + } + + func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePicker>) { + + } +} + diff --git a/enzevalos_iphone/SwiftUI/Inbox/InboxView.swift b/enzevalos_iphone/SwiftUI/Inbox/InboxView.swift index a062bd5a7b249f0b4c7e19b02e5b194e23f9ccfe..ea97ae014874534e7b370d40c28a3a53aed2a987 100644 --- a/enzevalos_iphone/SwiftUI/Inbox/InboxView.swift +++ b/enzevalos_iphone/SwiftUI/Inbox/InboxView.swift @@ -54,6 +54,7 @@ struct InboxView: View { } } + private var mailListView: some View { MailListView(folderPath: folderPath, folderName: folderName, refreshModel: refreshModel) .environment(\.managedObjectContext, diff --git a/enzevalos_iphone/de.lproj/Localizable.strings b/enzevalos_iphone/de.lproj/Localizable.strings index ebcad85c88877968e804b18beaee20b49c11ca1e..70b5737d4c5cc5da42447712d605ac4de0ad9496 100644 --- a/enzevalos_iphone/de.lproj/Localizable.strings +++ b/enzevalos_iphone/de.lproj/Localizable.strings @@ -30,6 +30,10 @@ "AddressRecord.lastHeardFrom.Never" = "noch nie"; "Attach" = "Anhängen"; "Attachment" = "Anhang"; +"Attachment.Alert.Title" = "Willst du diese E-Mail wirklich absenden"; +"Attachment.Alert.Text" = "In deiner E-Mail erwähnst du Anhänge, aber der Anhang ist momentan leer."; +"Attachment.Alert.PrimaryButton" = "Senden"; +"Attachment.Alert.SecondaryButton" = "E-Mail bearbeiten"; "Authentification" = "Authentifizierung"; "Back" = "Zurück"; "Bcc" = "Bcc"; diff --git a/enzevalos_iphone/en.lproj/Localizable.strings b/enzevalos_iphone/en.lproj/Localizable.strings index 5a4c6debdd34161867486764f5aadc5dce20d2af..85817114fdbbc7f0528881bf7d0902d2b7abcde0 100644 --- a/enzevalos_iphone/en.lproj/Localizable.strings +++ b/enzevalos_iphone/en.lproj/Localizable.strings @@ -30,6 +30,10 @@ "AddressRecord.lastHeardFrom.Never" = "never"; "Attach" = "Attach"; "Attachment" = "Attachment"; +"Attachment.Alert.Title" = "Are you sure you want to send this mail?"; +"Attachment.Alert.Text" = "In your mail you mentioned attachments, but you did not attach any."; +"Attachment.Alert.PrimaryButton" = "Send"; +"Attachment.Alert.SecondaryButton" = "Edit Mail"; "Authentification" = "Authentification"; "Back" = "Back"; "Bcc" = "Bcc";