aboutsummaryrefslogtreecommitdiff
path: root/Hotline/Models
diff options
context:
space:
mode:
authorDustin Mierau <dustin@mierau.me>2024-01-05 19:46:13 -0800
committerDustin Mierau <dustin@mierau.me>2024-01-05 19:46:37 -0800
commit2f8f71d0410f59d5d303a4c221a628630168bfe2 (patch)
tree6a18ae2e4dbd72a688667bd9e819ecee72d76b52 /Hotline/Models
parent57c33eb798f6802fe5af8fcd3d1992a4b63a448f (diff)
Add image preview to Files so you don't have to download images to view them, works with animated GIFs too. Added cmd-R shortcut for Servers window. More work on login view.
Diffstat (limited to 'Hotline/Models')
-rw-r--r--Hotline/Models/FileInfo.swift11
-rw-r--r--Hotline/Models/FilePreview.swift110
-rw-r--r--Hotline/Models/Hotline.swift98
-rw-r--r--Hotline/Models/PreviewFileInfo.swift20
-rw-r--r--Hotline/Models/Server.swift33
5 files changed, 212 insertions, 60 deletions
diff --git a/Hotline/Models/FileInfo.swift b/Hotline/Models/FileInfo.swift
index 9caa976..0cd9b2b 100644
--- a/Hotline/Models/FileInfo.swift
+++ b/Hotline/Models/FileInfo.swift
@@ -1,4 +1,5 @@
import SwiftUI
+import UniformTypeIdentifiers
@Observable class FileInfo: Identifiable, Hashable {
let id: UUID
@@ -15,6 +16,16 @@ import SwiftUI
var expanded: Bool = false
var children: [FileInfo]? = nil
+ var isImage: Bool {
+ let fileExtension = (self.name as NSString).pathExtension
+ if let fileType = UTType(filenameExtension: fileExtension) {
+ if fileType.isSubtype(of: .image) {
+ return true
+ }
+ }
+ return false
+ }
+
init(hotlineFile: HotlineFile) {
self.id = UUID()
self.path = hotlineFile.path
diff --git a/Hotline/Models/FilePreview.swift b/Hotline/Models/FilePreview.swift
index 552ccff..a7b183f 100644
--- a/Hotline/Models/FilePreview.swift
+++ b/Hotline/Models/FilePreview.swift
@@ -1,41 +1,73 @@
import SwiftUI
+import UniformTypeIdentifiers
-//struct FilePreview: Identifiable, Hashable {
-// let id: UUID
-//
-// let path: [String]
-// let name: String
-//
-// let type: String
-// let creator: String
-// let fileSize: UInt
-//
-// let isFolder: Bool
-// let isUnavailable: Bool
-// var expanded: Bool = false
-// var children: [FileInfo]? = nil
-//
-// init(hotlineFile: HotlineFile) {
-// self.id = UUID()
-// self.path = hotlineFile.path
-// self.name = hotlineFile.name
-// self.type = hotlineFile.type
-// self.creator = hotlineFile.creator
-// self.fileSize = UInt(hotlineFile.fileSize)
-// self.isFolder = hotlineFile.isFolder
-// self.isUnavailable = (!self.isFolder && (self.fileSize == 0))
-//
-// print(self.name, self.type, self.creator, self.isUnavailable)
-// if self.isFolder {
-// self.children = []
-// }
-// }
-//
-// static func == (lhs: FileInfo, rhs: FileInfo) -> Bool {
-// return lhs.id == rhs.id
-// }
-//
-// func hash(into hasher: inout Hasher) {
-// hasher.combine(self.id)
-// }
-//}
+enum FilePreviewState: Equatable {
+ case unloaded
+ case loading
+ case loaded
+ case failed
+}
+
+@Observable
+final class FilePreview: HotlineFileClientDelegate {
+ @ObservationIgnored let info: PreviewFileInfo
+ @ObservationIgnored var client: HotlineFileClient? = nil
+
+ var state: FilePreviewState = .unloaded
+ var progress: Double = 0.0
+
+ #if os(macOS)
+ var image: NSImage?
+ #endif
+
+ 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?.delegate = self
+ }
+
+ func download() {
+ self.client?.downloadToMemory()
+ }
+
+ func cancel() {
+ self.client?.cancel(deleteIncompleteFile: true)
+ }
+
+ func hotlineFileStatusChanged(client: HotlineFileClient, reference: UInt32, status: HotlineFileClientStatus, timeRemaining: TimeInterval) {
+ print("FILE STATUS CHANGED", status)
+
+ switch status {
+ case .unconnected:
+ state = .unloaded
+ progress = 0.0
+ case .connecting:
+ state = .loading
+ progress = 0.0
+ case .connected:
+ state = .loading
+ progress = 0.0
+ case .progress(let p):
+ state = .loading
+ progress = p
+ case .failed(_):
+ state = .failed
+ progress = 0.0
+ case .completed:
+ state = .loaded
+ progress = 1.0
+ }
+ }
+
+ func hotlineFileDownloadedData(client: HotlineFileClient, reference: UInt32, data: Data) {
+ self.state = .loaded
+
+ let fileExtension = (info.name as NSString).pathExtension
+ if let fileType = UTType(filenameExtension: fileExtension) {
+ if fileType.isSubtype(of: .image) {
+ self.image = NSImage(data: data)
+ }
+ }
+ }
+}
diff --git a/Hotline/Models/Hotline.swift b/Hotline/Models/Hotline.swift
index 3e3bf26..e88c267 100644
--- a/Hotline/Models/Hotline.swift
+++ b/Hotline/Models/Hotline.swift
@@ -1,6 +1,7 @@
import SwiftUI
-@Observable final class Hotline: HotlineClientDelegate, HotlineFileClientDelegate {
+@Observable
+final class Hotline: HotlineClientDelegate, HotlineFileClientDelegate {
let trackerClient: HotlineTrackerClient
let client: HotlineClient
@@ -239,13 +240,15 @@ import SwiftUI
self.trackerClient.disconnect()
}
- @MainActor func login(server: Server, login: String, password: String, username: String, iconID: Int, callback: ((Bool) -> Void)? = nil) {
+ @MainActor func login(server: Server, username: String, iconID: Int, callback: ((Bool) -> Void)? = nil) {
self.server = server
self.serverName = server.name
self.username = username
self.iconID = iconID
- self.client.login(server.address, port: UInt16(server.port), login: login, password: password, username: username, iconID: UInt16(iconID)) { [weak self] err, serverName, serverVersion in
+ print("CLIENT LOGIN: '\(server.login)' '\(server.password == nil)'")
+
+ self.client.login(server.address, port: UInt16(server.port), login: server.login, password: server.password, username: username, iconID: UInt16(iconID)) { [weak self] err, serverName, serverVersion in
self?.serverVersion = serverVersion
if serverName != nil {
self?.serverName = serverName
@@ -519,16 +522,21 @@ import SwiftUI
})
}
- @MainActor func previewFile(_ fileName: String, path: [String], addTransfer: Bool = false, complete callback: ((TransferInfo, Data) -> Void)? = nil) {
+ @MainActor func previewFile(_ fileName: String, path: [String], complete callback: ((PreviewFileInfo?) -> Void)? = nil) {
var fullPath: [String] = []
if path.count > 1 {
fullPath = Array(path[0..<path.count-1])
}
- self.client.sendDownloadFile(name: fileName, path: fullPath, preview: true, sent: { _ in
+ self.client.sendDownloadFile(name: fileName, path: fullPath, preview: true, sent: { success in
+ if !success {
+ callback?(nil)
+ return
+ }
}, reply: { [weak self] success, downloadReferenceNumber, downloadTransferSize, downloadFileSize, downloadWaitingCount in
guard success else {
+ callback?(nil)
return
}
@@ -539,24 +547,27 @@ import SwiftUI
print("REFERENCE NUM: \(downloadReferenceNumber.debugDescription)")
print("WAITING COUNT: \(downloadWaitingCount.debugDescription)")
+ var info: PreviewFileInfo? = nil
+
if
- let self = self,
- let address = self.server?.address,
- let port = self.server?.port,
+ let address = self?.server?.address,
+ let port = self?.server?.port,
let referenceNumber = downloadReferenceNumber,
let transferSize = downloadTransferSize {
- let fileClient = HotlineFileClient(address: address, port: UInt16(port), reference: referenceNumber, size: UInt32(transferSize), type: .preview)
- fileClient.delegate = self
- self.downloads.append(fileClient)
+ info = PreviewFileInfo(id: referenceNumber, address: address, port: port, size: transferSize, name: fileName)
- if addTransfer {
- let transfer = TransferInfo(id: referenceNumber, title: fileName, size: UInt(transferSize))
- transfer.previewCallback = callback
- self.transfers.append(transfer)
- }
+// let fileClient = HotlineFileClient(address: address, port: UInt16(port), reference: referenceNumber, size: UInt32(transferSize), type: .preview)
+// fileClient.delegate = self
+// self.downloads.append(fileClient)
- fileClient.downloadToMemory()
+// 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
@@ -567,9 +578,62 @@ import SwiftUI
// self.downloads.append(fileClient)
}
+
+ callback?(info)
})
}
+// @MainActor func previewFile(_ fileName: String, path: [String], addTransfer: Bool = false, complete callback: ((TransferInfo, Data) -> Void)? = nil) {
+// var fullPath: [String] = []
+// if path.count > 1 {
+// fullPath = Array(path[0..<path.count-1])
+// }
+//
+// self.client.sendDownloadFile(name: fileName, path: fullPath, preview: true, sent: { _ in
+//
+// }, reply: { [weak self] success, downloadReferenceNumber, downloadTransferSize, downloadFileSize, downloadWaitingCount in
+// guard success else {
+// return
+// }
+//
+// print("GOT DOWNLOAD REPLY:")
+// print("SUCCESS?", success)
+// print("TRANSFER SIZE: \(downloadTransferSize.debugDescription)")
+// print("FILE SIZE: \(downloadFileSize.debugDescription)")
+// print("REFERENCE NUM: \(downloadReferenceNumber.debugDescription)")
+// print("WAITING COUNT: \(downloadWaitingCount.debugDescription)")
+//
+// if
+// let self = self,
+// let address = self.server?.address,
+// let port = self.server?.port,
+// let referenceNumber = downloadReferenceNumber,
+// let transferSize = downloadTransferSize {
+//
+// 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)
+// }
+// })
+// }
+
@MainActor func deleteTransfer(id: UInt32) {
if let b = self.bannerClient, b.referenceNumber == id {
b.cancel()
diff --git a/Hotline/Models/PreviewFileInfo.swift b/Hotline/Models/PreviewFileInfo.swift
new file mode 100644
index 0000000..9a4c980
--- /dev/null
+++ b/Hotline/Models/PreviewFileInfo.swift
@@ -0,0 +1,20 @@
+
+struct PreviewFileInfo: Identifiable, Codable {
+ var id: UInt32
+ var address: String
+ var port: Int
+ var size: Int
+ var name: String
+}
+
+extension PreviewFileInfo: Equatable {
+ static func == (lhs: PreviewFileInfo, rhs: PreviewFileInfo) -> Bool {
+ return lhs.id == rhs.id
+ }
+}
+
+extension PreviewFileInfo: Hashable {
+ func hash(into hasher: inout Hasher) {
+ hasher.combine(self.id)
+ }
+}
diff --git a/Hotline/Models/Server.swift b/Hotline/Models/Server.swift
index 62c6867..27d45c6 100644
--- a/Hotline/Models/Server.swift
+++ b/Hotline/Models/Server.swift
@@ -4,22 +4,47 @@ struct Server: Codable {
var name: String?
var description: String?
var users: Int
+
var address: String
var port: Int
+ var login: String
+ var password: String
- init(name: String?, description: String?, address: String, port: Int = HotlinePorts.DefaultServerPort, users: Int = 0) {
+ init(name: String?, description: String?, address: String, port: Int = HotlinePorts.DefaultServerPort, users: Int = 0, login: String = "", password: String = "") {
self.name = name
self.description = description
self.address = address.lowercased()
self.port = port
self.users = users
+ self.login = login
+ self.password = password
+ }
+
+ init?(url: URL) {
+ guard url.scheme?.lowercased() == "hotline" else {
+ return nil
+ }
+
+ guard let host = url.host(percentEncoded: false) else {
+ return nil
+ }
+
+ self.name = nil
+ self.description = nil
+ self.users = 0
+
+ self.address = host.lowercased()
+ self.port = url.port ?? HotlinePorts.DefaultServerPort
+
+ self.login = url.user(percentEncoded: false) ?? ""
+ self.password = url.password(percentEncoded: false) ?? ""
}
static func parseServerAddressAndPort(_ address: String) -> (String, Int) {
let url = URL(string: "hotline://\(address)")
let port = url?.port ?? HotlinePorts.DefaultServerPort
- let host = url?.host(percentEncoded: false) ?? address
- return (host, port)
+ let host = url?.host(percentEncoded: false) ?? ""
+ return (host.lowercased().trimmingCharacters(in: .whitespacesAndNewlines), port)
}
}
@@ -29,7 +54,7 @@ extension Server: Identifiable {
extension Server: Equatable {
static func == (lhs: Server, rhs: Server) -> Bool {
- return (lhs.address == rhs.address) && (lhs.port == rhs.port)
+ return (lhs.address == rhs.address) && (lhs.port == rhs.port) && (lhs.login == rhs.login) && (lhs.password == rhs.password)
}
}