Skip to content
Snippets Groups Projects
Select Git revision
  • 0e26c3cfb993f07916792296bd97fb72d5b02f0c
  • main default protected
  • dev protected
  • hotfixes-oct-2022
  • refactor/avatar-cell
  • 1.1.5
  • 1.1.4
  • 1.1.3
  • 1.1
  • 1.0.8
  • 1.0.7
  • 1.0.6
12 results

Session+Chat.swift

Blame
  • Session+Chat.swift 9.15 KiB
    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) {
                    send(message: message)
                }
            }
        }
    
        private 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 = 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
            )
    
            message = try! self.dbManager.saveMessage(message)
    
            try! manager.listenDownloadFromTransfer(with: transfer.id) { completed, arrived, total, error in
                if let error = error { fatalError(error.localizedDescription) }
    
                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))
                }
            }
        }
    }