import UIKit import Models import Shared import XXModels import Foundation extension Session { public func send(imageData: Data, to contact: Contact, completion: @escaping (Result<Void, Error>) -> Void) { client.bindings.compress(image: imageData) { [weak self] result in guard let self = self else { completion(.success(())) return } switch result { case .success(let compressedImage): do { let url = try FileManager.store( data: compressedImage, name: "image_\(Date.asTimestamp)", type: "jpeg" ) self.sendFile(url: url, to: contact) completion(.success(())) } catch { completion(.failure(error)) } case .failure(let error): completion(.failure(error)) log(string: "Error when compressing image: \(error.localizedDescription)", type: .error) } } } public func sendFile(url: URL, to contact: Contact) { guard let manager = client.transferManager else { fatalError("A transfer manager was not created") } DispatchQueue.global().async { [weak self] in guard let self = self else { return } var tid: Data? do { tid = try manager.uploadFile(url: url, to: contact.id) { completed, send, arrived, total, error in guard let tid = tid else { return } if completed { self.endTransferWith(tid: tid) } else { if error != nil { self.failTransferWith(tid: tid) } else { self.progressTransferWith(tid: tid, arrived: Float(arrived), total: Float(total)) } } } guard let tid = tid else { return } let content = url.pathExtension == "m4a" ? "a voice message" : "an image" let transfer = FileTransfer( id: tid, contactId: contact.id, name: url.deletingPathExtension().lastPathComponent, type: url.pathExtension, data: try? Data(contentsOf: url), progress: 0.0, isIncoming: false, createdAt: Date() ) _ = try? self.dbManager.saveFileTransfer(transfer) let message = Message( networkId: nil, senderId: self.client.bindings.myId, recipientId: contact.id, groupId: nil, date: Date(), status: .sending, isUnread: false, text: "You sent \(content)", replyMessageId: nil, roundURL: nil, fileTransferId: tid ) _ = try? self.dbManager.saveMessage(message) } catch { print(error.localizedDescription) } } } public func send(_ payload: Payload, toContact contact: Contact) { var message = Message( networkId: nil, senderId: client.bindings.myId, recipientId: contact.id, groupId: nil, date: Date(), status: .sending, isUnread: false, text: payload.text, replyMessageId: payload.reply?.messageId, roundURL: nil, fileTransferId: nil ) do { message = try dbManager.saveMessage(message) send(message: message) } catch { log(string: error.localizedDescription, type: .error) } } public func retryMessage(_ id: Int64) { if var message = try? dbManager.fetchMessages(.init(id: [id])).first { message.status = .sending message.date = Date() if let message = try? dbManager.saveMessage(message) { if let _ = message.recipientId { send(message: message) } else { send(groupMessage: message) } } } } func send(message: Message) { var message = message var reply: Reply? if let replyId = message.replyMessageId, let replyMessage = try? dbManager.fetchMessages(Message.Query(networkId: replyId)).first { reply = Reply(messageId: replyId, senderId: replyMessage.senderId) } let payloadData = Payload(text: message.text, reply: reply).asData() DispatchQueue.global().async { [weak self] in guard let self = self else { return } switch self.client.bindings.send(payloadData, to: message.recipientId!) { case .success(let report): message.roundURL = report.roundURL self.client.bindings.listen(report: report.marshalled) { result in switch result { case .success(let status): switch status { case .failed: message.status = .sendingFailed case .sent: message.status = .sent case .timedout: message.status = .sendingTimedOut } case .failure: message.status = .sendingFailed } message.networkId = report.uniqueId message.date = Date.fromTimestamp(Int(report.timestamp)) DispatchQueue.main.async { do { _ = try self.dbManager.saveMessage(message) } catch { log(string: error.localizedDescription, type: .error) } } } case .failure(let error): message.status = .sendingFailed log(string: error.localizedDescription, type: .error) } DispatchQueue.main.async { do { _ = try self.dbManager.saveMessage(message) } catch { log(string: error.localizedDescription, type: .error) } } } } private func endTransferWith(tid: Data) { guard let manager = client.transferManager else { fatalError("A transfer manager was not created") } try? manager.endTransferUpload(with: tid) if var message = try? dbManager.fetchMessages(.init(fileTransferId: tid)).first { message.status = .sent _ = try? dbManager.saveMessage(message) } if var transfer = try? dbManager.fetchFileTransfers(.init(id: [tid])).first { transfer.progress = 1.0 _ = try? dbManager.saveFileTransfer(transfer) } } private func failTransferWith(tid: Data) { if var message = try? dbManager.fetchMessages(.init(fileTransferId: tid)).first { message.status = .sendingFailed _ = try? dbManager.saveMessage(message) } } private func progressTransferWith(tid: Data, arrived: Float, total: Float) { if var transfer = try? dbManager.fetchFileTransfers(.init(id: [tid])).first { transfer.progress = arrived/total _ = try? dbManager.saveFileTransfer(transfer) } } func handle(incomingTransfer transfer: FileTransfer) { guard let manager = client.transferManager else { fatalError("A transfer manager was not created") } let content = transfer.type == "m4a" ? "a voice message" : "an image" var message = try! dbManager.saveMessage( Message( networkId: nil, senderId: transfer.contactId, recipientId: myId, groupId: nil, date: transfer.createdAt, status: .receiving, isUnread: true, text: "Sent you \(content)", replyMessageId: nil, roundURL: nil, fileTransferId: transfer.id ) ) try! manager.listenDownloadFromTransfer(with: transfer.id) { completed, arrived, total, error in if let error = error { print(error.localizedDescription) return } if completed { guard let rawFile = try? manager.downloadFileFromTransfer(with: transfer.id) else { return } _ = try! FileManager.store(data: rawFile, name: transfer.name, type: transfer.type) var transfer = transfer transfer.data = rawFile transfer.progress = 1.0 _ = try? self.dbManager.saveFileTransfer(transfer) message.status = .received _ = try? self.dbManager.saveMessage(message) } else { self.progressTransferWith(tid: transfer.id, arrived: Float(arrived), total: Float(total)) } } } }