diff options
| author | Dustin Mierau <dustin@mierau.me> | 2024-07-24 12:32:41 -0700 |
|---|---|---|
| committer | Dustin Mierau <dustin@mierau.me> | 2024-07-24 12:32:41 -0700 |
| commit | 8633db7e6ca9f38345d7be0ab90193d6325904bf (patch) | |
| tree | a950663e587c0bb0b051a61882ca9df6949a2446 /Hotline/Models | |
| parent | 844317f8a3f5c7b11394653c8563c6e9c24d7e85 (diff) | |
First pass at uploads and a big refactoring of transfer clients. Also now showing download progress bar on file icon in dock (like Safari).
Diffstat (limited to 'Hotline/Models')
| -rw-r--r-- | Hotline/Models/FilePreview.swift | 16 | ||||
| -rw-r--r-- | Hotline/Models/Hotline.swift | 206 | ||||
| -rw-r--r-- | Hotline/Models/TransferInfo.swift | 1 |
3 files changed, 175 insertions, 48 deletions
diff --git a/Hotline/Models/FilePreview.swift b/Hotline/Models/FilePreview.swift index d558c4c..3960a0d 100644 --- a/Hotline/Models/FilePreview.swift +++ b/Hotline/Models/FilePreview.swift @@ -15,9 +15,9 @@ enum FilePreviewType: Equatable { } @Observable -final class FilePreview: HotlineFileClientDelegate { +final class FilePreview: HotlineFilePreviewClientDelegate { @ObservationIgnored let info: PreviewFileInfo - @ObservationIgnored var client: HotlineFileClient? = nil + @ObservationIgnored var client: HotlineFilePreviewClient? = nil var state: FilePreviewState = .unloaded var progress: Double = 0.0 @@ -49,20 +49,20 @@ final class FilePreview: HotlineFileClientDelegate { init(info: PreviewFileInfo) { self.info = info - self.client = HotlineFileClient(address: info.address, port: UInt16(info.port), reference: info.id, size: UInt32(info.size), type: .preview) + self.client = HotlineFilePreviewClient(address: info.address, port: UInt16(info.port), reference: info.id, size: UInt32(info.size)) self.client?.delegate = self } func download() { - self.client?.downloadToMemory() + self.client?.start() } func cancel() { - self.client?.cancel(deleteIncompleteFile: true) + self.client?.cancel() } - func hotlineFileStatusChanged(client: HotlineFileClient, reference: UInt32, status: HotlineFileClientStatus, timeRemaining: TimeInterval) { - print("FILE STATUS CHANGED", status) + func hotlineTransferStatusChanged(client: any HotlineTransferClient, reference: UInt32, status: HotlineTransferStatus, timeRemaining: TimeInterval) { + print("FilePreview: Download status changed:", status) switch status { case .unconnected: @@ -86,7 +86,7 @@ final class FilePreview: HotlineFileClientDelegate { } } - func hotlineFileDownloadedData(client: HotlineFileClient, reference: UInt32, data: Data) { + func hotlineFilePreviewComplete(client: HotlineFilePreviewClient, reference: UInt32, data: Data) { self.state = .loaded self.data = data diff --git a/Hotline/Models/Hotline.swift b/Hotline/Models/Hotline.swift index c57434a..69a11d3 100644 --- a/Hotline/Models/Hotline.swift +++ b/Hotline/Models/Hotline.swift @@ -1,7 +1,7 @@ import SwiftUI @Observable -class Hotline: Equatable, HotlineClientDelegate, HotlineFileClientDelegate { +class Hotline: Equatable, HotlineClientDelegate, HotlineFileDownloadClientDelegate, HotlineFilePreviewClientDelegate, HotlineFileUploadClientDelegate { let id: UUID = UUID() let trackerClient: HotlineTrackerClient let client: HotlineClient @@ -137,11 +137,11 @@ class Hotline: Equatable, HotlineClientDelegate, HotlineFileClientDelegate { var accountsLoaded: Bool = false var instantMessages: [UInt16:[InstantMessage]] = [:] var transfers: [TransferInfo] = [] - var downloads: [HotlineFileClient] = [] + var downloads: [HotlineTransferClient] = [] var unreadInstantMessages: [UInt16:UInt16] = [:] var unreadPublicChat: Bool = false - @ObservationIgnored var bannerClient: HotlineFileClient? + @ObservationIgnored var bannerClient: HotlineFilePreviewClient? #if os(macOS) var bannerImage: NSImage? = nil #elseif os(iOS) @@ -502,13 +502,15 @@ class Hotline: Equatable, HotlineClientDelegate, HotlineFileClientDelegate { if let self = self, +// let server = self.server, let address = self.server?.address, let port = self.server?.port, let referenceNumber = downloadReferenceNumber, let transferSize = downloadTransferSize { - print("DOWNLOADING TO MEMORY") - let fileClient = HotlineFileClient(address: address, port: UInt16(port), reference: referenceNumber, size: UInt32(transferSize), type: .file) + let fileClient = HotlineFileDownloadClient(address: address, port: UInt16(port), reference: referenceNumber, size: UInt32(transferSize)) +// let previewClient = HotlineFilePreviewClient(server: self.server, reference: referenceNumber, size: UInt32(transferSize), type: .fileDownload) +// let fileClient = HotlineFileClient(address: address, port: UInt16(port), reference: referenceNumber, size: UInt32(transferSize), type: .fileDownload) fileClient.delegate = self self.downloads.append(fileClient) @@ -516,7 +518,7 @@ class Hotline: Equatable, HotlineClientDelegate, HotlineFileClientDelegate { transfer.downloadCallback = callback self.transfers.append(transfer) - fileClient.downloadToFile() + fileClient.start() } } } @@ -542,8 +544,7 @@ class Hotline: Equatable, HotlineClientDelegate, HotlineFileClientDelegate { let referenceNumber = downloadReferenceNumber, let transferSize = downloadTransferSize { - print("DOWNLOADING TO MEMORY") - let fileClient = HotlineFileClient(address: address, port: UInt16(port), reference: referenceNumber, size: UInt32(transferSize), type: .file) + let fileClient = HotlineFileDownloadClient(address: address, port: UInt16(port), reference: referenceNumber, size: UInt32(transferSize)) fileClient.delegate = self self.downloads.append(fileClient) @@ -552,10 +553,80 @@ class Hotline: Equatable, HotlineClientDelegate, HotlineFileClientDelegate { transfer.progressCallback = progressCallback self.transfers.append(transfer) - fileClient.downloadToFile(to: fileURL) + fileClient.start(to: fileURL) } } } + + @MainActor func uploadFile(url fileURL: URL, path: [String], complete callback: ((TransferInfo) -> Void)? = nil) { + let fileName = fileURL.lastPathComponent + + guard fileURL.isFileURL, !fileName.isEmpty else { + print("NOT A FILE URL?") + return + } + + let filePath = fileURL.path(percentEncoded: false) + + var fileIsDirectory: ObjCBool = false + guard FileManager.default.fileExists(atPath: filePath, isDirectory: &fileIsDirectory), + fileIsDirectory.boolValue == false else { + print("FILE IS A DIRECTORY?") + return + } + + var fileSize: UInt = 0 + + // Data size + let fileAttributes = try? FileManager.default.attributesOfItem(atPath: filePath) + if let sizeAttribute = fileAttributes?[.size] as? NSNumber { + print("DATA SIZE \(sizeAttribute.uintValue)") + fileSize += sizeAttribute.uintValue + } + + // Resource size + let resourceURL = fileURL.appendingPathComponent("..namedfork/rsrc") + + + print("RESOURCE PATH \(resourceURL)") + let resourceAttributes = try? FileManager.default.attributesOfItem(atPath: resourceURL.path(percentEncoded: false)) + if let sizeAttribute = resourceAttributes?[.size] as? NSNumber { + print("RESOURCE SIZE \(sizeAttribute.uintValue)") + fileSize += sizeAttribute.uintValue + } + + print("FILE SIZE? \(fileSize)") + + guard fileSize > 0 else { + print("FILE IS EMPTY??") + return + } + + print("FILE SIZE: \(fileSize) NAME: \(fileName) PATH: \(path)") + + self.client.sendUploadFile(name: fileName, path: path) { [weak self] success, uploadReferenceNumber in + print("UPLOAD REFERENCE: \(String(describing: uploadReferenceNumber))") + + if let self = self, + let address = self.server?.address, + let port = self.server?.port, + let referenceNumber = uploadReferenceNumber, + let fileClient = HotlineFileUploadClient(upload: fileURL, address: address, port: UInt16(port), reference: referenceNumber) { + + print("GOING TO UPLOAD") + + fileClient.delegate = self + self.downloads.append(fileClient) + + let transfer = TransferInfo(id: referenceNumber, title: fileName, size: fileSize) + transfer.uploadCallback = callback + self.transfers.append(transfer) + + fileClient.start() + } + } + + } @MainActor func getFileDetails(_ fileName: String, path: [String]) async -> FileDetails? { var fullPath: [String] = [] @@ -611,27 +682,6 @@ class Hotline: Equatable, HotlineClientDelegate, HotlineFileClientDelegate { let transferSize = downloadTransferSize { info = PreviewFileInfo(id: referenceNumber, address: address, port: port, size: transferSize, name: fileName) - -// let fileClient = HotlineFileClient(address: address, port: UInt16(port), reference: referenceNumber, size: UInt32(transferSize), type: .preview) -// fileClient.delegate = self -// self.downloads.append(fileClient) - -// if addTransfer { -// let transfer = TransferInfo(id: referenceNumber, title: fileName, size: UInt(transferSize)) -// transfer.previewCallback = callback -// self.transfers.append(transfer) -// } - -// fileClient.downloadToMemory() - - print("DOWNLOADING TO MEMORY") -// fileClient.downloadToMemory({ [weak self] fileData in -// print("DOWNLOADED PREVIEW DATA", fileData?.count) -// self?.downloads.removeAll { $0.referenceNumber == referenceNumber } -// callback?(fileData != nil, fileData) -// }) - -// self.downloads.append(fileClient) } callback?(info) @@ -651,7 +701,7 @@ class Hotline: Equatable, HotlineClientDelegate, HotlineFileClientDelegate { if let i = self.downloads.firstIndex(where: { $0.referenceNumber == id }) { let fileClient = self.downloads.remove(at: i) - fileClient.cancel(deleteIncompleteFile: true) + fileClient.cancel() } } @@ -662,7 +712,7 @@ class Hotline: Equatable, HotlineClientDelegate, HotlineFileClientDelegate { self.downloads = [] for fileClient in downloads { - fileClient.cancel(deleteIncompleteFile: true) + fileClient.cancel() } } @@ -696,9 +746,9 @@ class Hotline: Equatable, HotlineClientDelegate, HotlineFileClientDelegate { let port = self.server?.port, let referenceNumber = downloadReferenceNumber, let transferSize = downloadTransferSize { - self.bannerClient = HotlineFileClient(address: address, port: UInt16(port), reference: referenceNumber, size: UInt32(transferSize), type: .preview) + self.bannerClient = HotlineFilePreviewClient(address: address, port: UInt16(port), reference: referenceNumber, size: UInt32(transferSize)) self.bannerClient?.delegate = self - self.bannerClient?.downloadToMemory() + self.bannerClient?.start() } } } @@ -856,9 +906,9 @@ class Hotline: Equatable, HotlineClientDelegate, HotlineFileClientDelegate { } - // MARK: - Hotline File Delegate + // MARK: - Hotline Transfer Delegate - func hotlineFileStatusChanged(client: HotlineFileClient, reference: UInt32, status: HotlineFileClientStatus, timeRemaining: TimeInterval) { + func hotlineTransferStatusChanged(client: HotlineTransferClient, reference: UInt32, status: HotlineTransferStatus, timeRemaining: TimeInterval) { switch status { case .unconnected: break @@ -880,7 +930,7 @@ class Hotline: Equatable, HotlineClientDelegate, HotlineFileClientDelegate { transfer.failed = true transfer.timeRemaining = 0.0 } - if let b = self.bannerClient, reference == b.referenceNumber { + if let b = self.bannerClient, b.referenceNumber == reference { b.delegate = nil self.bannerClient = nil } @@ -893,13 +943,55 @@ class Hotline: Equatable, HotlineClientDelegate, HotlineFileClientDelegate { } } - func hotlineFileReceivedInfo(client: HotlineFileClient, reference: UInt32, info: HotlineFileInfoFork) { + func hotlineFileDownloadReceivedInfo(client: HotlineFileDownloadClient, reference: UInt32, info: HotlineFileInfoFork) { if let transfer = self.transfers.first(where: { $0.id == reference }) { transfer.title = info.name } } - func hotlineFileDownloadedData(client: HotlineFileClient, reference: UInt32, data: Data) { +// func hotlineFileReceivedInfo(client: HotlineFileClient, reference: UInt32, info: HotlineFileInfoFork) { +// if let transfer = self.transfers.first(where: { $0.id == reference }) { +// transfer.title = info.name +// } +// } + +// func hotlineTransferStatusChanged(client: HotlineTransferClient, status: HotlineTransferStatus, timeRemaining: TimeInterval) { +// switch status { +// case .unconnected: +// break +// case .connecting: +// break +// case .connected: +// break +// case .progress(let progress): +// if let transfer = self.transfers.first(where: { $0.id == client.referenceNumber }) { +// transfer.progress = progress +// transfer.timeRemaining = timeRemaining +// transfer.progressCallback?(transfer, progress) +// } +// case .failed(_): +// if let i = self.downloads.firstIndex(where: { $0.referenceNumber == client.referenceNumber }) { +// self.downloads.remove(at: i) +// } +// if let transfer = self.transfers.first(where: { $0.id == client.referenceNumber }) { +// transfer.failed = true +// transfer.timeRemaining = 0.0 +// } +// if let b = self.bannerClient, reference == b.referenceNumber { +// b.delegate = nil +// self.bannerClient = nil +// } +// case .completed: +// if let transfer = self.transfers.first(where: { $0.id == client.referenceNumber }) { +// transfer.completed = true +// transfer.timeRemaining = 0.0 +// } +// break +// } +// } + + + func hotlineFilePreviewComplete(client: HotlineFilePreviewClient, reference: UInt32, data: Data) { if let b = self.bannerClient, b.referenceNumber == reference { #if os(macOS) self.bannerImage = NSImage(data: data) @@ -919,7 +1011,27 @@ class Hotline: Equatable, HotlineClientDelegate, HotlineFileClientDelegate { } } - func hotlineFileDownloadedFile(client: HotlineFileClient, reference: UInt32, at: URL) { +// func hotlineFileDownloadedData(client: HotlineFileClient, reference: UInt32, data: Data) { +// if let b = self.bannerClient, b.referenceNumber == reference { +// #if os(macOS) +// self.bannerImage = NSImage(data: data) +// #elseif os(iOS) +// self.bannerImage = UIImage(data: data) +// #endif +// } +// else +// if let i = self.transfers.firstIndex(where: { $0.id == reference }) { +// let transfer = self.transfers[i] +// transfer.previewCallback?(transfer, data) +// self.transfers.remove(at: i) +// } +// +// if let i = self.downloads.firstIndex(where: { $0.referenceNumber == reference }) { +// self.downloads.remove(at: i) +// } +// } + + func hotlineFileDownloadComplete(client: HotlineFileDownloadClient, reference: UInt32, at: URL) { if let i = self.transfers.firstIndex(where: { $0.id == reference }) { let transfer = self.transfers[i] transfer.fileURL = at @@ -934,6 +1046,20 @@ class Hotline: Equatable, HotlineClientDelegate, HotlineFileClientDelegate { } } + func hotlineFileUploadComplete(client: HotlineFileUploadClient, reference: UInt32) { + if let i = self.transfers.firstIndex(where: { $0.id == reference }) { + let transfer = self.transfers[i] + transfer.uploadCallback?(transfer) + if Prefs.shared.playSounds && Prefs.shared.playFileTransferCompleteSound { + SoundEffectPlayer.shared.playSoundEffect(.transferComplete) + } + } + + if let i = self.downloads.firstIndex(where: { $0.referenceNumber == reference }) { + self.downloads.remove(at: i) + } + } + // MARK: - Utilities func updateServerTitle() { diff --git a/Hotline/Models/TransferInfo.swift b/Hotline/Models/TransferInfo.swift index 8b8d97a..50b23f5 100644 --- a/Hotline/Models/TransferInfo.swift +++ b/Hotline/Models/TransferInfo.swift @@ -16,6 +16,7 @@ class TransferInfo: Identifiable, Equatable, Hashable { var progressCallback: ((TransferInfo, Double) -> Void)? = nil var downloadCallback: ((TransferInfo, URL) -> Void)? = nil + var uploadCallback: ((TransferInfo) -> Void)? = nil var previewCallback: ((TransferInfo, Data) -> Void)? = nil init(id: UInt32, title: String, size: UInt) { |