From 3361b828ef49fa1c8ae5d23123b067a674d73a61 Mon Sep 17 00:00:00 2001 From: fmodf Date: Tue, 9 Jul 2024 14:37:51 +0200 Subject: [PATCH] wip --- .../AppCore/Actions/ConversationActions.swift | 3 + .../AppCore/Models/Attachment.swift | 22 ++++++++ .../AppCore/XMPP/XMPPService.swift | 55 +++++++++++++++++++ ConversationsClassic/Helpers/Const.swift | 19 ++++--- .../AttachmentContactsPickerView.swift | 1 + .../AttachmentMediaPickerView.swift | 34 ++++++++++-- 6 files changed, 120 insertions(+), 14 deletions(-) create mode 100644 ConversationsClassic/AppCore/Models/Attachment.swift diff --git a/ConversationsClassic/AppCore/Actions/ConversationActions.swift b/ConversationsClassic/AppCore/Actions/ConversationActions.swift index f299693..2c054d7 100644 --- a/ConversationsClassic/AppCore/Actions/ConversationActions.swift +++ b/ConversationsClassic/AppCore/Actions/ConversationActions.swift @@ -7,4 +7,7 @@ enum ConversationAction: Codable { case setReplyText(String) case showAttachmentPicker(Bool) + case sendAttachment(Attachment) + case sendAttachmentDone + case sendAttachmentError(reason: String) } diff --git a/ConversationsClassic/AppCore/Models/Attachment.swift b/ConversationsClassic/AppCore/Models/Attachment.swift new file mode 100644 index 0000000..137e8cd --- /dev/null +++ b/ConversationsClassic/AppCore/Models/Attachment.swift @@ -0,0 +1,22 @@ +import Foundation +import GRDB +import Martin +import SwiftUI + +enum AttachmentType: Stateable { + case movie + case image + case audio + case file + case location + case contact +} + +struct Attachment: Stateable { + let id: String + let type: AttachmentType + let url: URL? + let data: [Data]? + let str: String? + let localPath: URL? +} diff --git a/ConversationsClassic/AppCore/XMPP/XMPPService.swift b/ConversationsClassic/AppCore/XMPP/XMPPService.swift index 2386227..360ea12 100644 --- a/ConversationsClassic/AppCore/XMPP/XMPPService.swift +++ b/ConversationsClassic/AppCore/XMPP/XMPPService.swift @@ -126,3 +126,58 @@ final class XMPPService: ObservableObject { } } } + +// open class HTTPFileUploadHelper { +// +// private static let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "HTTPFileUploadHelper") +// +// public static func upload(for context: Context, filename: String, inputStream: InputStream, filesize size: Int, mimeType: String, delegate: URLSessionDelegate?, completionHandler: @escaping (Result)->Void) { +// let httpUploadModule = context.module(.httpFileUpload); +// httpUploadModule.findHttpUploadComponent(completionHandler: { result in +// switch result { +// case .success(let components): +// guard let component = components.first(where: { $0.maxSize > size }) else { +// completionHandler(.failure(.fileTooBig)); +// return; +// } +// httpUploadModule.requestUploadSlot(componentJid: component.jid, filename: filename, size: size, contentType: mimeType, completionHandler: { result in +// switch result { +// case .success(let slot): +// var request = URLRequest(url: slot.putUri); +// slot.putHeaders.forEach({ (k,v) in +// request.addValue(v, forHTTPHeaderField: k); +// }); +// request.httpMethod = "PUT"; +// request.httpBodyStream = inputStream; +// request.addValue(String(size), forHTTPHeaderField: "Content-Length"); +// request.addValue(mimeType, forHTTPHeaderField: "Content-Type"); +// let session = URLSession(configuration: URLSessionConfiguration.default, delegate: delegate, delegateQueue: OperationQueue.main); +// session.dataTask(with: request) { (data, response, error) in +// let code = (response as? HTTPURLResponse)?.statusCode ?? 500; +// guard error == nil && (code == 200 || code == 201) else { +// logger.error("upload of file \(filename) failed, error: \(error as Any), response: \(response as Any)"); +// completionHandler(.failure(.httpError)); +// return; +// } +// if code == 200 { +// completionHandler(.failure(.invalidResponseCode(url: slot.getUri))); +// } else { +// completionHandler(.success(slot.getUri)); +// } +// }.resume(); +// case .failure(let error): +// logger.error("upload of file \(filename) failed, upload component returned error: \(error as Any)"); +// completionHandler(.failure(.unknownError)); +// } +// }); +// case .failure(let error): +// completionHandler(.failure(error.errorCondition == .item_not_found ? .notSupported : .unknownError)); +// } +// }) +// } +// +// public enum UploadResult { +// case success(url: URL, filesize: Int, mimeType: String?) +// case failure(ShareError) +// } +// } diff --git a/ConversationsClassic/Helpers/Const.swift b/ConversationsClassic/Helpers/Const.swift index 1e6e4e7..02c7b53 100644 --- a/ConversationsClassic/Helpers/Const.swift +++ b/ConversationsClassic/Helpers/Const.swift @@ -1,14 +1,14 @@ import Foundation enum Const { - // Network - #if DEBUG - static let baseUrl = "staging.some.com/api" - #else - static let baseUrl = "prod.some.com/api" - #endif - static let requestTimeout = 15.0 - static let networkLogging = true + // // Network + // #if DEBUG + // static let baseUrl = "staging.some.com/api" + // #else + // static let baseUrl = "prod.some.com/api" + // #endif + // static let requestTimeout = 15.0 + // static let networkLogging = true // App static var appVersion: String { @@ -27,4 +27,7 @@ enum Const { case narayana = "narayana.im" case conversations = "conversations.im" } + + // Upload/download file folder + static let fileFolder = "ConversationsClassic" } diff --git a/ConversationsClassic/View/Screens/Attachments/AttachmentContactsPickerView.swift b/ConversationsClassic/View/Screens/Attachments/AttachmentContactsPickerView.swift index 63791e9..57c0522 100644 --- a/ConversationsClassic/View/Screens/Attachments/AttachmentContactsPickerView.swift +++ b/ConversationsClassic/View/Screens/Attachments/AttachmentContactsPickerView.swift @@ -1,6 +1,7 @@ import SwiftUI struct AttachmentContactsPickerView: View { + @EnvironmentObject var store: AppStore @State private var selectedContact: Roster? var body: some View { diff --git a/ConversationsClassic/View/Screens/Attachments/AttachmentMediaPickerView.swift b/ConversationsClassic/View/Screens/Attachments/AttachmentMediaPickerView.swift index 48bf788..3f619fe 100644 --- a/ConversationsClassic/View/Screens/Attachments/AttachmentMediaPickerView.swift +++ b/ConversationsClassic/View/Screens/Attachments/AttachmentMediaPickerView.swift @@ -95,9 +95,15 @@ struct AttachmentMediaPickerView: View { } } .fullScreenCover(isPresented: $showCameraPicker) { - CameraPicker(sourceType: .camera) { _ in - // TODO: Send captures photo/video - print("Image captured") + CameraPicker(sourceType: .camera) { data, type in + store.dispatch(.conversationAction(.sendAttachment(.init( + id: UUID().uuidString, + type: type, + url: nil, + data: [data], + str: nil, + localPath: nil + )))) showCameraPicker = false } .edgesIgnoringSafeArea(.all) @@ -355,7 +361,7 @@ struct CameraView: UIViewRepresentable { struct CameraPicker: UIViewControllerRepresentable { var sourceType: UIImagePickerController.SourceType - var completionHandler: (UIImage) -> Void + var completionHandler: (Data, AttachmentType) -> Void func makeUIViewController(context: Context) -> UIImagePickerController { let picker = UIImagePickerController() @@ -382,8 +388,24 @@ struct CameraPicker: UIViewControllerRepresentable { } func imagePickerController(_: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) { - if let image = info[.originalImage] as? UIImage { - parent.completionHandler(image) + if let mediaType = info[.mediaType] as? UTType { + switch mediaType { + case .image: + if let image = info[.originalImage] as? UIImage { + let data = image.jpegData(compressionQuality: 1.0) ?? Data() + parent.completionHandler(data, .image) + } + + case .movie: + if let url = info[.mediaURL] as? URL { + let data = try? Data(contentsOf: url) + parent.completionHandler(data ?? Data(), .movie) + } + parent.completionHandler(Data(), .movie) + + default: + break + } } } }