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) {
|
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))
|
completion(.failure(LocalAccountDelegateError.invalidParameter))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let editedName = name == nil || name!.isEmpty ? nil : name
|
let editedName = name == nil || name!.isEmpty ? nil : name
|
||||||
|
|
||||||
// Username should be part of the URL on new feed adds
|
createRSSWebFeed(for: account, url: url, editedName: editedName, container: container, validateFeed: validateFeed, completion: completion)
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func renameWebFeed(for account: Account, with feed: WebFeed, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
|
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) {
|
func combinedRefresh(_ account: Account, _ webFeeds: Set<WebFeed>, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||||
|
|
||||||
var refresherWebFeeds = Set<WebFeed>()
|
|
||||||
let group = DispatchGroup()
|
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()
|
group.enter()
|
||||||
refresher.refreshFeeds(refresherWebFeeds) {
|
refresher.refreshFeeds(webFeeds) {
|
||||||
group.leave()
|
group.leave()
|
||||||
}
|
}
|
||||||
|
|
||||||
group.notify(queue: DispatchQueue.main) {
|
group.notify(queue: DispatchQueue.main) {
|
||||||
if let error = feedProviderError {
|
completion(.success(()))
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var refresherWebFeeds = Set<WebFeed>()
|
|
||||||
let webFeeds = account.flattenedWebFeeds()
|
let webFeeds = account.flattenedWebFeeds()
|
||||||
refreshProgress.addToNumberOfTasksAndRemaining(webFeeds.count)
|
refreshProgress.addToNumberOfTasksAndRemaining(webFeeds.count)
|
||||||
|
|
||||||
let group = DispatchGroup()
|
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()
|
group.enter()
|
||||||
refresher?.refreshFeeds(refresherWebFeeds) {
|
refresher?.refreshFeeds(webFeeds) {
|
||||||
group.leave()
|
group.leave()
|
||||||
}
|
}
|
||||||
|
|
||||||
group.notify(queue: DispatchQueue.main) {
|
group.notify(queue: DispatchQueue.main) {
|
||||||
self.refreshProgress.clear()
|
self.refreshProgress.clear()
|
||||||
account.metadata.lastArticleFetchEndTime = Date()
|
account.metadata.lastArticleFetchEndTime = Date()
|
||||||
if let error = feedProviderError {
|
completion(.success(()))
|
||||||
completion(.failure(error))
|
|
||||||
} else {
|
|
||||||
completion(.success(()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -156,12 +128,7 @@ final class LocalAccountDelegate: AccountDelegate {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Username should be part of the URL on new feed adds
|
createRSSWebFeed(for: account, url: url, editedName: name, container: container, completion: completion)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func renameWebFeed(for account: Account, with feed: WebFeed, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
|
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 {
|
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) {
|
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
|
// We need to use a batch update here because we need to assign add the feed to the
|
||||||
|
@ -233,11 +233,8 @@ public final class WebFeed: Feed, Renamable, Hashable {
|
|||||||
let components = URLComponents(url: webfeedURL, resolvingAgainstBaseURL: false) else {
|
let components = URLComponents(url: webfeedURL, resolvingAgainstBaseURL: false) else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if FeedProviderManager.shared.best(for: components) == nil {
|
return false
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +123,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
|
|||||||
SecretsManager.provider = Secrets()
|
SecretsManager.provider = Secrets()
|
||||||
AccountManager.shared = AccountManager(accountsFolder: Platform.dataSubfolder(forApplication: nil, folderName: "Accounts")!)
|
AccountManager.shared = AccountManager(accountsFolder: Platform.dataSubfolder(forApplication: nil, folderName: "Accounts")!)
|
||||||
ArticleThemesManager.shared = ArticleThemesManager(folderPath: Platform.dataSubfolder(forApplication: nil, folderName: "Themes")!)
|
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(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(inspectableObjectsDidChange(_:)), name: .InspectableObjectsDidChange, 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()
|
static let shared = ExtensionPointManager()
|
||||||
|
|
||||||
@ -65,10 +65,6 @@ final class ExtensionPointManager: FeedProviderManagerDelegate {
|
|||||||
return commands
|
return commands
|
||||||
}
|
}
|
||||||
|
|
||||||
var activeFeedProviders: [FeedProvider] {
|
|
||||||
return activeExtensionPoints.values.compactMap({ return $0 as? FeedProvider })
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
possibleExtensionPointTypes = []
|
possibleExtensionPointTypes = []
|
||||||
loadExtensionPoints()
|
loadExtensionPoints()
|
||||||
@ -126,14 +122,4 @@ private extension ExtensionPointManager {
|
|||||||
#endif
|
#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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if let components = URLComponents(string: feed.url), let feedProvider = FeedProviderManager.shared.best(for: components) {
|
checkFeedIconURL()
|
||||||
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()
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user