From c68738bf644b730d7294cc7f95540a9597609820 Mon Sep 17 00:00:00 2001 From: Brent Simmons Date: Tue, 26 Mar 2024 22:18:48 -0700 Subject: [PATCH] Convert createFolder to async await. --- Account/Sources/Account/Account.swift | 4 +- Account/Sources/Account/AccountDelegate.swift | 2 +- .../CloudKit/CloudKitAccountDelegate.swift | 17 ++++++++- .../Feedbin/FeedbinAccountDelegate.swift | 10 ++--- .../Feedly/FeedlyAccountDelegate.swift | 20 +++++++++- .../LocalAccount/LocalAccountDelegate.swift | 12 +++--- .../NewsBlur/NewsBlurAccountDelegate.swift | 17 ++++++++- .../ReaderAPI/ReaderAPIAccountDelegate.swift | 14 +++---- .../AddFolder/AddFolderWindowController.swift | 10 ++--- .../Sidebar/SidebarOutlineDataSource.swift | 13 ++++--- Mac/Scriptability/Folder+Scriptability.swift | 38 +++++++++---------- iOS/Add/AddFolderViewController.swift | 10 +++-- 12 files changed, 109 insertions(+), 58 deletions(-) diff --git a/Account/Sources/Account/Account.swift b/Account/Sources/Account/Account.swift index edf871913..7b3975439 100644 --- a/Account/Sources/Account/Account.swift +++ b/Account/Sources/Account/Account.swift @@ -620,8 +620,8 @@ public enum FetchType { delegate.restoreFeed(for: self, feed: feed, container: container, completion: completion) } - public func addFolder(_ name: String, completion: @escaping (Result) -> Void) { - delegate.createFolder(for: self, name: name, completion: completion) + public func addFolder(_ name: String) async throws -> Folder { + try await delegate.createFolder(for: self, name: name) } public func removeFolder(_ folder: Folder, completion: @escaping (Result) -> Void) { diff --git a/Account/Sources/Account/AccountDelegate.swift b/Account/Sources/Account/AccountDelegate.swift index 2c6d8001f..15e2536d5 100644 --- a/Account/Sources/Account/AccountDelegate.swift +++ b/Account/Sources/Account/AccountDelegate.swift @@ -32,7 +32,7 @@ import Secrets func importOPML(for account:Account, opmlFile: URL) async throws - func createFolder(for account: Account, name: String, completion: @escaping (Result) -> Void) + func createFolder(for account: Account, name: String) async throws -> Folder func renameFolder(for account: Account, with folder: Folder, to name: String, completion: @escaping (Result) -> Void) func removeFolder(for account: Account, with folder: Folder, completion: @escaping (Result) -> Void) diff --git a/Account/Sources/Account/CloudKit/CloudKitAccountDelegate.swift b/Account/Sources/Account/CloudKit/CloudKitAccountDelegate.swift index 20538439b..b68c83696 100644 --- a/Account/Sources/Account/CloudKit/CloudKitAccountDelegate.swift +++ b/Account/Sources/Account/CloudKit/CloudKitAccountDelegate.swift @@ -332,7 +332,22 @@ enum CloudKitAccountDelegateError: LocalizedError { } } - func createFolder(for account: Account, name: String, completion: @escaping (Result) -> Void) { + func createFolder(for account: Account, name: String) async throws -> Folder { + + try await withCheckedThrowingContinuation { continuation in + + self.createFolder(for: account, name: name) { result in + switch result { + case .success(let folder): + continuation.resume(returning: folder) + case .failure(let error): + continuation.resume(throwing: error) + } + } + } + } + + private func createFolder(for account: Account, name: String, completion: @escaping (Result) -> Void) { refreshProgress.addToNumberOfTasksAndRemaining(1) accountZone.createFolder(name: name) { result in self.refreshProgress.completeTask() diff --git a/Account/Sources/Account/Feedbin/FeedbinAccountDelegate.swift b/Account/Sources/Account/Feedbin/FeedbinAccountDelegate.swift index bc92e5d2a..5233b374b 100644 --- a/Account/Sources/Account/Feedbin/FeedbinAccountDelegate.swift +++ b/Account/Sources/Account/Feedbin/FeedbinAccountDelegate.swift @@ -339,12 +339,12 @@ final class FeedbinAccountDelegate: AccountDelegate { } - func createFolder(for account: Account, name: String, completion: @escaping (Result) -> Void) { - if let folder = account.ensureFolder(with: name) { - completion(.success(folder)) - } else { - completion(.failure(FeedbinAccountDelegateError.invalidParameter)) + func createFolder(for account: Account, name: String) async throws -> Folder { + + guard let folder = account.ensureFolder(with: name) else { + throw FeedbinAccountDelegateError.invalidParameter } + return folder } func renameFolder(for account: Account, with folder: Folder, to name: String, completion: @escaping (Result) -> Void) { diff --git a/Account/Sources/Account/Feedly/FeedlyAccountDelegate.swift b/Account/Sources/Account/Feedly/FeedlyAccountDelegate.swift index 4874626cf..198c9d034 100644 --- a/Account/Sources/Account/Feedly/FeedlyAccountDelegate.swift +++ b/Account/Sources/Account/Feedly/FeedlyAccountDelegate.swift @@ -304,8 +304,24 @@ final class FeedlyAccountDelegate: AccountDelegate { } } - func createFolder(for account: Account, name: String, completion: @escaping (Result) -> Void) { - + func createFolder(for account: Account, name: String) async throws -> Folder { + + try await withCheckedThrowingContinuation { continuation in + + self.createFolder(for: account, name: name) { result in + switch result { + case .success(let folder): + continuation.resume(returning: folder) + case .failure(let error): + continuation.resume(throwing: error) + } + } + } + } + + + private func createFolder(for account: Account, name: String, completion: @escaping (Result) -> Void) { + let progress = refreshProgress progress.addToNumberOfTasksAndRemaining(1) diff --git a/Account/Sources/Account/LocalAccount/LocalAccountDelegate.swift b/Account/Sources/Account/LocalAccount/LocalAccountDelegate.swift index 06cca7530..1e1641b72 100644 --- a/Account/Sources/Account/LocalAccount/LocalAccountDelegate.swift +++ b/Account/Sources/Account/LocalAccount/LocalAccountDelegate.swift @@ -117,14 +117,14 @@ final class LocalAccountDelegate: AccountDelegate { completion(.success(())) } - func createFolder(for account: Account, name: String, completion: @escaping (Result) -> Void) { - if let folder = account.ensureFolder(with: name) { - completion(.success(folder)) - } else { - completion(.failure(FeedbinAccountDelegateError.invalidParameter)) + func createFolder(for account: Account, name: String) async throws -> Folder { + + guard let folder = account.ensureFolder(with: name) else { + throw LocalAccountDelegateError.invalidParameter } + return folder } - + func renameFolder(for account: Account, with folder: Folder, to name: String, completion: @escaping (Result) -> Void) { folder.name = name completion(.success(())) diff --git a/Account/Sources/Account/NewsBlur/NewsBlurAccountDelegate.swift b/Account/Sources/Account/NewsBlur/NewsBlurAccountDelegate.swift index 77c76236d..b564a2b75 100644 --- a/Account/Sources/Account/NewsBlur/NewsBlurAccountDelegate.swift +++ b/Account/Sources/Account/NewsBlur/NewsBlurAccountDelegate.swift @@ -396,7 +396,22 @@ final class NewsBlurAccountDelegate: AccountDelegate { func importOPML(for account: Account, opmlFile: URL) async throws { } - func createFolder(for account: Account, name: String, completion: @escaping (Result) -> ()) { + func createFolder(for account: Account, name: String) async throws -> Folder { + + try await withCheckedThrowingContinuation { continuation in + + self.createFolder(for: account, name: name) { result in + switch result { + case .success(let folder): + continuation.resume(returning: folder) + case .failure(let error): + continuation.resume(throwing: error) + } + } + } + } + + private func createFolder(for account: Account, name: String, completion: @escaping (Result) -> ()) { self.refreshProgress.addToNumberOfTasksAndRemaining(1) caller.addFolder(named: name) { result in diff --git a/Account/Sources/Account/ReaderAPI/ReaderAPIAccountDelegate.swift b/Account/Sources/Account/ReaderAPI/ReaderAPIAccountDelegate.swift index 30297239c..7443a623b 100644 --- a/Account/Sources/Account/ReaderAPI/ReaderAPIAccountDelegate.swift +++ b/Account/Sources/Account/ReaderAPI/ReaderAPIAccountDelegate.swift @@ -340,15 +340,15 @@ final class ReaderAPIAccountDelegate: AccountDelegate { func importOPML(for account:Account, opmlFile: URL) async throws { } - - func createFolder(for account: Account, name: String, completion: @escaping (Result) -> Void) { - if let folder = account.ensureFolder(with: name) { - completion(.success(folder)) - } else { - completion(.failure(ReaderAPIAccountDelegateError.invalidParameter)) + + func createFolder(for account: Account, name: String) async throws -> Folder { + + guard let folder = account.ensureFolder(with: name) else { + throw ReaderAPIAccountDelegateError.invalidParameter } + return folder } - + func renameFolder(for account: Account, with folder: Folder, to name: String, completion: @escaping (Result) -> Void) { refreshProgress.addToNumberOfTasksAndRemaining(1) diff --git a/Mac/MainWindow/AddFolder/AddFolderWindowController.swift b/Mac/MainWindow/AddFolder/AddFolderWindowController.swift index 84053ee20..8b1e6f0ac 100644 --- a/Mac/MainWindow/AddFolder/AddFolderWindowController.swift +++ b/Mac/MainWindow/AddFolder/AddFolderWindowController.swift @@ -88,6 +88,7 @@ extension AddFolderWindowController: NSTextFieldDelegate { private extension AddFolderWindowController { private func addFolderIfNeeded() { + guard let menuItem = accountPopupButton.selectedItem else { return } @@ -100,11 +101,10 @@ private extension AddFolderWindowController { return } - account.addFolder(folderName) { result in - switch result { - case .success: - break - case .failure(let error): + Task { @MainActor in + do { + _ = try await account.addFolder(folderName) + } catch { NSApplication.shared.presentError(error) } } diff --git a/Mac/MainWindow/Sidebar/SidebarOutlineDataSource.swift b/Mac/MainWindow/Sidebar/SidebarOutlineDataSource.swift index 06825d065..bc9424190 100644 --- a/Mac/MainWindow/Sidebar/SidebarOutlineDataSource.swift +++ b/Mac/MainWindow/Sidebar/SidebarOutlineDataSource.swift @@ -424,14 +424,17 @@ private extension SidebarOutlineDataSource { } func copyFolderBetweenAccounts(node: Node, to parentNode: Node) { + guard let folder = node.representedObject as? Folder, let destinationAccount = nodeAccount(parentNode) else { return } - destinationAccount.addFolder(folder.name ?? "") { result in - switch result { - case .success(let destinationFolder): + Task { @MainActor in + + do { + let destinationFolder = try await destinationAccount.addFolder(folder.name ?? "") + for feed in folder.topLevelFeeds { if let existingFeed = destinationAccount.existingFeed(withURL: feed.url) { destinationAccount.addFeed(existingFeed, to: destinationFolder) { result in @@ -453,11 +456,11 @@ private extension SidebarOutlineDataSource { } } } - case .failure(let error): + + } catch { NSApplication.shared.presentError(error) } } - } func acceptLocalFoldersDrop(_ outlineView: NSOutlineView, _ draggedFolders: Set, _ parentNode: Node, _ index: Int) -> Bool { diff --git a/Mac/Scriptability/Folder+Scriptability.swift b/Mac/Scriptability/Folder+Scriptability.swift index 5ebc705ec..0def7becb 100644 --- a/Mac/Scriptability/Folder+Scriptability.swift +++ b/Mac/Scriptability/Folder+Scriptability.swift @@ -66,33 +66,33 @@ import Core tell account X to make new folder at end with properties {name:"new folder name"} */ @MainActor class func handleCreateElement(command:NSCreateCommand) -> Any? { - guard command.isCreateCommand(forClass:"fold") else { return nil } - let name = command.property(forKey:"name") as? String ?? "" + guard command.isCreateCommand(forClass:"fold") else { return nil } + let name = command.property(forKey:"name") as? String ?? "" + + // some combination of the tell target and the location specifier ("in" or "at") + // identifies where the new folder should be created + let (account, folder) = command.accountAndFolderForNewChild() + guard folder == nil else { + print("support for folders within folders is NYI"); + return nil + } - // some combination of the tell target and the location specifier ("in" or "at") - // identifies where the new folder should be created - let (account, folder) = command.accountAndFolderForNewChild() - guard folder == nil else { - print("support for folders within folders is NYI"); - return nil - } - command.suspendExecution() - - account.addFolder(name) { result in - switch result { - case .success(let folder): + + Task { @MainActor in + do { + let folder = try await account.addFolder(name) let scriptableAccount = ScriptableAccount(account) let scriptableFolder = ScriptableFolder(folder, container:scriptableAccount) command.resumeExecution(withResult:scriptableFolder.objectSpecifier) - case .failure: + } catch { command.resumeExecution(withResult:nil) } } - - return nil - } - + + return nil + } + // MARK: --- Scriptable elements --- @objc(feeds) diff --git a/iOS/Add/AddFolderViewController.swift b/iOS/Add/AddFolderViewController.swift index 07d0b1450..231770aad 100644 --- a/iOS/Add/AddFolderViewController.swift +++ b/iOS/Add/AddFolderViewController.swift @@ -76,14 +76,16 @@ class AddFolderViewController: UITableViewController { } @IBAction func add(_ sender: Any) { + guard let folderName = nameTextField.text else { return } - selectedAccount.addFolder(folderName) { result in - switch result { - case .success: + + Task { @MainActor in + do { + _ = try await selectedAccount.addFolder(folderName) self.dismiss(animated: true) - case .failure(let error): + } catch { self.presentError(error) self.dismiss(animated: true) }