mirror of
https://github.com/Ranchero-Software/NetNewsWire.git
synced 2025-02-09 08:39:00 +01:00
Add Feed Provider support to the CloudKit account type.
This commit is contained in:
parent
3e7a3b001b
commit
ba73881f21
@ -212,72 +212,20 @@ final class CloudKitAccountDelegate: AccountDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func createWebFeed(for account: Account, url urlString: String, name: String?, container: Container, completion: @escaping (Result<WebFeed, Error>) -> Void) {
|
func createWebFeed(for account: Account, url urlString: String, name: String?, container: Container, completion: @escaping (Result<WebFeed, Error>) -> Void) {
|
||||||
let editedName = name == nil || name!.isEmpty ? nil : name
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
BatchUpdate.shared.start()
|
let editedName = name == nil || name!.isEmpty ? nil : name
|
||||||
refreshProgress.addToNumberOfTasksAndRemaining(3)
|
|
||||||
FeedFinder.find(url: url) { result in
|
|
||||||
|
|
||||||
self.refreshProgress.completeTask()
|
|
||||||
switch result {
|
|
||||||
case .success(let feedSpecifiers):
|
|
||||||
guard let bestFeedSpecifier = FeedSpecifier.bestFeed(in: feedSpecifiers), let url = URL(string: bestFeedSpecifier.urlString) else {
|
|
||||||
BatchUpdate.shared.end()
|
|
||||||
self.refreshProgress.clear()
|
|
||||||
completion(.failure(AccountError.createErrorNotFound))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if account.hasWebFeed(withURL: bestFeedSpecifier.urlString) {
|
|
||||||
BatchUpdate.shared.end()
|
|
||||||
self.refreshProgress.clear()
|
|
||||||
completion(.failure(AccountError.createErrorAlreadySubscribed))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
self.accountZone.createWebFeed(url: bestFeedSpecifier.urlString, editedName: editedName, container: container) { result in
|
|
||||||
|
|
||||||
self.refreshProgress.completeTask()
|
// Username should be part of the URL on new feed adds
|
||||||
switch result {
|
if let feedProvider = FeedProviderManager.shared.best(for: urlComponents, with: nil) {
|
||||||
case .success(let externalID):
|
createProviderWebFeed(for: account, urlComponents: urlComponents, editedName: editedName, container: container, feedProvider: feedProvider, completion: completion)
|
||||||
|
} else {
|
||||||
let feed = account.createWebFeed(with: nil, url: url.absoluteString, webFeedID: url.absoluteString, homePageURL: nil)
|
createRSSWebFeed(for: account, url: url, editedName: editedName, container: container, completion: completion)
|
||||||
feed.editedName = editedName
|
|
||||||
feed.externalID = externalID
|
|
||||||
container.addWebFeed(feed)
|
|
||||||
|
|
||||||
InitialFeedDownloader.download(url) { parsedFeed in
|
|
||||||
self.refreshProgress.completeTask()
|
|
||||||
|
|
||||||
if let parsedFeed = parsedFeed {
|
|
||||||
account.update(feed, with: parsedFeed, {_ in
|
|
||||||
BatchUpdate.shared.end()
|
|
||||||
completion(.success(feed))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
case .failure(let error):
|
|
||||||
BatchUpdate.shared.end()
|
|
||||||
self.refreshProgress.clear()
|
|
||||||
completion(.failure(error))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case .failure:
|
|
||||||
BatchUpdate.shared.end()
|
|
||||||
self.refreshProgress.clear()
|
|
||||||
completion(.failure(AccountError.createErrorNotFound))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
@ -594,7 +542,7 @@ private extension CloudKitAccountDelegate {
|
|||||||
|
|
||||||
self.refreshProgress.completeTask()
|
self.refreshProgress.completeTask()
|
||||||
|
|
||||||
self.refresher.refreshFeeds(webFeeds) {
|
self.refreshWebFeeds(account, webFeeds) {
|
||||||
account.metadata.lastArticleFetchEndTime = Date()
|
account.metadata.lastArticleFetchEndTime = Date()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -615,6 +563,158 @@ private extension CloudKitAccountDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func refreshWebFeeds(_ account: Account, _ webFeeds: Set<WebFeed>, completion: @escaping () -> Void) {
|
||||||
|
var refresherWebFeeds = Set<WebFeed>()
|
||||||
|
let group = DispatchGroup()
|
||||||
|
|
||||||
|
for webFeed in webFeeds {
|
||||||
|
if let components = URLComponents(string: webFeed.url), let feedProvider = FeedProviderManager.shared.best(for: components, with: webFeed.username) {
|
||||||
|
refreshProgress.addToNumberOfTasksAndRemaining(1)
|
||||||
|
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)
|
||||||
|
self.refreshProgress.completeTask()
|
||||||
|
group.leave()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
refresherWebFeeds.insert(webFeed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshProgress.addToNumberOfTasksAndRemaining(refresherWebFeeds.count)
|
||||||
|
group.enter()
|
||||||
|
refresher.refreshFeeds(refresherWebFeeds) {
|
||||||
|
group.leave()
|
||||||
|
}
|
||||||
|
|
||||||
|
group.notify(queue: DispatchQueue.main) {
|
||||||
|
completion()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createProviderWebFeed(for account: Account, urlComponents: URLComponents, editedName: String?, container: Container, feedProvider: FeedProvider, completion: @escaping (Result<WebFeed, Error>) -> Void) {
|
||||||
|
refreshProgress.addToNumberOfTasksAndRemaining(3)
|
||||||
|
|
||||||
|
feedProvider.assignName(urlComponents) { result in
|
||||||
|
self.refreshProgress.completeTask()
|
||||||
|
switch result {
|
||||||
|
|
||||||
|
case .success(let name):
|
||||||
|
|
||||||
|
// Move the user to the WebFeed and out of the URL
|
||||||
|
var newURLComponents = urlComponents
|
||||||
|
newURLComponents.user = nil
|
||||||
|
guard let newURLString = newURLComponents.url?.absoluteString else {
|
||||||
|
completion(.failure(AccountError.createErrorNotFound))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.accountZone.createWebFeed(url: newURLString, editedName: editedName, container: container) { result in
|
||||||
|
|
||||||
|
self.refreshProgress.completeTask()
|
||||||
|
switch result {
|
||||||
|
case .success(let externalID):
|
||||||
|
|
||||||
|
let feed = account.createWebFeed(with: name, url: newURLString, webFeedID: newURLString, homePageURL: nil)
|
||||||
|
feed.editedName = editedName
|
||||||
|
feed.username = urlComponents.user
|
||||||
|
feed.externalID = externalID
|
||||||
|
container.addWebFeed(feed)
|
||||||
|
|
||||||
|
feedProvider.refresh(feed) { result in
|
||||||
|
self.refreshProgress.completeTask()
|
||||||
|
switch result {
|
||||||
|
case .success(let parsedItems):
|
||||||
|
account.update(newURLString, with: parsedItems) { _ in
|
||||||
|
completion(.success(feed))
|
||||||
|
}
|
||||||
|
case .failure:
|
||||||
|
completion(.failure(AccountError.createErrorNotFound))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case .failure(let error):
|
||||||
|
self.refreshProgress.clear()
|
||||||
|
completion(.failure(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case .failure(let error):
|
||||||
|
self.refreshProgress.clear()
|
||||||
|
completion(.failure(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createRSSWebFeed(for account: Account, url: URL, editedName: String?, container: Container, completion: @escaping (Result<WebFeed, Error>) -> Void) {
|
||||||
|
BatchUpdate.shared.start()
|
||||||
|
refreshProgress.addToNumberOfTasksAndRemaining(3)
|
||||||
|
FeedFinder.find(url: url) { result in
|
||||||
|
|
||||||
|
self.refreshProgress.completeTask()
|
||||||
|
switch result {
|
||||||
|
case .success(let feedSpecifiers):
|
||||||
|
guard let bestFeedSpecifier = FeedSpecifier.bestFeed(in: feedSpecifiers), let url = URL(string: bestFeedSpecifier.urlString) else {
|
||||||
|
BatchUpdate.shared.end()
|
||||||
|
self.refreshProgress.clear()
|
||||||
|
completion(.failure(AccountError.createErrorNotFound))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if account.hasWebFeed(withURL: bestFeedSpecifier.urlString) {
|
||||||
|
BatchUpdate.shared.end()
|
||||||
|
self.refreshProgress.clear()
|
||||||
|
completion(.failure(AccountError.createErrorAlreadySubscribed))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.accountZone.createWebFeed(url: bestFeedSpecifier.urlString, editedName: editedName, container: container) { result in
|
||||||
|
|
||||||
|
self.refreshProgress.completeTask()
|
||||||
|
switch result {
|
||||||
|
case .success(let externalID):
|
||||||
|
|
||||||
|
let feed = account.createWebFeed(with: nil, url: url.absoluteString, webFeedID: url.absoluteString, homePageURL: nil)
|
||||||
|
feed.editedName = editedName
|
||||||
|
feed.externalID = externalID
|
||||||
|
container.addWebFeed(feed)
|
||||||
|
|
||||||
|
InitialFeedDownloader.download(url) { parsedFeed in
|
||||||
|
self.refreshProgress.completeTask()
|
||||||
|
|
||||||
|
if let parsedFeed = parsedFeed {
|
||||||
|
account.update(feed, with: parsedFeed, {_ in
|
||||||
|
BatchUpdate.shared.end()
|
||||||
|
completion(.success(feed))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
case .failure(let error):
|
||||||
|
BatchUpdate.shared.end()
|
||||||
|
self.refreshProgress.clear()
|
||||||
|
completion(.failure(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case .failure:
|
||||||
|
BatchUpdate.shared.end()
|
||||||
|
self.refreshProgress.clear()
|
||||||
|
completion(.failure(AccountError.createErrorNotFound))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func processAccountError(_ account: Account, _ error: Error) {
|
func processAccountError(_ account: Account, _ error: Error) {
|
||||||
if case CloudKitZoneError.userDeletedZone = error {
|
if case CloudKitZoneError.userDeletedZone = error {
|
||||||
account.removeFeeds(account.topLevelWebFeeds)
|
account.removeFeeds(account.topLevelWebFeeds)
|
||||||
|
@ -180,28 +180,61 @@ private extension CloudKitAcountZoneDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func createWebFeedIfNecessary(url: URL, editedName: String?, webFeedExternalID: String, container: Container, completion: @escaping (WebFeed) -> Void) {
|
func createWebFeedIfNecessary(url: URL, editedName: String?, webFeedExternalID: String, container: Container, completion: @escaping (WebFeed) -> Void) {
|
||||||
guard let account = account else { return }
|
guard let account = account, let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) else { return }
|
||||||
|
|
||||||
if let webFeed = account.existingWebFeed(withExternalID: webFeedExternalID) {
|
if let webFeed = account.existingWebFeed(withExternalID: webFeedExternalID) {
|
||||||
completion(webFeed)
|
completion(webFeed)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let webFeed = account.createWebFeed(with: editedName, url: url.absoluteString, webFeedID: url.absoluteString, homePageURL: nil)
|
let webFeed = account.createWebFeed(with: nil, url: url.absoluteString, webFeedID: url.absoluteString, homePageURL: nil)
|
||||||
webFeed.editedName = editedName
|
webFeed.editedName = editedName
|
||||||
webFeed.externalID = webFeedExternalID
|
webFeed.externalID = webFeedExternalID
|
||||||
container.addWebFeed(webFeed)
|
|
||||||
|
|
||||||
refreshProgress?.addToNumberOfTasksAndRemaining(1)
|
if let feedProvider = FeedProviderManager.shared.best(for: urlComponents, with: nil) {
|
||||||
InitialFeedDownloader.download(url) { parsedFeed in
|
|
||||||
self.refreshProgress?.completeTask()
|
refreshProgress?.addToNumberOfTasksAndRemaining(2)
|
||||||
if let parsedFeed = parsedFeed {
|
feedProvider.assignName(urlComponents) { result in
|
||||||
account.update(webFeed, with: parsedFeed, { _ in
|
self.refreshProgress?.completeTask()
|
||||||
|
switch result {
|
||||||
|
case .success(let name):
|
||||||
|
|
||||||
|
webFeed.name = name
|
||||||
|
container.addWebFeed(webFeed)
|
||||||
|
|
||||||
|
feedProvider.refresh(webFeed) { result in
|
||||||
|
self.refreshProgress?.completeTask()
|
||||||
|
switch result {
|
||||||
|
case .success(let parsedItems):
|
||||||
|
account.update(url.absoluteString, with: parsedItems) { _ in
|
||||||
|
completion(webFeed)
|
||||||
|
}
|
||||||
|
case .failure:
|
||||||
|
completion(webFeed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case .failure:
|
||||||
completion(webFeed)
|
completion(webFeed)
|
||||||
})
|
}
|
||||||
} else {
|
|
||||||
completion(webFeed)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
container.addWebFeed(webFeed)
|
||||||
|
|
||||||
|
refreshProgress?.addToNumberOfTasksAndRemaining(1)
|
||||||
|
InitialFeedDownloader.download(url) { parsedFeed in
|
||||||
|
self.refreshProgress?.completeTask()
|
||||||
|
if let parsedFeed = parsedFeed {
|
||||||
|
account.update(webFeed, with: parsedFeed, { _ in
|
||||||
|
completion(webFeed)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
completion(webFeed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -84,6 +84,7 @@ final class LocalAccountDelegate: AccountDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group.notify(queue: DispatchQueue.main) {
|
group.notify(queue: DispatchQueue.main) {
|
||||||
|
account.metadata.lastArticleFetchEndTime = Date()
|
||||||
completion(.success(()))
|
completion(.success(()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,9 +148,9 @@ final class LocalAccountDelegate: AccountDelegate {
|
|||||||
|
|
||||||
// Username should be part of the URL on new feed adds
|
// Username should be part of the URL on new feed adds
|
||||||
if let feedProvider = FeedProviderManager.shared.best(for: urlComponents, with: nil) {
|
if let feedProvider = FeedProviderManager.shared.best(for: urlComponents, with: nil) {
|
||||||
createProviderWebFeed(for: account, urlComponents: urlComponents, name: name, container: container, feedProvider: feedProvider, completion: completion)
|
createProviderWebFeed(for: account, urlComponents: urlComponents, editedName: name, container: container, feedProvider: feedProvider, completion: completion)
|
||||||
} else {
|
} else {
|
||||||
createRSSWebFeed(for: account, url: url, name: name, container: container, completion: completion)
|
createRSSWebFeed(for: account, url: url, editedName: name, container: container, completion: completion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,14 +245,13 @@ extension LocalAccountDelegate: LocalAccountRefresherDelegate {
|
|||||||
|
|
||||||
func localAccountRefresherDidFinish(_ refresher: LocalAccountRefresher) {
|
func localAccountRefresherDidFinish(_ refresher: LocalAccountRefresher) {
|
||||||
self.refreshProgress.clear()
|
self.refreshProgress.clear()
|
||||||
account?.metadata.lastArticleFetchEndTime = Date()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension LocalAccountDelegate {
|
private extension LocalAccountDelegate {
|
||||||
|
|
||||||
func createProviderWebFeed(for account: Account, urlComponents: URLComponents, name: String?, container: Container, feedProvider: FeedProvider, completion: @escaping (Result<WebFeed, Error>) -> Void) {
|
func createProviderWebFeed(for account: Account, urlComponents: URLComponents, editedName: String?, container: Container, feedProvider: FeedProvider, completion: @escaping (Result<WebFeed, Error>) -> Void) {
|
||||||
refreshProgress.addToNumberOfTasksAndRemaining(2)
|
refreshProgress.addToNumberOfTasksAndRemaining(2)
|
||||||
|
|
||||||
feedProvider.assignName(urlComponents) { result in
|
feedProvider.assignName(urlComponents) { result in
|
||||||
@ -269,7 +269,7 @@ private extension LocalAccountDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let feed = account.createWebFeed(with: name, url: newURLString, webFeedID: newURLString, homePageURL: nil)
|
let feed = account.createWebFeed(with: name, url: newURLString, webFeedID: newURLString, homePageURL: nil)
|
||||||
feed.editedName = name
|
feed.editedName = editedName
|
||||||
feed.username = urlComponents.user
|
feed.username = urlComponents.user
|
||||||
container.addWebFeed(feed)
|
container.addWebFeed(feed)
|
||||||
|
|
||||||
@ -291,7 +291,7 @@ private extension LocalAccountDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createRSSWebFeed(for account: Account, url: URL, name: 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
|
||||||
// container before the name has been downloaded. This will put it in the sidebar
|
// container before the name has been downloaded. This will put it in the sidebar
|
||||||
@ -318,7 +318,7 @@ private extension LocalAccountDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let feed = account.createWebFeed(with: nil, url: url.absoluteString, webFeedID: url.absoluteString, homePageURL: nil)
|
let feed = account.createWebFeed(with: nil, url: url.absoluteString, webFeedID: url.absoluteString, homePageURL: nil)
|
||||||
feed.editedName = name
|
feed.editedName = editedName
|
||||||
container.addWebFeed(feed)
|
container.addWebFeed(feed)
|
||||||
|
|
||||||
InitialFeedDownloader.download(url) { parsedFeed in
|
InitialFeedDownloader.download(url) { parsedFeed in
|
||||||
|
Loading…
x
Reference in New Issue
Block a user