diff --git a/Frameworks/Account/Account.swift b/Frameworks/Account/Account.swift index 4bb5df495..4a53ee32f 100644 --- a/Frameworks/Account/Account.swift +++ b/Frameworks/Account/Account.swift @@ -377,8 +377,8 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, delegate.removeFeed(for: self, from: container, with: feed, completion: completion) } - public func createFeed(url: String, completion: @escaping (Result) -> Void) { - delegate.createFeed(for: self, url: url, completion: completion) + public func createFeed(url: String, name: String?, container: Container, completion: @escaping (Result) -> Void) { + delegate.createFeed(for: self, url: url, name: name, container: container, completion: completion) } func createFeed(with name: String?, url: String, feedID: String, homePageURL: String?) -> Feed { @@ -401,8 +401,8 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, delegate.renameFeed(for: self, with: feed, to: name, completion: completion) } - public func restoreFeed(_ feed: Feed, folder: Folder?, completion: @escaping (Result) -> Void) { - delegate.restoreFeed(for: self, feed: feed, folder: folder, completion: completion) + public func restoreFeed(_ feed: Feed, container: Container, completion: @escaping (Result) -> Void) { + delegate.restoreFeed(for: self, feed: feed, container: container, completion: completion) } public func deleteFolder(_ folder: Folder, completion: @escaping (Result) -> Void) { diff --git a/Frameworks/Account/AccountDelegate.swift b/Frameworks/Account/AccountDelegate.swift index 314a944c5..33dace1a3 100644 --- a/Frameworks/Account/AccountDelegate.swift +++ b/Frameworks/Account/AccountDelegate.swift @@ -31,14 +31,14 @@ protocol AccountDelegate { func renameFolder(for account: Account, with folder: Folder, to name: String, completion: @escaping (Result) -> Void) func deleteFolder(for account: Account, with folder: Folder, completion: @escaping (Result) -> Void) - func createFeed(for account: Account, url: String, completion: @escaping (Result) -> Void) + func createFeed(for account: Account, url: String, name: String?, container: Container, completion: @escaping (Result) -> Void) func renameFeed(for account: Account, with feed: Feed, to name: String, completion: @escaping (Result) -> Void) func deleteFeed(for account: Account, with feed: Feed, completion: @escaping (Result) -> Void) func addFeed(for account: Account, to container: Container, with: Feed, completion: @escaping (Result) -> Void) func removeFeed(for account: Account, from container: Container, with: Feed, completion: @escaping (Result) -> Void) - func restoreFeed(for account: Account, feed: Feed, folder: Folder?, completion: @escaping (Result) -> Void) + func restoreFeed(for account: Account, feed: Feed, container: Container, completion: @escaping (Result) -> Void) func restoreFolder(for account: Account, folder: Folder, completion: @escaping (Result) -> Void) func markArticles(for account: Account, articles: Set
, statusKey: ArticleStatus.Key, flag: Bool) -> Set
? diff --git a/Frameworks/Account/Feedbin/FeedbinAccountDelegate.swift b/Frameworks/Account/Feedbin/FeedbinAccountDelegate.swift index eddcd43b1..0a9b223d5 100644 --- a/Frameworks/Account/Feedbin/FeedbinAccountDelegate.swift +++ b/Frameworks/Account/Feedbin/FeedbinAccountDelegate.swift @@ -284,16 +284,16 @@ final class FeedbinAccountDelegate: AccountDelegate { } - func createFeed(for account: Account, url: String, completion: @escaping (Result) -> Void) { + func createFeed(for account: Account, url: String, name: String?, container: Container, completion: @escaping (Result) -> Void) { caller.createSubscription(url: url) { result in switch result { case .success(let subResult): switch subResult { case .created(let subscription): - self.createFeed(account: account, subscription: subscription, completion: completion) + self.createFeed(account: account, subscription: subscription, name: name, container: container, completion: completion) case .multipleChoice(let choices): - self.decideBestFeedChoice(account: account, url: url, choices: choices, completion: completion) + self.decideBestFeedChoice(account: account, url: url, name: name, container: container, choices: choices, completion: completion) case .alreadySubscribed: DispatchQueue.main.async { completion(.failure(AccountError.createErrorAlreadySubscribed)) @@ -424,19 +424,14 @@ final class FeedbinAccountDelegate: AccountDelegate { } - func restoreFeed(for account: Account, feed: Feed, folder: Folder?, completion: @escaping (Result) -> Void) { + func restoreFeed(for account: Account, feed: Feed, container: Container, completion: @escaping (Result) -> Void) { - let editedName = feed.editedName - - createFeed(for: account, url: feed.url) { result in + createFeed(for: account, url: feed.url, name: feed.editedName, container: container) { result in switch result { - case .success(let feed): - self.processRestoredFeed(for: account, feed: feed, editedName: editedName, folder: folder, completion: completion) + case .success: + completion(.success(())) case .failure(let error): - DispatchQueue.main.async { - let wrappedError = AccountError.wrappedError(error: error, account: account) - completion(.failure(wrappedError)) - } + completion(.failure(error)) } } @@ -824,74 +819,6 @@ private extension FeedbinAccountDelegate { } - func processRestoredFeed(for account: Account, feed: Feed, editedName: String?, folder: Folder?, completion: @escaping (Result) -> Void) { - - if let folder = folder { - - addFeed(for: account, to: folder, with: feed) { result in - - switch result { - case .success: - - if editedName != nil { - DispatchQueue.main.async { - account.removeFeed(feed) - folder.addFeed(feed) - } - self.processRestoredFeedName(for: account, feed: feed, editedName: editedName!, completion: completion) - } else { - DispatchQueue.main.async { - account.removeFeed(feed) - folder.addFeed(feed) - completion(.success(())) - } - } - - case .failure(let error): - DispatchQueue.main.async { - completion(.failure(error)) - } - } - - } - - } else { - - DispatchQueue.main.async { - account.addFeed(feed) - } - - if editedName != nil { - processRestoredFeedName(for: account, feed: feed, editedName: editedName!, completion: completion) - } else { - DispatchQueue.main.async { - completion(.success(())) - } - } - - } - - } - - func processRestoredFeedName(for account: Account, feed: Feed, editedName: String, completion: @escaping (Result) -> Void) { - - renameFeed(for: account, with: feed, to: editedName) { result in - switch result { - case .success: - DispatchQueue.main.async { - feed.editedName = editedName - completion(.success(())) - } - case .failure(let error): - DispatchQueue.main.async { - completion(.failure(error)) - } - } - - } - - } - func clearFolderRelationship(for feed: Feed, withFolderName folderName: String) { if var folderRelationship = feed.folderRelationship { folderRelationship[folderName] = nil @@ -908,7 +835,7 @@ private extension FeedbinAccountDelegate { } } - func decideBestFeedChoice(account: Account, url: String, choices: [FeedbinSubscriptionChoice], completion: @escaping (Result) -> Void) { + func decideBestFeedChoice(account: Account, url: String, name: String?, container: Container, choices: [FeedbinSubscriptionChoice], completion: @escaping (Result) -> Void) { let feedSpecifiers: [FeedSpecifier] = choices.map { choice in let source = url == choice.url ? FeedSpecifier.Source.UserEntered : FeedSpecifier.Source.HTMLLink @@ -918,7 +845,7 @@ private extension FeedbinAccountDelegate { if let bestSpecifier = FeedSpecifier.bestFeed(in: Set(feedSpecifiers)) { if let bestSubscription = choices.filter({ bestSpecifier.urlString == $0.url }).first { - createFeed(for: account, url: bestSubscription.url, completion: completion) + createFeed(for: account, url: bestSubscription.url, name: name, container: container, completion: completion) } else { DispatchQueue.main.async { completion(.failure(FeedbinAccountDelegateError.invalidParameter)) @@ -932,44 +859,67 @@ private extension FeedbinAccountDelegate { } - func createFeed( account: Account, subscription sub: FeedbinSubscription, completion: @escaping (Result) -> Void) { + func createFeed( account: Account, subscription sub: FeedbinSubscription, name: String?, container: Container, completion: @escaping (Result) -> Void) { + DispatchQueue.main.async { let feed = account.createFeed(with: sub.name, url: sub.url, feedID: String(sub.feedID), homePageURL: sub.homePageURL) feed.subscriptionID = String(sub.subscriptionID) - // Download the initial articles - self.caller.retrieveEntries(feedID: feed.feedID) { result in - + container.addFeed(feed) { result in switch result { - case .success(let (entries, page)): - - self.processEntries(account: account, entries: entries) { - self.refreshArticles(account, page: page) { - self.refreshArticleStatus(for: account) { - self.refreshMissingArticles(account) { - DispatchQueue.main.async { - completion(.success(feed)) - } - } + case .success: + if let name = name { + account.renameFeed(feed, to: name) { result in + switch result { + case .success: + self.initialFeedDownload(account: account, feed: feed, completion: completion) + case .failure(let error): + completion(.failure(error)) } } + } else { + self.initialFeedDownload(account: account, feed: feed, completion: completion) } - case .failure(let error): - os_log(.error, log: self.log, "Initial articles download failed: %@.", error.localizedDescription) - DispatchQueue.main.async { - completion(.success(feed)) - } + completion(.failure(error)) } - } - + } } + func initialFeedDownload( account: Account, feed: Feed, completion: @escaping (Result) -> Void) { + + // Download the initial articles + self.caller.retrieveEntries(feedID: feed.feedID) { result in + + switch result { + case .success(let (entries, page)): + + self.processEntries(account: account, entries: entries) { + self.refreshArticles(account, page: page) { + self.refreshArticleStatus(for: account) { + self.refreshMissingArticles(account) { + DispatchQueue.main.async { + completion(.success(feed)) + } + } + } + } + } + + case .failure(let error): + completion(.failure(error)) + } + + } + + + } + func refreshArticles(_ account: Account, completion: @escaping (() -> Void)) { os_log(.debug, log: log, "Refreshing articles...") diff --git a/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift b/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift index f9efdbc31..10e5ce446 100644 --- a/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift +++ b/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift @@ -27,6 +27,8 @@ final class LocalAccountDelegate: AccountDelegate { private weak var account: Account? private var feedFinder: FeedFinder? + private var createFeedName: String? + private var createFeedContainer: Container? private var createFeedCompletion: ((Result) -> Void)? private let refresher = LocalAccountRefresher() @@ -99,7 +101,7 @@ final class LocalAccountDelegate: AccountDelegate { completion(.success(())) } - func createFeed(for account: Account, url urlString: String, completion: @escaping (Result) -> Void) { + func createFeed(for account: Account, url urlString: String, name: String?, container: Container, completion: @escaping (Result) -> Void) { guard let url = URL(string: urlString) else { completion(.failure(LocalAccountDelegateError.invalidParameter)) @@ -107,8 +109,9 @@ final class LocalAccountDelegate: AccountDelegate { } self.account = account + createFeedName = name + createFeedContainer = container createFeedCompletion = completion - feedFinder = FeedFinder(url: url, delegate: self) } @@ -143,10 +146,8 @@ final class LocalAccountDelegate: AccountDelegate { func addFeed(for account: Account, to container: Container, with feed: Feed, completion: @escaping (Result) -> Void) { if let folder = container as? Folder { folder.addFeed(feed) - feed.account = folder.account } else if let account = container as? Account { account.addFeed(feed) - feed.account = account } completion(.success(())) } @@ -161,13 +162,8 @@ final class LocalAccountDelegate: AccountDelegate { completion(.success(())) } - func restoreFeed(for account: Account, feed: Feed, folder: Folder?, completion: @escaping (Result) -> Void) { - if let folder = folder { - folder.addFeed(feed) - } else { - account.addFeed(feed) - } - completion(.success(())) + func restoreFeed(for account: Account, feed: Feed, container: Container, completion: @escaping (Result) -> Void) { + container.addFeed(feed, completion: completion) } func restoreFolder(for account: Account, folder: Folder, completion: @escaping (Result) -> Void) { @@ -216,11 +212,24 @@ extension LocalAccountDelegate: FeedFinderDelegate { } let feed = account.createFeed(with: nil, url: url.absoluteString, feedID: url.absoluteString, homePageURL: nil) - InitialFeedDownloader.download(url) { [weak self] parsedFeed in + + InitialFeedDownloader.download(url) { parsedFeed in + if let parsedFeed = parsedFeed { account.update(feed, with: parsedFeed, {}) } - self?.createFeedCompletion!(.success(feed)) + + feed.editedName = self.createFeedName + + self.createFeedContainer?.addFeed(feed) { result in + switch result { + case .success: + self.createFeedCompletion?(.success(feed)) + case .failure(let error): + self.createFeedCompletion?(.failure(error)) + } + } + } } diff --git a/Mac/MainWindow/AddFeed/AddFeedController.swift b/Mac/MainWindow/AddFeed/AddFeedController.swift index 8b1babdbe..7475be1b3 100644 --- a/Mac/MainWindow/AddFeed/AddFeedController.swift +++ b/Mac/MainWindow/AddFeed/AddFeedController.swift @@ -52,7 +52,6 @@ class AddFeedController: AddFeedWindowControllerDelegate { return } let account = accountAndFolderSpecifier.account - let folder = accountAndFolderSpecifier.folder if account.hasFeed(withURL: url.absoluteString) { showAlreadySubscribedError(url.absoluteString) @@ -61,20 +60,20 @@ class AddFeedController: AddFeedWindowControllerDelegate { BatchUpdate.shared.start() - account.createFeed(url: url.absoluteString) { [weak self] result in - - self?.endShowingProgress() + account.createFeed(url: url.absoluteString, name: title, container: container) { result in + self.endShowingProgress() + BatchUpdate.shared.end() + switch result { case .success(let feed): - self?.processFeed(feed, account: account, folder: folder, url: url, title: title) + NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed]) case .failure(let error): - BatchUpdate.shared.end() switch error { case AccountError.createErrorAlreadySubscribed: - self?.showAlreadySubscribedError(url.absoluteString) + self.showAlreadySubscribedError(url.absoluteString) case AccountError.createErrorNotFound: - self?.showNoFeedsErrorMessage() + self.showNoFeedsErrorMessage() default: NSApplication.shared.presentError(error) } @@ -125,45 +124,6 @@ private extension AddFeedController { } } - func processFeed(_ feed: Feed, account: Account, folder: Folder?, url: URL, title: String?) { - - if let title = title { - account.renameFeed(feed, to: title) { result in - switch result { - case .success: - break - case .failure(let error): - NSApplication.shared.presentError(error) - } - } - } - - if let folder = folder { - folder.addFeed(feed) { result in - switch result { - case .success: - BatchUpdate.shared.end() - NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed]) - case .failure(let error): - BatchUpdate.shared.end() - NSApplication.shared.presentError(error) - } - } - } else { - account.addFeed(feed) { result in - switch result { - case .success: - BatchUpdate.shared.end() - NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed]) - case .failure(let error): - BatchUpdate.shared.end() - NSApplication.shared.presentError(error) - } - } - } - - } - // MARK: Errors func showAlreadySubscribedError(_ urlString: String) { diff --git a/Mac/Scriptability/Feed+Scriptability.swift b/Mac/Scriptability/Feed+Scriptability.swift index db385b312..42fefde87 100644 --- a/Mac/Scriptability/Feed+Scriptability.swift +++ b/Mac/Scriptability/Feed+Scriptability.swift @@ -91,7 +91,9 @@ class ScriptableFeed: NSObject, UniqueIdScriptingObject, ScriptingObjectContaine if let existingFeed = account.existingFeed(withURL:url) { return self.scriptableFeed(existingFeed, account:account, folder:folder) } - + + let container: Container = folder != nil ? folder! : account + // at this point, we need to download the feed and parse it. // RS Parser does the callback for the download on the main thread (which it probably shouldn't?) // because we can't wait here (on the main thread, maybe) for the callback, we have to return from this function @@ -100,27 +102,12 @@ class ScriptableFeed: NSObject, UniqueIdScriptingObject, ScriptingObjectContaine // suspendExecution(). When we get the callback, we can supply the event result and call resumeExecution() command.suspendExecution() - account.createFeed(url: url) { result in + account.createFeed(url: url, name: titleFromArgs, container: container) { result in switch result { case .success(let feed): - - if let editedName = titleFromArgs { - account.renameFeed(feed, to: editedName) { result in - } - } - - // add the feed, putting it in a folder if needed - account.addFeed(feed) { result in - switch result { - case .success: - NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed]) - let scriptableFeed = self.scriptableFeed(feed, account:account, folder:folder) - command.resumeExecution(withResult:scriptableFeed.objectSpecifier) - case .failure: - command.resumeExecution(withResult:nil) - } - } - + NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed]) + let scriptableFeed = self.scriptableFeed(feed, account:account, folder:folder) + command.resumeExecution(withResult:scriptableFeed.objectSpecifier) case .failure: command.resumeExecution(withResult:nil) } diff --git a/Shared/Commands/DeleteCommand.swift b/Shared/Commands/DeleteCommand.swift index afd07f1f2..309a8c8f0 100644 --- a/Shared/Commands/DeleteCommand.swift +++ b/Shared/Commands/DeleteCommand.swift @@ -161,12 +161,12 @@ private struct SidebarItemSpecifier { private func restoreFeed() { - guard let account = account, let feed = feed else { + guard let account = account, let feed = feed, let container = path.resolveContainer() else { return } BatchUpdate.shared.start() - account.restoreFeed(feed, folder: resolvedFolder()) { result in + account.restoreFeed(feed, container: container) { result in BatchUpdate.shared.end() self.checkResult(result) } @@ -187,10 +187,6 @@ private struct SidebarItemSpecifier { } - private func resolvedFolder() -> Folder? { - return path.resolveContainer() as? Folder - } - private func checkResult(_ result: Result) { switch result { diff --git a/iOS/Add/AddFeedViewController.swift b/iOS/Add/AddFeedViewController.swift index b2b84cee0..b2dc3464b 100644 --- a/iOS/Add/AddFeedViewController.swift +++ b/iOS/Add/AddFeedViewController.swift @@ -80,13 +80,10 @@ class AddFeedViewController: UITableViewController, AddContainerViewControllerCh let container = pickerData.containers[folderPickerView.selectedRow(inComponent: 0)] var account: Account? - var folder: Folder? if let containerAccount = container as? Account { account = containerAccount - } - if let containerFolder = container as? Folder, let containerAccount = containerFolder.account { + } else if let containerFolder = container as? Folder, let containerAccount = containerFolder.account { account = containerAccount - folder = containerFolder } if account!.hasFeed(withURL: url.absoluteString) { @@ -94,26 +91,28 @@ class AddFeedViewController: UITableViewController, AddContainerViewControllerCh return } - let title = nameTextField.text - delegate?.processingDidBegin() + BatchUpdate.shared.start() + + account!.createFeed(url: url.absoluteString, name: nameTextField.text, container: container) { result in - account!.createFeed(url: url.absoluteString) { [weak self] result in + BatchUpdate.shared.end() switch result { case .success(let feed): - self?.processFeed(feed, account: account!, folder: folder, url: url, title: title) + self.delegate?.processingDidEnd() + NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed]) case .failure(let error): switch error { case AccountError.createErrorAlreadySubscribed: - self?.showAlreadySubscribedError() - self?.delegate?.processingDidCancel() + self.showAlreadySubscribedError() + self.delegate?.processingDidCancel() case AccountError.createErrorNotFound: - self?.showNoFeedsErrorMessage() - self?.delegate?.processingDidCancel() + self.showNoFeedsErrorMessage() + self.delegate?.processingDidCancel() default: - self?.presentError(error) - self?.delegate?.processingDidCancel() + self.presentError(error) + self.delegate?.processingDidCancel() } } @@ -178,45 +177,6 @@ private extension AddFeedViewController { presentError(title: title, message: message as String) } - func processFeed(_ feed: Feed, account: Account, folder: Folder?, url: URL, title: String?) { - - if let title = title { - account.renameFeed(feed, to: title) { [weak self] result in - switch result { - case .success: - break - case .failure(let error): - self?.presentError(error) - } - } - } - - if let folder = folder { - folder.addFeed(feed) { [weak self] result in - switch result { - case .success: - self?.delegate?.processingDidEnd() - NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed]) - case .failure(let error): - self?.delegate?.processingDidEnd() - self?.presentError(error) - } - } - } else { - account.addFeed(feed) { [weak self] result in - switch result { - case .success: - self?.delegate?.processingDidEnd() - NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed]) - case .failure(let error): - self?.delegate?.processingDidEnd() - self?.presentError(error) - } - } - } - - } - } extension AddFeedViewController: UITextFieldDelegate {