diff --git a/enzevalos_iphone/SwiftUI/Compose/AddAttachmentsView.swift b/enzevalos_iphone/SwiftUI/Compose/AddAttachmentsView.swift new file mode 100644 index 0000000000000000000000000000000000000000..527f1badbbc810b12c5aff76e7d9d0d45b9a4aa0 --- /dev/null +++ b/enzevalos_iphone/SwiftUI/Compose/AddAttachmentsView.swift @@ -0,0 +1,215 @@ +// +// AddAttachmentsView.swift +// enzevalos_iphone +// +// Created by Aylin Akkus on 27.03.21. +// Copyright © 2021 fu-berlin. All rights reserved. + +import SwiftUI + +struct AddAttachmentsView: View { + @ObservedObject var model: ComposeModel + + // properties regarding attaching files + // used to controll the fileUploadButton + @State var importNewFile = false + // the attachment that is supposed to be viewed fullscreen + @State var fullScreenAttachment: Attachment? + + // properties regarding attaching images + // Used to store the imported Image + @State var image: Image? + @State private var inputImage: UIImage? + + // properties to handle several sheet views (only one .sheet(...) is allowed) + @State var showSheet = false + @State var sheetState = SheetStates.none { + willSet { + showSheet = newValue != .none + } + } + + /// SheetStates to controll several sheet views + enum SheetStates { + // default + case none + // when user presses the imageUploadButton + case imageImport + // when user taps on a file preview + case fullScreenFilePreview + } + + /// a func that returns the right sheet content according to the sheetState + @ViewBuilder + private func sheetContent() -> some View { + if sheetState == .imageImport { + ImagePicker(image: self.$inputImage) + } + else if sheetState == .fullScreenFilePreview { + if let attachment = fullScreenAttachment { + QuickLookView(name: attachment.myName, data: attachment.myData, shallDL: false).aspectRatio(100/141, contentMode: .fit) + } + else { + // this should never happen because fullScreenFilePreview + // is only nil in the beginning + EmptyView() + } + } + else { + // this should never happen because the sheetState .none is + // a default + EmptyView() + } + } + + /// a func that returns the right onDismiss parameter according to the sheetState + private func sheetOnDismiss() -> (() -> Void)? { + if sheetState == .imageImport { + return { + // TODO: currently no inputImage is imported + // even if user chose one + guard let inputImage = inputImage else { + return + } + image = Image(uiImage: inputImage) + } + } + else if sheetState == .fullScreenFilePreview { + return {}// nothing + } + else { + return {}// nothing + } + } + + + + + /// a view that enables uploading an previewing files and pictures as attachments + // TODO: image import and preview are currently not fully supported + var body: some View { + ScrollView(.horizontal, showsIndicators: false) { + HStack { + // both upload buttons + VStack { + fileUploadButton + Spacer() + .frame(height: 20) + imageUploadButton + Spacer() + .frame(height: 3) + } + + filePreviews + } + .padding(4) + .sheet(isPresented: $showSheet, onDismiss: sheetOnDismiss()) { + sheetContent() + } + } + } + + /// a view that contains the upload button for files + var fileUploadButton: some View { + Button { + importNewFile.toggle() + } label: { + ZStack(alignment: Alignment(horizontal: .trailing, vertical: .top)) { + Image(systemName: "doc.badge.plus") + .font(.system(size: 50)) + } + } + // file import when button is pressed + .fileImporter(isPresented: $importNewFile, 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(fileName: fileName, fileData: fileData) + model.attachments.append(newAttachment) + } + catch { + // Error while loading file + print("Error while importing file.") + } + } // end of fileImport + } + + /// a view that contains the upload button for pictures + var imageUploadButton: some View { + Button { + showSheet.toggle() + self.sheetState = .imageImport + } label: { + // try to imitate SF Symbol "doc.badge.plus" + // with "photo" + ZStack(alignment: Alignment(horizontal: .leading, vertical: .bottom)) { + 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, id: \.self.myID) { currentFile in + ZStack(alignment: Alignment(horizontal: .trailing, vertical: .top)) { + ZStack(alignment: Alignment(horizontal: .trailing, vertical: .bottom)) { + // file preview using quicklook + QuickLookView(name: currentFile.myName, data: currentFile.myData, shallDL: false) + // resembles A4 format + .frame(width: 100, height: 141) + // creating a custom frame + .clipShape(RoundedRectangle(cornerRadius: 5)) + .padding(0.5) + .background(RoundedRectangle(cornerRadius: 5).fill(Color.gray)) + + // print file name and size + Text(currentFile.myName + ", " + currentFile.countData(attachment: currentFile)) + .lineLimit(1) + .frame(width: 90) + .font(.caption) + .foregroundColor(.secondary) + .truncationMode(.middle) + .padding(2) + .background(Color(.systemBackground).opacity(1)) + .clipShape(RoundedRectangle(cornerRadius: 5)) + } + Button { + fullScreenAttachment = currentFile + showSheet.toggle() + self.sheetState = .fullScreenFilePreview + } label: { + Image(systemName: "arrow.up.left.and.arrow.down.right") + } + .offset(x:23, y: 23) + // delete button in upper right corner + Button { + // remove from attachments list + if let deleteIndex = model.attachments.firstIndex(of: currentFile) { + model.attachments.remove(at: deleteIndex) + } + } label: { + Image(systemName: "multiply") + .font(.system(size: 20)) + } + } + } + } +} +