aboutsummaryrefslogtreecommitdiff
path: root/Hotline/State
diff options
context:
space:
mode:
authorDustin Mierau <dustin@mierau.me>2025-11-10 21:00:43 -0800
committerDustin Mierau <dustin@mierau.me>2025-11-10 21:00:43 -0800
commitddb9c69b24a67ac140af9ff20f5c36bdef6fb51b (patch)
tree0bb994f1ac7a608c90b33c402629b0c8b21caff4 /Hotline/State
parenta4263aea6e2875fa77783685985e5c8f7991337f (diff)
Remove "New" suffix from our transfer clients. Further cleanup. Allow previewing certain files with HFS types (but no extensions). Cleanup preview files when window closes.
Diffstat (limited to 'Hotline/State')
-rw-r--r--Hotline/State/AppUpdate.swift9
-rw-r--r--Hotline/State/FilePreviewState.swift153
-rw-r--r--Hotline/State/HotlineState.swift22
-rw-r--r--Hotline/State/ServerState.swift2
4 files changed, 84 insertions, 102 deletions
diff --git a/Hotline/State/AppUpdate.swift b/Hotline/State/AppUpdate.swift
index 601859e..558a296 100644
--- a/Hotline/State/AppUpdate.swift
+++ b/Hotline/State/AppUpdate.swift
@@ -35,8 +35,6 @@ final class AppUpdate {
case manual
}
- // MARK: - Public State
-
var isChecking = false
var isDownloading = false
var showWindow = false
@@ -55,7 +53,7 @@ final class AppUpdate {
private let remindDateKey = "update.remind.date"
private let lastPromptedVersionKey = "update.last.prompt.version"
- // MARK: - Public API
+ // MARK: - API
func checkForUpdatesOnLaunch() async {
await checkForUpdates(trigger: .automatic)
@@ -100,7 +98,7 @@ final class AppUpdate {
resetAndCloseWindow()
}
- // MARK: - Internal Logic
+ // MARK: - Implementation
private func checkForUpdates(trigger: CheckTrigger) async {
await MainActor.run {
@@ -259,8 +257,7 @@ final class AppUpdate {
private func downloadRelease(_ release: UpdateReleaseInfo) async {
do {
let (temporaryURL, _) = try await URLSession.shared.download(from: release.downloadURL)
- let downloadsDirectory = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first!
- let destinationURL = downloadsDirectory.appendingPathComponent(release.assetName)
+ let destinationURL = URL.downloadsDirectory.appendingPathComponent(release.assetName)
if FileManager.default.fileExists(atPath: destinationURL.path) {
try? FileManager.default.removeItem(at: destinationURL)
diff --git a/Hotline/State/FilePreviewState.swift b/Hotline/State/FilePreviewState.swift
index 27f8199..ec79f66 100644
--- a/Hotline/State/FilePreviewState.swift
+++ b/Hotline/State/FilePreviewState.swift
@@ -1,8 +1,6 @@
import SwiftUI
import UniformTypeIdentifiers
-// MARK: - Preview Type
-
enum FilePreviewType: Equatable {
case unknown
case image
@@ -13,13 +11,15 @@ enum FilePreviewType: Equatable {
@MainActor
@Observable
final class FilePreviewState {
- // MARK: - Properties
-
+ enum LoadState: Equatable {
+ case unloaded
+ case loading
+ case loaded
+ case failed
+ }
+
let info: PreviewFileInfo
- private var previewClient: HotlineFilePreviewClient?
- private var previewTask: Task<Void, Never>?
-
var state: LoadState = .unloaded
var progress: Double = 0.0
@@ -33,39 +33,35 @@ final class FilePreviewState {
var text: String? = nil
var styledText: NSAttributedString? = nil
-
- // MARK: - Computed Properties
+
+ @ObservationIgnored private var previewClient: HotlineFilePreviewClient?
+ @ObservationIgnored private var previewTask: Task<Void, Never>?
var previewType: FilePreviewType {
- info.previewType
+ self.info.previewType
}
- // MARK: - Initialization
-
init(info: PreviewFileInfo) {
self.info = info
}
- nonisolated deinit {
- // Note: Can't access @MainActor properties from deinit
- // Cleanup will happen when previewClient is deallocated
- }
-
- // MARK: - Public API
+ // MARK: - API
func download() {
// Cancel any existing download
- previewTask?.cancel()
- previewClient?.cleanup()
+ self.previewTask?.cancel()
+ self.previewClient?.cleanup()
let task = Task { @MainActor in
do {
let client = HotlineFilePreviewClient(
- fileName: info.name,
- address: info.address,
- port: UInt16(info.port),
- reference: info.id,
- size: UInt32(info.size)
+ fileName: self.info.name,
+ address: self.info.address,
+ port: UInt16(self.info.port),
+ reference: self.info.id,
+ size: UInt32(self.info.size),
+ fileType: self.info.type,
+ fileCreator: self.info.creator
)
self.previewClient = client
@@ -132,69 +128,58 @@ final class FilePreviewState {
}
func cancel() {
- previewTask?.cancel()
- previewTask = nil
- previewClient?.cancel()
+ self.previewTask?.cancel()
+ self.previewTask = nil
+ self.previewClient?.cancel()
}
func cleanup() {
- previewClient?.cleanup()
- previewClient = nil
- fileURL = nil
- image = nil
- text = nil
- styledText = nil
+ self.previewClient?.cleanup()
+ self.previewClient = nil
+ self.fileURL = nil
+ self.image = nil
+ self.text = nil
+ self.styledText = nil
}
- // MARK: - Private Implementation
+ // MARK: - Utility
- private func loadPreview(from url: URL) {
- guard let data = try? Data(contentsOf: url) else {
- self.state = .failed
- print("FilePreviewState: Failed to read preview data from \(url.path)")
- return
- }
-
- switch self.previewType {
- case .image:
- #if os(iOS)
- self.image = UIImage(data: data)
- #elseif os(macOS)
- self.image = NSImage(data: data)
- #endif
-
- if self.image == nil {
- self.state = .failed
- print("FilePreviewState: Failed to create image from data")
- }
-
- case .text:
- let encoding: UInt = NSString.stringEncoding(for: data, convertedString: nil, usedLossyConversion: nil)
- if encoding != 0 {
- self.text = String(data: data, encoding: String.Encoding(rawValue: encoding))
- } else {
- self.text = String(data: data, encoding: .utf8)
- }
-
- if self.text == nil {
- self.state = .failed
- print("FilePreviewState: Failed to decode text data")
- }
-
- case .unknown:
- print("FilePreviewState: Unknown preview type for \(info.name)")
- break
- }
- }
-}
-
-// MARK: - Load State
-
-extension FilePreviewState {
- enum LoadState: Equatable {
- case unloaded
- case loading
- case loaded
- case failed
- }
+// private func loadPreview(from url: URL) {
+// guard let data = try? Data(contentsOf: url) else {
+// self.state = .failed
+// print("FilePreviewState: Failed to read preview data from \(url.path)")
+// return
+// }
+//
+// switch self.previewType {
+// case .image:
+// #if os(iOS)
+// self.image = UIImage(data: data)
+// #elseif os(macOS)
+// self.image = NSImage(data: data)
+// #endif
+//
+// if self.image == nil {
+// self.state = .failed
+// print("FilePreviewState: Failed to create image from data")
+// }
+//
+// case .text:
+// let encoding: UInt = NSString.stringEncoding(for: data, convertedString: nil, usedLossyConversion: nil)
+// if encoding != 0 {
+// self.text = String(data: data, encoding: String.Encoding(rawValue: encoding))
+// } else {
+// self.text = String(data: data, encoding: .utf8)
+// }
+//
+// if self.text == nil {
+// self.state = .failed
+// print("FilePreviewState: Failed to decode text data")
+// }
+//
+// case .unknown:
+// print("FilePreviewState: Unknown preview type for \(info.name)")
+// break
+// }
+// }
}
diff --git a/Hotline/State/HotlineState.swift b/Hotline/State/HotlineState.swift
index e80571a..f814818 100644
--- a/Hotline/State/HotlineState.swift
+++ b/Hotline/State/HotlineState.swift
@@ -252,7 +252,7 @@ class HotlineState: Equatable {
// MARK: - Private State
- @ObservationIgnored private var client: HotlineClientNew?
+ @ObservationIgnored private var client: HotlineClient?
@ObservationIgnored private var eventTask: Task<Void, Never>?
@ObservationIgnored private var chatSessionKey: ChatStore.SessionKey?
@ObservationIgnored private var restoredChatSessionKey: ChatStore.SessionKey?
@@ -308,13 +308,13 @@ class HotlineState: Equatable {
iconID: UInt16(iconID)
)
- print("HotlineState.login(): Calling HotlineClientNew.connect()...")
- let client = try await HotlineClientNew.connect(
+ print("HotlineState.login(): Calling HotlineClient.connect()...")
+ let client = try await HotlineClient.connect(
host: server.address,
port: UInt16(server.port),
login: loginInfo
)
- print("HotlineState.login(): HotlineClientNew.connect() returned")
+ print("HotlineState.login(): HotlineClient.connect() returned")
self.client = client
print("HotlineState.login(): Client stored")
@@ -865,7 +865,7 @@ class HotlineState: Equatable {
/// - progressCallback: Optional callback for progress updates (receives TransferInfo and progress 0.0-1.0)
/// - callback: Optional completion callback (receives TransferInfo and final file URL)
@MainActor
- func downloadFileNew(_ fileName: String, path: [String], to destination: URL? = nil, progress progressCallback: ((TransferInfo) -> Void)? = nil, complete callback: ((TransferInfo) -> Void)? = nil) {
+ func downloadFile(_ fileName: String, path: [String], to destination: URL? = nil, progress progressCallback: ((TransferInfo) -> Void)? = nil, complete callback: ((TransferInfo) -> Void)? = nil) {
guard let client = self.client else { return }
var fullPath: [String] = []
@@ -975,7 +975,7 @@ class HotlineState: Equatable {
/// - itemProgressCallback: Optional callback for per-item updates (receives TransferInfo with current file info)
/// - callback: Optional completion callback (receives TransferInfo and final folder URL)
@MainActor
- func downloadFolderNew(
+ func downloadFolder(
_ folderName: String,
path: [String],
to destination: URL? = nil,
@@ -1018,7 +1018,7 @@ class HotlineState: Equatable {
AppState.shared.addTransfer(transfer)
// Create download client
- let downloadClient = HotlineFolderDownloadClientNew(
+ let downloadClient = HotlineFolderDownloadClient(
address: address,
port: UInt16(port),
reference: referenceNumber,
@@ -1091,7 +1091,7 @@ class HotlineState: Equatable {
}
}
- /// Modern async/await folder upload using HotlineFolderUploadClientNew
+ /// Upload a folder to the server.
///
/// - Parameters:
/// - folderURL: URL to the folder on disk to upload
@@ -1155,7 +1155,7 @@ class HotlineState: Equatable {
print("HotlineState: Got folder upload reference: \(referenceNumber)")
// Create upload client
- guard let uploadClient = HotlineFolderUploadClientNew(
+ guard let uploadClient = HotlineFolderUploadClient(
folderURL: folderURL,
address: address,
port: UInt16(port),
@@ -1346,9 +1346,9 @@ class HotlineState: Equatable {
}
func setFileInfo(fileName: String, path filePath: [String], fileNewName: String?, comment: String?, encoding: String.Encoding = .utf8) {
- // TODO: Implement setFileInfo in HotlineClientNew
+ // TODO: Implement setFileInfo in HotlineClient
// This method updates file metadata (name and/or comment)
- print("setFileInfo not yet implemented in HotlineState/HotlineClientNew")
+ print("setFileInfo not yet implemented in HotlineState/HotlineClient")
}
@MainActor
diff --git a/Hotline/State/ServerState.swift b/Hotline/State/ServerState.swift
index 805672d..5913bf5 100644
--- a/Hotline/State/ServerState.swift
+++ b/Hotline/State/ServerState.swift
@@ -29,7 +29,7 @@ enum ServerNavigationType: Identifiable, Hashable, Equatable {
case .files:
return "Files"
case .accounts:
- return "Admin"
+ return "Accounts"
case .user(let userID):
return String(userID)
}