Skip to content

Commit

Permalink
fix: Refresh the browser when uploads complete (#163)
Browse files Browse the repository at this point in the history
This changes makes `TransfersModel.upload` and `TransfersModel.download`
async to allow callers to await upload and download operations.
  • Loading branch information
jbmorley authored Dec 14, 2024
1 parent 7ada54d commit 1c4963a
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 20 deletions.
13 changes: 10 additions & 3 deletions Reconnect/Model/BrowserModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,9 @@ class BrowserModel {
if file.path.isWindowsDirectory {
downloadDirectory(path: file.path, convertFiles: convertFiles)
} else {
transfersModel.download(from: file, convertFiles: convertFiles)
Task {
try? await transfersModel.download(from: file, convertFiles: convertFiles)
}
}
}
}
Expand All @@ -243,7 +245,11 @@ class BrowserModel {
if file.isDirectory {
try fileManager.createDirectory(at: destinationURL, withIntermediateDirectories: true)
} else {
self.transfersModel.download(from: file, to: destinationURL, convertFiles: convertFiles)
Task {
try? await self.transfersModel.download(from: file,
to: destinationURL,
convertFiles: convertFiles)
}
}
}
}
Expand All @@ -255,7 +261,8 @@ class BrowserModel {
guard let path = self.path else {
throw ReconnectError.invalidFilePath
}
self.transfersModel.upload(from: url, to: path + url.lastPathComponent)
try? await self.transfersModel.upload(from: url, to: path + url.lastPathComponent)
self.refresh()
}
}

Expand Down
17 changes: 12 additions & 5 deletions Reconnect/Model/Transfer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ class Transfer: Identifiable {

let id = UUID()
let item: FileReference

let action: (Transfer) async throws -> Void

var status: Status

private var task: Task<Void, Never>? = nil
Expand All @@ -89,18 +90,24 @@ class Transfer: Identifiable {
}
}

init(item: FileReference, status: Status = .waiting, action: @escaping ((Transfer) async throws -> Void) = { _ in }) {
init(item: FileReference,
status: Status = .waiting,
action: @escaping ((Transfer) async throws -> Void) = { _ in }) {
self.item = item
self.status = status
let task = Task {
self.action = action
}

func run() async throws {
self.task = Task {
do {
try await action(self)
return try await action(self)
} catch {
print("Failed with error \(error).")
self.setStatus(.failed(error))
}
}
self.task = task
await task?.value
}

func setStatus(_ status: Status) {
Expand Down
28 changes: 17 additions & 11 deletions Reconnect/Model/TransfersModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,31 +39,33 @@ class TransfersModel {
init() {
}

func download(from source: FileServer.DirectoryEntry, to destinationURL: URL? = nil, convertFiles: Bool) {
func download(from source: FileServer.DirectoryEntry,
to destinationURL: URL? = nil,
convertFiles: Bool) async throws {
let fileManager = FileManager.default
let downloadsURL = fileManager.urls(for: .downloadsDirectory, in: .userDomainMask)[0]
let filename = source.path.lastWindowsPathComponent
let downloadURL = destinationURL ?? downloadsURL.appendingPathComponent(filename)
print("Downloading file at path '\(source.path)' to destination path '\(downloadURL.path)'...")
let destinationURL = destinationURL ?? downloadsURL.appendingPathComponent(filename)
print("Downloading file at path '\(source.path)' to destination path '\(destinationURL.path)'...")

transfers.append(Transfer(item: .remote(source)) { transfer in
let download = Transfer(item: .remote(source)) { transfer in

// Get the file information.
let directoryEntry = try await self.fileServer.getExtendedAttributes(path: source.path)

// Perform the file copy.
try await self.fileServer.copyFile(fromRemotePath: source.path, toLocalPath: downloadURL.path) { progress, size in
try await self.fileServer.copyFile(fromRemotePath: source.path, toLocalPath: destinationURL.path) { progress, size in
transfer.setStatus(.active(progress, size))
return transfer.isCancelled ? .cancel : .continue
}

// Convert known types.
// N.B. This would be better implemented as a user-configurable and extensible pipeline, but this is a
// reasonable point to hook an initial implementation.
var urls: [URL] = [downloadURL]
var urls: [URL] = [destinationURL]
if convertFiles {
if directoryEntry.fileType == .mbm || directoryEntry.pathExtension.lowercased() == "mbm" {
urls = try PsiLuaEnv().convertMultiBitmap(at: downloadURL, removeSource: true)
urls = try PsiLuaEnv().convertMultiBitmap(at: destinationURL, removeSource: true)
}
}

Expand All @@ -75,18 +77,22 @@ class TransfersModel {

// Mark the transfer as complete.
transfer.setStatus(.complete(details.first))
})
}
transfers.append(download)
try await download.run()
}

func upload(from sourceURL: URL, to destinationPath: String) {
func upload(from sourceURL: URL, to destinationPath: String) async throws {
print("Uploading file at path '\(sourceURL.path)' to destination path '\(destinationPath)'...")
transfers.append(Transfer(item: .local(sourceURL)) { transfer in
let upload = Transfer(item: .local(sourceURL)) { transfer in
try await self.fileServer.copyFile(fromLocalPath: sourceURL.path, toRemotePath: destinationPath) { progress, size in
transfer.setStatus(.active(progress, size))
return transfer.isCancelled ? .cancel : .continue
}
transfer.setStatus(.complete(nil))
})
}
transfers.append(upload)
try await upload.run()
}

func clear() {
Expand Down
4 changes: 3 additions & 1 deletion Reconnect/Windows/TransfersWindow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ struct TransfersWindow: Scene {
return
}
let filename = installerURL.lastPathComponent
transfersModel.upload(from: installerURL, to: "C:".appendingWindowsPathComponent(filename))
Task {
try? await transfersModel.upload(from: installerURL, to: "C:".appendingWindowsPathComponent(filename))
}
}
.handlesExternalEvents(preferring: [.install], allowing: [])
}
Expand Down

0 comments on commit 1c4963a

Please sign in to comment.