Remove FeedProviderManager and Feed Provider and remove references to them.
This commit is contained in:
parent
de1dca3912
commit
3aeda820e4
|
@ -176,20 +176,14 @@ final class CloudKitAccountDelegate: AccountDelegate {
|
|||
}
|
||||
|
||||
func createWebFeed(for account: Account, url urlString: String, name: String?, container: Container, validateFeed: Bool, completion: @escaping (Result<WebFeed, Error>) -> Void) {
|
||||
guard let url = URL(string: urlString), let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
|
||||
guard let url = URL(string: urlString) else {
|
||||
completion(.failure(LocalAccountDelegateError.invalidParameter))
|
||||
return
|
||||
}
|
||||
|
||||
let editedName = name == nil || name!.isEmpty ? nil : name
|
||||
|
||||
// Username should be part of the URL on new feed adds
|
||||
if let feedProvider = FeedProviderManager.shared.best(for: urlComponents) {
|
||||
createProviderWebFeed(for: account, urlComponents: urlComponents, editedName: editedName, container: container, feedProvider: feedProvider, completion: completion)
|
||||
} else {
|
||||
createRSSWebFeed(for: account, url: url, editedName: editedName, container: container, validateFeed: validateFeed, completion: completion)
|
||||
}
|
||||
|
||||
createRSSWebFeed(for: account, url: url, editedName: editedName, container: container, validateFeed: validateFeed, completion: completion)
|
||||
}
|
||||
|
||||
func renameWebFeed(for account: Account, with feed: WebFeed, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
|
@ -570,119 +564,15 @@ private extension CloudKitAccountDelegate {
|
|||
|
||||
func combinedRefresh(_ account: Account, _ webFeeds: Set<WebFeed>, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
|
||||
var refresherWebFeeds = Set<WebFeed>()
|
||||
let group = DispatchGroup()
|
||||
var feedProviderError: Error? = nil
|
||||
|
||||
for webFeed in webFeeds {
|
||||
if let components = URLComponents(string: webFeed.url), let feedProvider = FeedProviderManager.shared.best(for: components) {
|
||||
group.enter()
|
||||
feedProvider.refresh(webFeed) { result in
|
||||
switch result {
|
||||
case .success(let parsedItems):
|
||||
|
||||
account.update(webFeed.webFeedID, with: parsedItems) { result in
|
||||
switch result {
|
||||
case .success(let articleChanges):
|
||||
self.storeArticleChanges(new: articleChanges.newArticles, updated: articleChanges.updatedArticles, deleted: articleChanges.deletedArticles) {
|
||||
self.refreshProgress.completeTask()
|
||||
group.leave()
|
||||
}
|
||||
case .failure(let error):
|
||||
os_log(.error, log: self.log, "CloudKit Feed refresh update error: %@.", error.localizedDescription)
|
||||
self.refreshProgress.completeTask()
|
||||
group.leave()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
case .failure(let error):
|
||||
os_log(.error, log: self.log, "CloudKit Feed refresh error: %@.", error.localizedDescription)
|
||||
feedProviderError = error
|
||||
self.refreshProgress.completeTask()
|
||||
group.leave()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
refresherWebFeeds.insert(webFeed)
|
||||
}
|
||||
}
|
||||
|
||||
group.enter()
|
||||
refresher.refreshFeeds(refresherWebFeeds) {
|
||||
refresher.refreshFeeds(webFeeds) {
|
||||
group.leave()
|
||||
}
|
||||
|
||||
group.notify(queue: DispatchQueue.main) {
|
||||
if let error = feedProviderError {
|
||||
completion(.failure(error))
|
||||
} else {
|
||||
completion(.success(()))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func createProviderWebFeed(for account: Account, urlComponents: URLComponents, editedName: String?, container: Container, feedProvider: FeedProvider, completion: @escaping (Result<WebFeed, Error>) -> Void) {
|
||||
refreshProgress.addToNumberOfTasksAndRemaining(5)
|
||||
|
||||
feedProvider.metaData(urlComponents) { result in
|
||||
self.refreshProgress.completeTask()
|
||||
switch result {
|
||||
|
||||
case .success(let metaData):
|
||||
|
||||
guard let urlString = urlComponents.url?.absoluteString else {
|
||||
self.refreshProgress.completeTasks(4)
|
||||
completion(.failure(AccountError.createErrorNotFound))
|
||||
return
|
||||
}
|
||||
|
||||
self.accountZone.createWebFeed(url: urlString, name: metaData.name, editedName: editedName, homePageURL: metaData.homePageURL, container: container) { result in
|
||||
|
||||
self.refreshProgress.completeTask()
|
||||
switch result {
|
||||
case .success(let externalID):
|
||||
|
||||
let feed = account.createWebFeed(with: metaData.name, url: urlString, webFeedID: urlString, homePageURL: metaData.homePageURL)
|
||||
feed.editedName = editedName
|
||||
feed.externalID = externalID
|
||||
container.addWebFeed(feed)
|
||||
|
||||
feedProvider.refresh(feed) { result in
|
||||
self.refreshProgress.completeTask()
|
||||
switch result {
|
||||
case .success(let parsedItems):
|
||||
|
||||
account.update(urlString, with: parsedItems) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
self.sendNewArticlesToTheCloud(account, feed)
|
||||
self.refreshProgress.clear()
|
||||
completion(.success(feed))
|
||||
case .failure(let error):
|
||||
self.refreshProgress.completeTasks(2)
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
case .failure:
|
||||
self.refreshProgress.completeTasks(3)
|
||||
completion(.failure(AccountError.createErrorNotFound))
|
||||
}
|
||||
}
|
||||
|
||||
case .failure(let error):
|
||||
self.refreshProgress.completeTasks(4)
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
case .failure:
|
||||
self.refreshProgress.completeTasks(4)
|
||||
completion(.failure(AccountError.createErrorNotFound))
|
||||
}
|
||||
completion(.success(()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
//
|
||||
// FeedProvider.swift
|
||||
// FeedProvider
|
||||
//
|
||||
// Created by Maurice Parker on 4/6/20.
|
||||
// Copyright © 2020 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RSCore
|
||||
import RSParser
|
||||
|
||||
public enum FeedProviderAbility {
|
||||
case owner
|
||||
case available
|
||||
case none
|
||||
}
|
||||
|
||||
public struct FeedProviderFeedMetaData {
|
||||
let name: String
|
||||
let homePageURL: String?
|
||||
}
|
||||
|
||||
public protocol FeedProvider {
|
||||
|
||||
/// Informs the caller of the ability for this feed provider to service the given URL
|
||||
func ability(_ urlComponents: URLComponents) -> FeedProviderAbility
|
||||
|
||||
/// Provide the iconURL of the given URL
|
||||
func iconURL(_ urlComponents: URLComponents, completion: @escaping (Result<String, Error>) -> Void)
|
||||
|
||||
/// Construct the associated metadata for the new feed
|
||||
func metaData(_ urlComponents: URLComponents, completion: @escaping (Result<FeedProviderFeedMetaData, Error>) -> Void)
|
||||
|
||||
/// Refresh all the article entries (ParsedItems)
|
||||
func refresh(_ webFeed: WebFeed, completion: @escaping (Result<Set<ParsedItem>, Error>) -> Void)
|
||||
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
//
|
||||
// FeedProviderManager.swift
|
||||
// Account
|
||||
//
|
||||
// Created by Maurice Parker on 4/16/20.
|
||||
// Copyright © 2020 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol FeedProviderManagerDelegate: AnyObject {
|
||||
var activeFeedProviders: [FeedProvider] { get }
|
||||
}
|
||||
|
||||
public final class FeedProviderManager {
|
||||
|
||||
public static let shared = FeedProviderManager()
|
||||
public weak var delegate: FeedProviderManagerDelegate?
|
||||
|
||||
public func best(for offered: URLComponents) -> FeedProvider? {
|
||||
if let owner = feedProviderMatching(offered, ability: .owner) {
|
||||
return owner
|
||||
}
|
||||
return feedProviderMatching(offered, ability: .available)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private extension FeedProviderManager {
|
||||
|
||||
func feedProviderMatching(_ offered: URLComponents, ability: FeedProviderAbility) -> FeedProvider? {
|
||||
if let delegate = delegate {
|
||||
for feedProvider in delegate.activeFeedProviders {
|
||||
if feedProvider.ability(offered) == ability {
|
||||
return feedProvider
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
}
|
|
@ -50,48 +50,20 @@ final class LocalAccountDelegate: AccountDelegate {
|
|||
return
|
||||
}
|
||||
|
||||
var refresherWebFeeds = Set<WebFeed>()
|
||||
let webFeeds = account.flattenedWebFeeds()
|
||||
refreshProgress.addToNumberOfTasksAndRemaining(webFeeds.count)
|
||||
|
||||
let group = DispatchGroup()
|
||||
var feedProviderError: Error? = nil
|
||||
|
||||
for webFeed in webFeeds {
|
||||
if let components = URLComponents(string: webFeed.url), let feedProvider = FeedProviderManager.shared.best(for: components) {
|
||||
group.enter()
|
||||
feedProvider.refresh(webFeed) { result in
|
||||
switch result {
|
||||
case .success(let parsedItems):
|
||||
account.update(webFeed.webFeedID, with: parsedItems) { _ in
|
||||
self.refreshProgress.completeTask()
|
||||
group.leave()
|
||||
}
|
||||
case .failure(let error):
|
||||
os_log(.error, log: self.log, "Feed Provider refresh error: %@.", error.localizedDescription)
|
||||
feedProviderError = error
|
||||
self.refreshProgress.completeTask()
|
||||
group.leave()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
refresherWebFeeds.insert(webFeed)
|
||||
}
|
||||
}
|
||||
|
||||
group.enter()
|
||||
refresher?.refreshFeeds(refresherWebFeeds) {
|
||||
refresher?.refreshFeeds(webFeeds) {
|
||||
group.leave()
|
||||
}
|
||||
|
||||
group.notify(queue: DispatchQueue.main) {
|
||||
self.refreshProgress.clear()
|
||||
account.metadata.lastArticleFetchEndTime = Date()
|
||||
if let error = feedProviderError {
|
||||
completion(.failure(error))
|
||||
} else {
|
||||
completion(.success(()))
|
||||
}
|
||||
completion(.success(()))
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -156,12 +128,7 @@ final class LocalAccountDelegate: AccountDelegate {
|
|||
return
|
||||
}
|
||||
|
||||
// Username should be part of the URL on new feed adds
|
||||
if let feedProvider = FeedProviderManager.shared.best(for: urlComponents) {
|
||||
createProviderWebFeed(for: account, urlComponents: urlComponents, editedName: name, container: container, feedProvider: feedProvider, completion: completion)
|
||||
} else {
|
||||
createRSSWebFeed(for: account, url: url, editedName: name, container: container, completion: completion)
|
||||
}
|
||||
createRSSWebFeed(for: account, url: url, editedName: name, container: container, completion: completion)
|
||||
}
|
||||
|
||||
func renameWebFeed(for account: Account, with feed: WebFeed, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
|
@ -264,44 +231,6 @@ extension LocalAccountDelegate: LocalAccountRefresherDelegate {
|
|||
|
||||
private extension LocalAccountDelegate {
|
||||
|
||||
func createProviderWebFeed(for account: Account, urlComponents: URLComponents, editedName: String?, container: Container, feedProvider: FeedProvider, completion: @escaping (Result<WebFeed, Error>) -> Void) {
|
||||
refreshProgress.addToNumberOfTasksAndRemaining(2)
|
||||
|
||||
feedProvider.metaData(urlComponents) { result in
|
||||
self.refreshProgress.completeTask()
|
||||
switch result {
|
||||
|
||||
case .success(let metaData):
|
||||
|
||||
guard let urlString = urlComponents.url?.absoluteString else {
|
||||
completion(.failure(AccountError.createErrorNotFound))
|
||||
return
|
||||
}
|
||||
|
||||
let feed = account.createWebFeed(with: metaData.name, url: urlString, webFeedID: urlString, homePageURL: metaData.homePageURL)
|
||||
feed.editedName = editedName
|
||||
container.addWebFeed(feed)
|
||||
|
||||
feedProvider.refresh(feed) { result in
|
||||
self.refreshProgress.completeTask()
|
||||
switch result {
|
||||
case .success(let parsedItems):
|
||||
account.update(urlString, with: parsedItems) { _ in
|
||||
completion(.success(feed))
|
||||
}
|
||||
case .failure(let error):
|
||||
self.refreshProgress.clear()
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
case .failure:
|
||||
self.refreshProgress.clear()
|
||||
completion(.failure(AccountError.createErrorNotFound))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func createRSSWebFeed(for account: Account, url: URL, editedName: String?, container: Container, completion: @escaping (Result<WebFeed, Error>) -> Void) {
|
||||
|
||||
// We need to use a batch update here because we need to assign add the feed to the
|
||||
|
|
|
@ -234,10 +234,7 @@ public final class WebFeed: Feed, Renamable, Hashable {
|
|||
return false
|
||||
}
|
||||
|
||||
if FeedProviderManager.shared.best(for: components) == nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -123,7 +123,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
|
|||
SecretsManager.provider = Secrets()
|
||||
AccountManager.shared = AccountManager(accountsFolder: Platform.dataSubfolder(forApplication: nil, folderName: "Accounts")!)
|
||||
ArticleThemesManager.shared = ArticleThemesManager(folderPath: Platform.dataSubfolder(forApplication: nil, folderName: "Themes")!)
|
||||
FeedProviderManager.shared.delegate = ExtensionPointManager.shared
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(inspectableObjectsDidChange(_:)), name: .InspectableObjectsDidChange, object: nil)
|
||||
|
|
|
@ -27,7 +27,7 @@ public enum ExtensionPointManagerError: LocalizedError {
|
|||
}
|
||||
|
||||
|
||||
final class ExtensionPointManager: FeedProviderManagerDelegate {
|
||||
final class ExtensionPointManager {
|
||||
|
||||
static let shared = ExtensionPointManager()
|
||||
|
||||
|
@ -65,10 +65,6 @@ final class ExtensionPointManager: FeedProviderManagerDelegate {
|
|||
return commands
|
||||
}
|
||||
|
||||
var activeFeedProviders: [FeedProvider] {
|
||||
return activeExtensionPoints.values.compactMap({ return $0 as? FeedProvider })
|
||||
}
|
||||
|
||||
init() {
|
||||
possibleExtensionPointTypes = []
|
||||
loadExtensionPoints()
|
||||
|
@ -126,14 +122,4 @@ private extension ExtensionPointManager {
|
|||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
func feedProviderMatching(_ offered: URLComponents, ability: FeedProviderAbility) -> FeedProvider? {
|
||||
for extensionPoint in activeExtensionPoints.values {
|
||||
if let feedProvider = extensionPoint as? FeedProvider, feedProvider.ability(offered) == ability {
|
||||
return feedProvider
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -118,31 +118,7 @@ public final class WebFeedIconDownloader {
|
|||
return nil
|
||||
}
|
||||
|
||||
if let components = URLComponents(string: feed.url), let feedProvider = FeedProviderManager.shared.best(for: components) {
|
||||
guard !urlsInProgress.contains(feed.url) else {
|
||||
return nil
|
||||
}
|
||||
urlsInProgress.insert(feed.url)
|
||||
|
||||
feedProvider.iconURL(components) { result in
|
||||
self.urlsInProgress.remove(feed.url)
|
||||
switch result {
|
||||
case .success(let feedProviderURL):
|
||||
self.feedURLToIconURLCache[feed.url] = feedProviderURL
|
||||
self.feedURLToIconURLCacheDirty = true
|
||||
self.icon(forURL: feedProviderURL, feed: feed) { (image) in
|
||||
if let image = image {
|
||||
self.postFeedIconDidBecomeAvailableNotification(feed)
|
||||
self.cache[feed] = IconImage(image)
|
||||
}
|
||||
}
|
||||
case .failure:
|
||||
checkFeedIconURL()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
checkFeedIconURL()
|
||||
}
|
||||
checkFeedIconURL()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue