diff --git a/Frameworks/Account/Account.swift b/Frameworks/Account/Account.swift index 909f4e16d..639c5c0f9 100644 --- a/Frameworks/Account/Account.swift +++ b/Frameworks/Account/Account.swift @@ -382,26 +382,25 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, } } - public func createFeed(with name: String?, editedName: String?, url: String) -> Feed { - - // For syncing, this may need to be an async method with a callback, - // since it will likely need to call the server. - return createFeed(with: name, editedName: editedName, url: url, feedID: url, homePageURL: nil) - + public func createFeed(with name: String?, url: String, completion: @escaping (Result) -> Void) { + delegate.createFeed(for: self, with: name, url: url, completion: completion) } - func createFeed(with name: String?, editedName: String?, url: String, feedID: String, homePageURL: String?) -> Feed { + func createFeed(with name: String?, url: String, feedID: String, homePageURL: String?) -> Feed { let metadata = feedMetadata(feedURL: url, feedID: feedID) let feed = Feed(account: self, url: url, metadata: metadata) feed.name = name - feed.editedName = editedName feed.homePageURL = homePageURL return feed } + public func renameFeed(_ feed: Feed, to name: String, completion: @escaping (Result) -> Void) { + delegate.renameFeed(for: self, with: feed, to: name, completion: completion) + } + public func canAddFolder(_ folder: Folder, to containingFolder: Folder?) -> Bool { return false // TODO diff --git a/Frameworks/Account/Account.xcodeproj/project.pbxproj b/Frameworks/Account/Account.xcodeproj/project.pbxproj index 1650498b1..73c2c8c8d 100644 --- a/Frameworks/Account/Account.xcodeproj/project.pbxproj +++ b/Frameworks/Account/Account.xcodeproj/project.pbxproj @@ -21,6 +21,11 @@ 5165D71622821C2400D9D53D /* taggings_delete.json in Resources */ = {isa = PBXBuildFile; fileRef = 5165D71322821C2400D9D53D /* taggings_delete.json */; }; 5165D71722821C2400D9D53D /* taggings_add.json in Resources */ = {isa = PBXBuildFile; fileRef = 5165D71422821C2400D9D53D /* taggings_add.json */; }; 5165D71822821C2400D9D53D /* taggings_initial.json in Resources */ = {isa = PBXBuildFile; fileRef = 5165D71522821C2400D9D53D /* taggings_initial.json */; }; + 5165D71B22833A7500D9D53D /* AccountCreateFeedResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5165D7192283398700D9D53D /* AccountCreateFeedResult.swift */; }; + 5165D72822835F7800D9D53D /* FeedFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5165D71C22835E9800D9D53D /* FeedFinder.swift */; }; + 5165D72922835F7A00D9D53D /* FeedSpecifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5165D71D22835E9800D9D53D /* FeedSpecifier.swift */; }; + 5165D72A22835F7D00D9D53D /* HTMLFeedFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5165D71E22835E9800D9D53D /* HTMLFeedFinder.swift */; }; + 5165D73122837F3400D9D53D /* InitialFeedDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5165D73022837F3400D9D53D /* InitialFeedDownloader.swift */; }; 51D58755227F53BE00900287 /* FeedbinTag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51D58754227F53BE00900287 /* FeedbinTag.swift */; }; 51D5875A227F630B00900287 /* tags_delete.json in Resources */ = {isa = PBXBuildFile; fileRef = 51D58757227F630B00900287 /* tags_delete.json */; }; 51D5875B227F630B00900287 /* tags_add.json in Resources */ = {isa = PBXBuildFile; fileRef = 51D58758227F630B00900287 /* tags_add.json */; }; @@ -113,6 +118,11 @@ 5165D71322821C2400D9D53D /* taggings_delete.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = taggings_delete.json; sourceTree = ""; }; 5165D71422821C2400D9D53D /* taggings_add.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = taggings_add.json; sourceTree = ""; }; 5165D71522821C2400D9D53D /* taggings_initial.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = taggings_initial.json; sourceTree = ""; }; + 5165D7192283398700D9D53D /* AccountCreateFeedResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountCreateFeedResult.swift; sourceTree = ""; }; + 5165D71C22835E9800D9D53D /* FeedFinder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedFinder.swift; sourceTree = ""; }; + 5165D71D22835E9800D9D53D /* FeedSpecifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedSpecifier.swift; sourceTree = ""; }; + 5165D71E22835E9800D9D53D /* HTMLFeedFinder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTMLFeedFinder.swift; sourceTree = ""; }; + 5165D73022837F3400D9D53D /* InitialFeedDownloader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InitialFeedDownloader.swift; sourceTree = ""; }; 51D58754227F53BE00900287 /* FeedbinTag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinTag.swift; sourceTree = ""; }; 51D58757227F630B00900287 /* tags_delete.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = tags_delete.json; sourceTree = ""; }; 51D58758227F630B00900287 /* tags_add.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = tags_add.json; sourceTree = ""; }; @@ -178,6 +188,16 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 5165D71F22835E9800D9D53D /* FeedFinder */ = { + isa = PBXGroup; + children = ( + 5165D71C22835E9800D9D53D /* FeedFinder.swift */, + 5165D71D22835E9800D9D53D /* FeedSpecifier.swift */, + 5165D71E22835E9800D9D53D /* HTMLFeedFinder.swift */, + ); + path = FeedFinder; + sourceTree = ""; + }; 51D58756227F62E300900287 /* JSON */ = { isa = PBXGroup; children = ( @@ -218,6 +238,7 @@ children = ( 8419742C1F6DDE84006346C4 /* LocalAccountDelegate.swift */, 8419742D1F6DDE96006346C4 /* LocalAccountRefresher.swift */, + 5165D73022837F3400D9D53D /* InitialFeedDownloader.swift */, ); path = LocalAccount; sourceTree = ""; @@ -253,6 +274,7 @@ isa = PBXGroup; children = ( 848935101F62486800CEBD24 /* Account.swift */, + 5165D7192283398700D9D53D /* AccountCreateFeedResult.swift */, 841974241F6DDCE4006346C4 /* AccountDelegate.swift */, 846E77531F6F00E300A165E2 /* AccountManager.swift */, 84AF4EA3222CFDD100F6A800 /* AccountMetadata.swift */, @@ -265,6 +287,7 @@ 84B2D4CE2238C13D00498ADA /* FeedMetadata.swift */, 841974001F6DD1EC006346C4 /* Folder.swift */, 844B297E210CE37E004020B3 /* UnreadCountProvider.swift */, + 5165D71F22835E9800D9D53D /* FeedFinder */, 8419742B1F6DDE84006346C4 /* LocalAccount */, 84245C7D1FDDD2580074AFBB /* Feedbin */, 848935031F62484F00CEBD24 /* AccountTests */, @@ -485,17 +508,22 @@ 5144EA4E227B829A00D19003 /* FeedbinAccountDelegate.swift in Sources */, 846E77451F6EF9B900A165E2 /* Container.swift in Sources */, 84F73CF1202788D90000BCEF /* ArticleFetcher.swift in Sources */, + 5165D71B22833A7500D9D53D /* AccountCreateFeedResult.swift in Sources */, 841974251F6DDCE4006346C4 /* AccountDelegate.swift in Sources */, + 5165D73122837F3400D9D53D /* InitialFeedDownloader.swift in Sources */, 846E77541F6F00E300A165E2 /* AccountManager.swift in Sources */, + 5165D72922835F7A00D9D53D /* FeedSpecifier.swift in Sources */, 844B297D2106C7EC004020B3 /* Feed.swift in Sources */, 84B2D4D02238CD8A00498ADA /* FeedMetadata.swift in Sources */, 5144EA49227B497600D19003 /* FeedbinAPICaller.swift in Sources */, 84B99C9F1FAE8D3200ECDEDB /* ContainerPath.swift in Sources */, 5133231122810EB200C30F19 /* FeedbinIcon.swift in Sources */, 846E77501F6EF9C400A165E2 /* LocalAccountRefresher.swift in Sources */, + 5165D72822835F7800D9D53D /* FeedFinder.swift in Sources */, 51D58755227F53BE00900287 /* FeedbinTag.swift in Sources */, 84D09623217418DC00D77525 /* FeedbinTagging.swift in Sources */, 84CAD7161FDF2E22000F0755 /* FeedbinEntry.swift in Sources */, + 5165D72A22835F7D00D9D53D /* HTMLFeedFinder.swift in Sources */, 841974011F6DD1EC006346C4 /* Folder.swift in Sources */, 846E774F1F6EF9C000A165E2 /* LocalAccountDelegate.swift in Sources */, 844B297F210CE37E004020B3 /* UnreadCountProvider.swift in Sources */, diff --git a/Frameworks/Account/AccountCreateFeedResult.swift b/Frameworks/Account/AccountCreateFeedResult.swift new file mode 100644 index 000000000..e4f521846 --- /dev/null +++ b/Frameworks/Account/AccountCreateFeedResult.swift @@ -0,0 +1,21 @@ +// +// AccountCreateFeedResult.swift +// AccountTests +// +// Created by Maurice Parker on 5/8/19. +// Copyright © 2019 Ranchero Software, LLC. All rights reserved. +// + +import Foundation + +public enum AccountCreateFeedResult { + case created(Feed) + case multipleChoice([AccountCreateFeedChoice]) + case alreadySubscribed + case notFound +} + +public struct AccountCreateFeedChoice { + let name: String + let url: String +} diff --git a/Frameworks/Account/AccountDelegate.swift b/Frameworks/Account/AccountDelegate.swift index 5cbf903d1..17f8aa187 100644 --- a/Frameworks/Account/AccountDelegate.swift +++ b/Frameworks/Account/AccountDelegate.swift @@ -19,11 +19,14 @@ protocol AccountDelegate { var refreshProgress: DownloadProgress { get } - func refreshAll(for: Account, completion: (() -> Void)?) + func refreshAll(for account: Account, completion: (() -> Void)?) - func renameFolder(for: Account, with folder: Folder, to name: String, completion: @escaping (Result) -> Void) - func deleteFolder(for: Account, with folder: Folder, completion: @escaping (Result) -> Void) + 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, with name: String?, url: String, completion: @escaping (Result) -> Void) + func renameFeed(for account: Account, with feed: Feed, to name: String, completion: @escaping (Result) -> Void) + // Called at the end of account’s init method. func accountDidInitialize(_ account: Account) diff --git a/Shared/FeedFinder/FeedFinder.swift b/Frameworks/Account/FeedFinder/FeedFinder.swift similarity index 100% rename from Shared/FeedFinder/FeedFinder.swift rename to Frameworks/Account/FeedFinder/FeedFinder.swift diff --git a/Shared/FeedFinder/FeedSpecifier.swift b/Frameworks/Account/FeedFinder/FeedSpecifier.swift similarity index 100% rename from Shared/FeedFinder/FeedSpecifier.swift rename to Frameworks/Account/FeedFinder/FeedSpecifier.swift diff --git a/Shared/FeedFinder/HTMLFeedFinder.swift b/Frameworks/Account/FeedFinder/HTMLFeedFinder.swift similarity index 100% rename from Shared/FeedFinder/HTMLFeedFinder.swift rename to Frameworks/Account/FeedFinder/HTMLFeedFinder.swift diff --git a/Frameworks/Account/Feedbin/FeedbinAPICaller.swift b/Frameworks/Account/Feedbin/FeedbinAPICaller.swift index aca381d73..60ae08532 100644 --- a/Frameworks/Account/Feedbin/FeedbinAPICaller.swift +++ b/Frameworks/Account/Feedbin/FeedbinAPICaller.swift @@ -10,8 +10,8 @@ import Foundation import RSWeb enum CreateSubscriptionResult { - case created(String?) - case multipleChoice([FeedbinSubscription]?) + case created(FeedbinSubscription) + case multipleChoice([FeedbinSubscription]) case alreadySubscribed case notFound } @@ -123,24 +123,46 @@ final class FeedbinAPICaller: NSObject { func createSubscription(url: String, completionHandler completion: @escaping (Result) -> Void) { let callURL = feedbinBaseURL.appendingPathComponent("subscriptions.json") - let conditionalGet = accountMetadata?.conditionalGetInfo[AccountMetadata.ConditionalGetKeys.subscriptions] - let request = URLRequest(url: callURL, credentials: credentials, conditionalGet: conditionalGet) - let payload = FeedbinCreateSubscription(feedURL: url) + var request = URLRequest(url: callURL, credentials: credentials) + request.httpMethod = HTTPMethod.post + request.addValue("application/json; charset=utf-8", forHTTPHeaderField: HTTPRequestHeader.contentType) + let payload: Data + do { + payload = try JSONEncoder().encode(FeedbinCreateSubscription(feedURL: url)) + } catch { + completion(.failure(error)) + return + } - transport.send(request: request, method: HTTPMethod.post, payload: payload, resultType: [FeedbinSubscription].self) { [weak self] result in + transport.send(request: request, payload: payload) { result in switch result { - case .success(let (response, subscriptions)): - - self?.storeConditionalGet(metadata: self?.accountMetadata, key: AccountMetadata.ConditionalGetKeys.subscriptions, headers: response.allHeaderFields) + case .success(let (response, data)): switch response.forcedStatusCode { case 201: - let location = response.valueForHTTPHeaderField(HTTPResponseHeader.location) - completion(.success(.created(location))) + guard let subData = data else { + completion(.failure(TransportError.noData)) + break + } + do { + let subscription = try JSONDecoder().decode(FeedbinSubscription.self, from: subData) + completion(.success(.created(subscription))) + } catch { + completion(.failure(error)) + } case 300: - completion(.success(.multipleChoice(subscriptions))) + guard let subData = data else { + completion(.failure(TransportError.noData)) + break + } + do { + let subscriptions = try JSONDecoder().decode([FeedbinSubscription].self, from: subData) + completion(.success(.multipleChoice(subscriptions))) + } catch { + completion(.failure(error)) + } case 302: completion(.success(.alreadySubscribed)) default: @@ -166,6 +188,13 @@ final class FeedbinAPICaller: NSObject { } + func renameFeed(feedID: String, newName: String, completion: @escaping (Result) -> Void) { + let callURL = feedbinBaseURL.appendingPathComponent("subscriptions/\(feedID).json") + let request = URLRequest(url: callURL, credentials: credentials) + let payload = FeedbinUpdateSubscription(title: newName) + transport.send(request: request, method: HTTPMethod.patch, payload: payload, completion: completion) + } + func retrieveTaggings(completionHandler completion: @escaping (Result<[FeedbinTagging]?, Error>) -> Void) { let callURL = feedbinBaseURL.appendingPathComponent("taggings.json") diff --git a/Frameworks/Account/Feedbin/FeedbinAccountDelegate.swift b/Frameworks/Account/Feedbin/FeedbinAccountDelegate.swift index b2789ec02..6aae5fab1 100644 --- a/Frameworks/Account/Feedbin/FeedbinAccountDelegate.swift +++ b/Frameworks/Account/Feedbin/FeedbinAccountDelegate.swift @@ -110,6 +110,59 @@ final class FeedbinAccountDelegate: AccountDelegate { } + func createFeed(for account: Account, with name: String?, url: String, completion: @escaping (Result) -> Void) { + + caller.createSubscription(url: url) { result in + switch result { + case .success(let subResult): + switch subResult { + case .created(let sub): + DispatchQueue.main.async { + let feed = account.createFeed(with: sub.name, url: sub.url, feedID: String(sub.feedID), homePageURL: sub.homePageURL) + completion(.success(.created(feed))) + } + case .multipleChoice(let subs): + let resultSubs = subs.map { sub in return AccountCreateFeedChoice(name: sub.name ?? "", url: sub.url) } + DispatchQueue.main.async { + completion(.success(.multipleChoice(resultSubs))) + } + case .alreadySubscribed: + DispatchQueue.main.async { + completion(.success(.alreadySubscribed)) + } + case .notFound: + DispatchQueue.main.async { + completion(.success(.notFound)) + } + } + case .failure(let error): + DispatchQueue.main.async { + completion(.failure(error)) + } + } + + } + + } + + func renameFeed(for account: Account, with feed: Feed, to name: String, completion: @escaping (Result) -> Void) { + + caller.renameFeed(feedID: feed.feedID, newName: name) { result in + switch result { + case .success: + DispatchQueue.main.async { + feed.editedName = name + completion(.success(())) + } + case .failure(let error): + DispatchQueue.main.async { + completion(.failure(error)) + } + } + } + + } + func accountDidInitialize(_ account: Account) { credentials = try? account.retrieveBasicCredentials() accountMetadata = account.metadata @@ -282,7 +335,7 @@ private extension FeedbinAccountDelegate { feed.name = subscription.name feed.homePageURL = subscription.homePageURL } else { - let feed = account.createFeed(with: subscription.name, editedName: nil, url: subscription.url, feedID: subFeedId, homePageURL: subscription.homePageURL) + let feed = account.createFeed(with: subscription.name, url: subscription.url, feedID: subFeedId, homePageURL: subscription.homePageURL) account.addFeed(feed, to: nil) } } diff --git a/Frameworks/Account/Feedbin/FeedbinSubscription.swift b/Frameworks/Account/Feedbin/FeedbinSubscription.swift index 7294d3778..d16328995 100644 --- a/Frameworks/Account/Feedbin/FeedbinSubscription.swift +++ b/Frameworks/Account/Feedbin/FeedbinSubscription.swift @@ -29,11 +29,15 @@ struct FeedbinSubscription: Codable { } struct FeedbinCreateSubscription: Codable { - let feedURL: String - enum CodingKeys: String, CodingKey { case feedURL = "feed_url" } - +} + +struct FeedbinUpdateSubscription: Codable { + let title: String + enum CodingKeys: String, CodingKey { + case title + } } diff --git a/Shared/Add Feed/InitialFeedDownloader.swift b/Frameworks/Account/LocalAccount/InitialFeedDownloader.swift similarity index 100% rename from Shared/Add Feed/InitialFeedDownloader.swift rename to Frameworks/Account/LocalAccount/InitialFeedDownloader.swift diff --git a/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift b/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift index 56f727c06..e359870b4 100644 --- a/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift +++ b/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift @@ -9,6 +9,10 @@ import Foundation import RSWeb +public enum LocalAccountDelegateError: String, Error { + case invalidParameter = "An invalid parameter was used." +} + final class LocalAccountDelegate: AccountDelegate { let supportsSubFolders = false @@ -16,6 +20,10 @@ final class LocalAccountDelegate: AccountDelegate { var credentials: Credentials? var accountMetadata: AccountMetadata? + private weak var account: Account? + private var feedFinder: FeedFinder? + private var createFeedCompletion: ((Result) -> Void)? + private let refresher = LocalAccountRefresher() var refreshProgress: DownloadProgress { @@ -38,6 +46,25 @@ final class LocalAccountDelegate: AccountDelegate { completion(.success(())) } + func createFeed(for account: Account, with name: String?, url urlString: String, completion: @escaping (Result) -> Void) { + + guard let url = URL(string: urlString) else { + completion(.failure(LocalAccountDelegateError.invalidParameter)) + return + } + + self.account = account + createFeedCompletion = completion + + feedFinder = FeedFinder(url: url, delegate: self) + + } + + func renameFeed(for account: Account, with feed: Feed, to name: String, completion: @escaping (Result) -> Void) { + feed.editedName = name + completion(.success(())) + } + func accountDidInitialize(_ account: Account) { } @@ -46,3 +73,37 @@ final class LocalAccountDelegate: AccountDelegate { } } + +extension LocalAccountDelegate: FeedFinderDelegate { + + // MARK: FeedFinderDelegate + + public func feedFinder(_ feedFinder: FeedFinder, didFindFeeds feedSpecifiers: Set) { + + if let error = feedFinder.initialDownloadError { + if feedFinder.initialDownloadStatusCode == 404 { + createFeedCompletion!(.success(.notFound)) + } else { + createFeedCompletion!(.failure(error)) + } + return + } + + guard let bestFeedSpecifier = FeedSpecifier.bestFeed(in: feedSpecifiers), + let url = URL(string: bestFeedSpecifier.urlString), + let account = account else { + createFeedCompletion!(.success(.notFound)) + return + } + + let feed = account.createFeed(with: nil, url: url.absoluteString, feedID: url.absoluteString, homePageURL: nil) + InitialFeedDownloader.download(url) { [weak self] parsedFeed in + if let parsedFeed = parsedFeed { + account.update(feed, with: parsedFeed, {}) + } + self?.createFeedCompletion!(.success(.created(feed))) + } + + } + +} diff --git a/Mac/MainWindow/AddFeed/AddFeedController.swift b/Mac/MainWindow/AddFeed/AddFeedController.swift index 5626320cd..c1c71a88f 100644 --- a/Mac/MainWindow/AddFeed/AddFeedController.swift +++ b/Mac/MainWindow/AddFeed/AddFeedController.swift @@ -21,19 +21,12 @@ import RSParser // Else, // display error sheet. -class AddFeedController: AddFeedWindowControllerDelegate, FeedFinderDelegate { +class AddFeedController: AddFeedWindowControllerDelegate { private let hostWindow: NSWindow private var addFeedWindowController: AddFeedWindowController? - private var userEnteredURL: URL? - private var userEnteredFolder: Folder? - private var userEnteredTitle: String? - private var userEnteredAccount: Account? private var foundFeedURLString: String? private var titleFromFeed: String? - private var feedFinder: FeedFinder? - private var isFindingFeed = false - private var bestFeedSpecifier: FeedSpecifier? init(hostWindow: NSWindow) { @@ -66,12 +59,30 @@ class AddFeedController: AddFeedWindowControllerDelegate, FeedFinderDelegate { return } - userEnteredAccount = account - userEnteredURL = url - userEnteredFolder = folder - userEnteredTitle = title - - findFeed() + account.createFeed(with: nil, url: url.absoluteString) { [weak self] result in + + self?.endShowingProgress() + + switch result { + case .success(let createFeedResult): + switch createFeedResult { + case .created(let feed): + self?.processFeed(feed, account: account, folder: folder, url: url, title: title) + case .multipleChoice(let feedChoices): + print() + case .alreadySubscribed: + self?.showAlreadySubscribedError(url.absoluteString) + case .notFound: + self?.showNoFeedsErrorMessage() + } + case .failure(let error): + NSApplication.shared.presentError(error) + } + + } + + beginShowingProgress() + } func addFeedWindowControllerUserDidCancel(_: AddFeedWindowController) { @@ -79,46 +90,8 @@ class AddFeedController: AddFeedWindowControllerDelegate, FeedFinderDelegate { closeAddFeedSheet(NSApplication.ModalResponse.cancel) } - // MARK: FeedFinderDelegate - - public func feedFinder(_ feedFinder: FeedFinder, didFindFeeds feedSpecifiers: Set) { - - isFindingFeed = false - endShowingProgress() - - if let error = feedFinder.initialDownloadError { - if feedFinder.initialDownloadStatusCode == 404 { - showNoFeedsErrorMessage() - } - else { - showInitialDownloadError(error) - } - return - } - - guard let bestFeedSpecifier = FeedSpecifier.bestFeed(in: feedSpecifiers) else { - showNoFeedsErrorMessage() - return - } - - self.bestFeedSpecifier = bestFeedSpecifier - self.foundFeedURLString = bestFeedSpecifier.urlString - - if let url = URL(string: bestFeedSpecifier.urlString) { - - InitialFeedDownloader.download(url) { (parsedFeed) in - self.titleFromFeed = parsedFeed?.title - self.addFeedIfPossible(parsedFeed) - } - } - else { - // Shouldn't happen. - showNoFeedsErrorMessage() - } - } } - private extension AddFeedController { var urlStringFromPasteboard: String? { @@ -151,50 +124,27 @@ private extension AddFeedController { } } - - func addFeedIfPossible(_ parsedFeed: ParsedFeed?) { - - // Add feed if not already subscribed-to. - - guard let account = userEnteredAccount else { - assertionFailure("Expected account.") - return + 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) + } + } } - guard let feedURLString = foundFeedURLString else { - assertionFailure("Expected feedURLString.") - return - } - - if account.hasFeed(withURL: feedURLString) { - showAlreadySubscribedError(feedURLString) - return - } - - let feed = account.createFeed(with: titleFromFeed, editedName: userEnteredTitle, url: feedURLString) - - if let parsedFeed = parsedFeed { - account.update(feed, with: parsedFeed, {}) - } - - account.addFeed(feed, to: userEnteredFolder) + + // TODO: make this async and add to above code + account.addFeed(feed, to: folder) + + // Move this into the mess above NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed]) - } - - // MARK: Find Feeds - - func findFeed() { - - guard let url = userEnteredURL else { - assertionFailure("Expected userEnteredURL.") - return - } - isFindingFeed = true - feedFinder = FeedFinder(url: url, delegate: self) - - beginShowingProgress() } - + // MARK: Errors func showAlreadySubscribedError(_ urlString: String) { diff --git a/Mac/Scriptability/Feed+Scriptability.swift b/Mac/Scriptability/Feed+Scriptability.swift index 87c674b0c..5e40cae0c 100644 --- a/Mac/Scriptability/Feed+Scriptability.swift +++ b/Mac/Scriptability/Feed+Scriptability.swift @@ -59,16 +59,6 @@ class ScriptableFeed: NSObject, UniqueIdScriptingObject, ScriptingObjectContaine // MARK: --- handle NSCreateCommand --- - class func parsedFeedForURL(_ urlString:String, _ completionHandler: @escaping (_ parsedFeed: ParsedFeed?) -> Void) { - guard let url = URL(string: urlString) else { - completionHandler(nil) - return - } - InitialFeedDownloader.download(url) { (parsedFeed) in - completionHandler(parsedFeed) - } - } - class func urlForNewFeed(arguments:[String:Any]) -> String? { var url:String? if let withDataParam = arguments["ObjectData"] { @@ -110,23 +100,36 @@ class ScriptableFeed: NSObject, UniqueIdScriptingObject, ScriptingObjectContaine // suspendExecution(). When we get the callback, we can supply the event result and call resumeExecution() command.suspendExecution() - self.parsedFeedForURL(url, { (parsedFeedOptional) in - if let parsedFeed = parsedFeedOptional { - let titleFromFeed = parsedFeed.title - - let feed = account.createFeed(with: titleFromFeed, editedName: titleFromArgs, url: url) - account.update(feed, with:parsedFeed, {}) - - // add the feed, putting it in a folder if needed - account.addFeed(feed, to:folder) - NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed]) + account.createFeed(with: nil, url: url) { result in + switch result { + case .success(let createFeedResult): - let scriptableFeed = self.scriptableFeed(feed, account:account, folder:folder) - command.resumeExecution(withResult:scriptableFeed.objectSpecifier) - } else { - command.resumeExecution(withResult:nil) - } - }) + switch createFeedResult { + case .created(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, to:folder) + + 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) + + default: + command.resumeExecution(withResult:nil) + } + + case .failure: + command.resumeExecution(withResult:nil) + } + + } + return nil } diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 69e52b3ee..b65eca789 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -112,7 +112,6 @@ 51C452AC22650FD200C03939 /* AppNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842E45CD1ED8C308000A8B52 /* AppNotifications.swift */; }; 51C452AE2265104D00C03939 /* TimelineStringFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97731ED9EC04007D329B /* TimelineStringFormatter.swift */; }; 51C452AF2265108300C03939 /* ArticleArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F204DF1FAACBB30076E152 /* ArticleArray.swift */; }; - 51C452B1226510E600C03939 /* InitialFeedDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97A01ED9F180007D329B /* InitialFeedDownloader.swift */; }; 51C452B42265141B00C03939 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 51C452B32265141B00C03939 /* WebKit.framework */; }; 51C452B82265178500C03939 /* styleSheet.css in Resources */ = {isa = PBXBuildFile; fileRef = 51C452B72265178500C03939 /* styleSheet.css */; }; 51D5948722668EFA00DFC836 /* MarkStatusCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84702AA31FA27AC0006B8943 /* MarkStatusCommand.swift */; }; @@ -217,7 +216,6 @@ 849A978A1ED9ECEF007D329B /* ArticleStylesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97881ED9ECEF007D329B /* ArticleStylesManager.swift */; }; 849A97981ED9EFAA007D329B /* Node-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97971ED9EFAA007D329B /* Node-Extensions.swift */; }; 849A979F1ED9F130007D329B /* SidebarCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A979E1ED9F130007D329B /* SidebarCell.swift */; }; - 849A97A21ED9F180007D329B /* InitialFeedDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97A01ED9F180007D329B /* InitialFeedDownloader.swift */; }; 849A97A31ED9F180007D329B /* FolderTreeControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97A11ED9F180007D329B /* FolderTreeControllerDelegate.swift */; }; 849C64681ED37A5D003D8FC0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 849C64671ED37A5D003D8FC0 /* Assets.xcassets */; }; 849EE70F203919360082A1EA /* AppImages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849EE70E203919360082A1EA /* AppImages.swift */; }; @@ -275,12 +273,6 @@ 84F2D5371FC22FCC00998D64 /* PseudoFeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F2D5351FC22FCB00998D64 /* PseudoFeed.swift */; }; 84F2D5381FC22FCC00998D64 /* TodayFeedDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F2D5361FC22FCB00998D64 /* TodayFeedDelegate.swift */; }; 84F2D53A1FC2308B00998D64 /* UnreadFeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F2D5391FC2308B00998D64 /* UnreadFeed.swift */; }; - 84F3EE1620DEC97E003FADEB /* FeedFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F3EE0620DEC97E003FADEB /* FeedFinder.swift */; }; - 84F3EE1720DEC97E003FADEB /* FeedFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F3EE0620DEC97E003FADEB /* FeedFinder.swift */; }; - 84F3EE1820DEC97E003FADEB /* FeedSpecifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F3EE0720DEC97E003FADEB /* FeedSpecifier.swift */; }; - 84F3EE1920DEC97E003FADEB /* FeedSpecifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F3EE0720DEC97E003FADEB /* FeedSpecifier.swift */; }; - 84F3EE1A20DEC97E003FADEB /* HTMLFeedFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F3EE0820DEC97E003FADEB /* HTMLFeedFinder.swift */; }; - 84F3EE1B20DEC97E003FADEB /* HTMLFeedFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F3EE0820DEC97E003FADEB /* HTMLFeedFinder.swift */; }; 84F9EAE5213660A100CF2DE4 /* AppleScriptXCTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F9EAD1213660A100CF2DE4 /* AppleScriptXCTestCase.swift */; }; 84F9EAE6213660A100CF2DE4 /* ScriptingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F9EAD2213660A100CF2DE4 /* ScriptingTests.swift */; }; 84F9EAE7213660A100CF2DE4 /* testNameOfAuthors.applescript in Sources */ = {isa = PBXBuildFile; fileRef = 84F9EAD4213660A100CF2DE4 /* testNameOfAuthors.applescript */; }; @@ -801,7 +793,6 @@ 849A97881ED9ECEF007D329B /* ArticleStylesManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArticleStylesManager.swift; sourceTree = ""; }; 849A97971ED9EFAA007D329B /* Node-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Node-Extensions.swift"; sourceTree = ""; }; 849A979E1ED9F130007D329B /* SidebarCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SidebarCell.swift; sourceTree = ""; }; - 849A97A01ED9F180007D329B /* InitialFeedDownloader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InitialFeedDownloader.swift; sourceTree = ""; }; 849A97A11ED9F180007D329B /* FolderTreeControllerDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FolderTreeControllerDelegate.swift; sourceTree = ""; }; 849C64601ED37A5D003D8FC0 /* NetNewsWire.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NetNewsWire.app; sourceTree = BUILT_PRODUCTS_DIR; }; 849C64671ED37A5D003D8FC0 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -859,9 +850,6 @@ 84F2D5351FC22FCB00998D64 /* PseudoFeed.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PseudoFeed.swift; sourceTree = ""; }; 84F2D5361FC22FCB00998D64 /* TodayFeedDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TodayFeedDelegate.swift; sourceTree = ""; }; 84F2D5391FC2308B00998D64 /* UnreadFeed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnreadFeed.swift; sourceTree = ""; }; - 84F3EE0620DEC97E003FADEB /* FeedFinder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedFinder.swift; sourceTree = ""; }; - 84F3EE0720DEC97E003FADEB /* FeedSpecifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedSpecifier.swift; sourceTree = ""; }; - 84F3EE0820DEC97E003FADEB /* HTMLFeedFinder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTMLFeedFinder.swift; sourceTree = ""; }; 84F9EAD1213660A100CF2DE4 /* AppleScriptXCTestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppleScriptXCTestCase.swift; sourceTree = ""; }; 84F9EAD2213660A100CF2DE4 /* ScriptingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScriptingTests.swift; sourceTree = ""; }; 84F9EAD4213660A100CF2DE4 /* testNameOfAuthors.applescript */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.applescript; path = testNameOfAuthors.applescript; sourceTree = ""; }; @@ -1117,14 +1105,6 @@ path = Timeline; sourceTree = ""; }; - 51C452B0226510CF00C03939 /* Add Feed */ = { - isa = PBXGroup; - children = ( - 849A97A01ED9F180007D329B /* InitialFeedDownloader.swift */, - ); - path = "Add Feed"; - sourceTree = ""; - }; 51C452B22265141B00C03939 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -1544,7 +1524,6 @@ 841D4D5E2106B3E100DD04E6 /* ArticlesDatabase.xcodeproj */, 842E45CD1ED8C308000A8B52 /* AppNotifications.swift */, 51C452AD2265102800C03939 /* Timeline */, - 51C452B0226510CF00C03939 /* Add Feed */, 84702AB31FA27AE8006B8943 /* Commands */, 51C452A822650DA100C03939 /* Article Rendering */, 849A97861ED9ECEF007D329B /* Article Styles */, @@ -1554,7 +1533,6 @@ 848F6AE31FC29CFA002D422E /* Favicons */, 845213211FCA5B10003B6E93 /* Images */, 8426119C1FCB6ED40086A189 /* HTMLMetadata */, - 84F3EE0420DEC97E003FADEB /* FeedFinder */, 5183CCEA226F70350010922C /* Refresh */, 849A97561ED9EB0D007D329B /* Data */, 512E08DD22687FA000BDCFDD /* Tree */, @@ -1704,16 +1682,6 @@ path = SmartFeeds; sourceTree = ""; }; - 84F3EE0420DEC97E003FADEB /* FeedFinder */ = { - isa = PBXGroup; - children = ( - 84F3EE0620DEC97E003FADEB /* FeedFinder.swift */, - 84F3EE0720DEC97E003FADEB /* FeedSpecifier.swift */, - 84F3EE0820DEC97E003FADEB /* HTMLFeedFinder.swift */, - ); - path = FeedFinder; - sourceTree = ""; - }; 84F9EACF213660A100CF2DE4 /* NetNewsWireTests */ = { isa = PBXGroup; children = ( @@ -2291,7 +2259,6 @@ 51F85BF92274AA7B00C787DC /* UIBarButtonItem-Extensions.swift in Sources */, 51C45296226509D300C03939 /* OPMLExporter.swift in Sources */, 51C4525B226508DA00C03939 /* UIImage-Extensions.swift in Sources */, - 84F3EE1720DEC97E003FADEB /* FeedFinder.swift in Sources */, 51C45291226509C800C03939 /* SmartFeed.swift in Sources */, 51C452A722650A3D00C03939 /* RSImage-Extensions.swift in Sources */, 51C45269226508F600C03939 /* MasterFeedTableViewCell.swift in Sources */, @@ -2320,7 +2287,6 @@ 51F85BE7227245FC00C787DC /* AboutViewController.swift in Sources */, 51C4529E22650A1900C03939 /* ImageDownloader.swift in Sources */, 51C45292226509C800C03939 /* TodayFeedDelegate.swift in Sources */, - 84F3EE1B20DEC97E003FADEB /* HTMLFeedFinder.swift in Sources */, 51C452A222650A1900C03939 /* RSHTMLMetadata+Extension.swift in Sources */, 5183CCE5226F4DFA0010922C /* RefreshInterval.swift in Sources */, 51EF0F7C2277919E0050506E /* TimelineNumberOfLinesViewController.swift in Sources */, @@ -2336,7 +2302,6 @@ 51EF0F802277A8330050506E /* MasterTimelineCellLayout.swift in Sources */, 51F85BF722749FA100C787DC /* UIFont-Extensions.swift in Sources */, 51C452AF2265108300C03939 /* ArticleArray.swift in Sources */, - 84F3EE1920DEC97E003FADEB /* FeedSpecifier.swift in Sources */, 51C4528E2265099C00C03939 /* SmartFeedsController.swift in Sources */, 51C4529C22650A1000C03939 /* SingleFaviconDownloader.swift in Sources */, 51C45290226509C100C03939 /* PseudoFeed.swift in Sources */, @@ -2355,7 +2320,6 @@ 512E09012268907400BDCFDD /* MasterFeedTableViewSectionHeader.swift in Sources */, 51C45268226508F600C03939 /* MasterFeedUnreadCountView.swift in Sources */, 5183CCD0226E1E880010922C /* NonIntrinsicLabel.swift in Sources */, - 51C452B1226510E600C03939 /* InitialFeedDownloader.swift in Sources */, 51C4529F22650A1900C03939 /* AuthorAvatarDownloader.swift in Sources */, 51C452A322650A1E00C03939 /* HTMLMetadataDownloader.swift in Sources */, 51C4528D2265095F00C03939 /* AddFolderViewController.swift in Sources */, @@ -2442,11 +2406,9 @@ 5144EA51227B8E4500D19003 /* AccountsFeedbinWindowController.swift in Sources */, 84AD1EBC2032AF5C00BC20B7 /* SidebarOutlineDataSource.swift in Sources */, 845A29241FC9255E007B49E3 /* SidebarCellAppearance.swift in Sources */, - 84F3EE1620DEC97E003FADEB /* FeedFinder.swift in Sources */, 845EE7B11FC2366500854A1F /* StarredFeedDelegate.swift in Sources */, 848F6AE51FC29CFB002D422E /* FaviconDownloader.swift in Sources */, 84C9FC7722629E1200D921D6 /* AdvancedPreferencesViewController.swift in Sources */, - 84F3EE1820DEC97E003FADEB /* FeedSpecifier.swift in Sources */, 849EE72120391F560082A1EA /* SharingServicePickerDelegate.swift in Sources */, 849A97981ED9EFAA007D329B /* Node-Extensions.swift in Sources */, 849EE70F203919360082A1EA /* AppImages.swift in Sources */, @@ -2472,7 +2434,6 @@ 841ABA6020145EC100980E11 /* BuiltinSmartFeedInspectorViewController.swift in Sources */, D5E4CC54202C1361009B4FFC /* AppDelegate+Scriptability.swift in Sources */, D5F4EDB5200744A700B9E363 /* ScriptingObject.swift in Sources */, - 84F3EE1A20DEC97E003FADEB /* HTMLFeedFinder.swift in Sources */, D5F4EDB920074D7C00B9E363 /* Folder+Scriptability.swift in Sources */, 842611A01FCB72600086A189 /* FeaturedImageDownloader.swift in Sources */, 849A97781ED9EC04007D329B /* TimelineCellLayout.swift in Sources */, @@ -2498,7 +2459,6 @@ 84E185B3203B74E500F69BFA /* SingleLineTextFieldSizer.swift in Sources */, 849A977A1ED9EC04007D329B /* TimelineTableCellView.swift in Sources */, 849A97761ED9EC04007D329B /* TimelineCellAppearance.swift in Sources */, - 849A97A21ED9F180007D329B /* InitialFeedDownloader.swift in Sources */, 849A977F1ED9EC42007D329B /* ArticleRenderer.swift in Sources */, 84C9FC7822629E1200D921D6 /* GeneralPrefencesViewController.swift in Sources */, ); diff --git a/iOS/Add/AddFeedViewController.swift b/iOS/Add/AddFeedViewController.swift index cbb2ddd94..dbcab8fcd 100644 --- a/iOS/Add/AddFeedViewController.swift +++ b/iOS/Add/AddFeedViewController.swift @@ -21,15 +21,6 @@ class AddFeedViewController: UITableViewController, AddContainerViewControllerCh private var pickerData: AddFeedFolderPickerData! - private var feedFinder: FeedFinder? - private var userEnteredURL: URL? - private var userEnteredFolder: Folder? - private var userEnteredTitle: String? - private var userEnteredAccount: Account? - private var foundFeedURLString: String? - private var bestFeedSpecifier: FeedSpecifier? - private var titleFromFeed: String? - private var userCancelled = false weak var delegate: AddContainerViewControllerChildDelegate? @@ -78,31 +69,50 @@ class AddFeedViewController: UITableViewController, AddContainerViewControllerCh return } - userEnteredURL = url - userEnteredTitle = nameTextField.text - let container = pickerData.containers[folderPickerView.selectedRow(inComponent: 0)] - if let account = container as? Account { - userEnteredAccount = account + + var account: Account? + var folder: Folder? + if let containerAccount = container as? Account { + account = containerAccount } - if let folder = container as? Folder, let account = folder.account { - userEnteredAccount = account - userEnteredFolder = folder + if let containerFolder = container as? Folder, let containerAccount = containerFolder.account { + account = containerAccount + folder = containerFolder } - guard let userEnteredAccount = userEnteredAccount else { - assertionFailure() - return - } - - if userEnteredAccount.hasFeed(withURL: url.absoluteString) { + if account!.hasFeed(withURL: url.absoluteString) { showAlreadySubscribedError() return } + let title = nameTextField.text + delegate?.processingDidBegin() - feedFinder = FeedFinder(url: url, delegate: self) + account!.createFeed(with: nil, url: url.absoluteString) { [weak self] result in + + switch result { + case .success(let createFeedResult): + switch createFeedResult { + case .created(let feed): + self?.processFeed(feed, account: account!, folder: folder, url: url, title: title) + case .multipleChoice(let feedChoices): + print() + self?.delegate?.processingDidCancel() + case .alreadySubscribed: + self?.showAlreadySubscribedError() + self?.delegate?.processingDidCancel() + case .notFound: + self?.showNoFeedsErrorMessage() + self?.delegate?.processingDidCancel() + } + case .failure(let error): + self?.presentError(error) + self?.delegate?.processingDidCancel() + } + + } } @@ -132,49 +142,6 @@ extension AddFeedViewController: UIPickerViewDataSource, UIPickerViewDelegate { } -extension AddFeedViewController: FeedFinderDelegate { - - public func feedFinder(_ feedFinder: FeedFinder, didFindFeeds feedSpecifiers: Set) { - - if userCancelled { - return - } - - if let error = feedFinder.initialDownloadError { - if feedFinder.initialDownloadStatusCode == 404 { - showNoFeedsErrorMessage() - delegate?.processingDidCancel() - } else { - showInitialDownloadError(error) - delegate?.processingDidCancel() - } - return - } - - guard let bestFeedSpecifier = FeedSpecifier.bestFeed(in: feedSpecifiers) else { - showNoFeedsErrorMessage() - delegate?.processingDidCancel() - return - } - - self.bestFeedSpecifier = bestFeedSpecifier - self.foundFeedURLString = bestFeedSpecifier.urlString - - if let url = URL(string: bestFeedSpecifier.urlString) { - InitialFeedDownloader.download(url) { (parsedFeed) in - self.titleFromFeed = parsedFeed?.title - self.addFeedIfPossible(parsedFeed) - } - } else { - // Shouldn't happen. - showNoFeedsErrorMessage() - delegate?.processingDidCancel() - } - - } - -} - private extension AddFeedViewController { private func showAlreadySubscribedError() { @@ -196,41 +163,27 @@ private extension AddFeedViewController { presentError(title: title, message: message as String) } - func addFeedIfPossible(_ parsedFeed: ParsedFeed?) { + func processFeed(_ feed: Feed, account: Account, folder: Folder?, url: URL, title: String?) { - if userCancelled { - return - } - - guard let account = userEnteredAccount else { - assertionFailure("Expected account.") - delegate?.processingDidCancel() - return + 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) + } + } } - guard let feedURLString = foundFeedURLString else { - assertionFailure("Expected feedURLString.") - delegate?.processingDidCancel() - return - } + // TODO: make this async and add to above code + account.addFeed(feed, to: folder) - if account.hasFeed(withURL: feedURLString) { - showAlreadySubscribedError() - delegate?.processingDidCancel() - return - } - - let feed = account.createFeed(with: titleFromFeed, editedName: userEnteredTitle, url: feedURLString) - - if let parsedFeed = parsedFeed { - account.update(feed, with: parsedFeed, {}) - } - - account.addFeed(feed, to: userEnteredFolder) + // Move this into the mess above NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed]) delegate?.processingDidEnd() - + } }