From a8dcf3eeee7d046f21d3e81e8399bc9c5169abfd Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Fri, 10 Apr 2020 17:23:39 -0500 Subject: [PATCH] Add the unread statuses on receipt to CloudKit. --- Frameworks/Account/Account.swift | 10 ++-- .../CloudKit/CloudKitAccountDelegate.swift | 49 ++++++++++++------- .../CloudKitArticlesZoneDelegate.swift | 4 +- .../LocalAccount/LocalAccountDelegate.swift | 4 ++ .../LocalAccount/LocalAccountRefresher.swift | 10 ++-- .../ArticlesDatabase/ArticlesDatabase.swift | 10 ++++ 6 files changed, 59 insertions(+), 28 deletions(-) diff --git a/Frameworks/Account/Account.swift b/Frameworks/Account/Account.swift index ecc3f2b6e..8086be584 100644 --- a/Frameworks/Account/Account.swift +++ b/Frameworks/Account/Account.swift @@ -715,7 +715,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, webFeedDictionariesNeedUpdate = true } - func update(_ webFeed: WebFeed, with parsedFeed: ParsedFeed, _ completion: @escaping DatabaseCompletionBlock) { + func update(_ webFeed: WebFeed, with parsedFeed: ParsedFeed, _ completion: @escaping UpdateArticlesCompletionBlock) { // Used only by an On My Mac or iCloud account. precondition(Thread.isMainThread) precondition(type == .onMyMac || type == .cloudKit) @@ -723,14 +723,14 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, webFeed.takeSettings(from: parsedFeed) let parsedItems = parsedFeed.items guard !parsedItems.isEmpty else { - completion(nil) + completion(.success(NewAndUpdatedArticles())) return } update(webFeed.webFeedID, with: parsedItems, completion: completion) } - func update(_ webFeedID: String, with parsedItems: Set, completion: @escaping DatabaseCompletionBlock) { + func update(_ webFeedID: String, with parsedItems: Set, completion: @escaping UpdateArticlesCompletionBlock) { // Used only by an On My Mac or iCloud account. precondition(Thread.isMainThread) precondition(type == .onMyMac || type == .cloudKit) @@ -739,9 +739,9 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, switch updateArticlesResult { case .success(let newAndUpdatedArticles): self.sendNotificationAbout(newAndUpdatedArticles) - completion(nil) + completion(.success(newAndUpdatedArticles)) case .failure(let databaseError): - completion(databaseError) + completion(.failure(databaseError)) } } } diff --git a/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift b/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift index 4342525d9..d20e23e30 100644 --- a/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift +++ b/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift @@ -13,6 +13,7 @@ import SyncDatabase import RSCore import RSParser import Articles +import ArticlesDatabase import RSWeb public enum CloudKitAccountDelegateError: String, Error { @@ -50,7 +51,6 @@ final class CloudKitAccountDelegate: AccountDelegate { var accountMetadata: AccountMetadata? var refreshProgress = DownloadProgress(numberOfTasks: 0) - var refreshAllCompletion: ((Result) -> Void)? = nil init(dataFolder: String) { accountZone = CloudKitAccountZone(container: container) @@ -79,13 +79,12 @@ final class CloudKitAccountDelegate: AccountDelegate { } func refreshAll(for account: Account, completion: @escaping (Result) -> Void) { - guard refreshAllCompletion == nil else { + guard refreshProgress.isComplete else { completion(.success(())) return } - refreshAllCompletion = completion - refreshAll(for: account, downloadFeeds: true) + refreshAll(for: account, downloadFeeds: true, completion: completion) } func sendArticleStatus(for account: Account, completion: @escaping ((Result) -> Void)) { @@ -155,11 +154,10 @@ final class CloudKitAccountDelegate: AccountDelegate { } func importOPML(for account:Account, opmlFile: URL, completion: @escaping (Result) -> Void) { - guard refreshAllCompletion == nil else { + guard refreshProgress.isComplete else { completion(.success(())) return } - refreshAllCompletion = completion var fileData: Data? @@ -214,7 +212,7 @@ final class CloudKitAccountDelegate: AccountDelegate { } self.accountZone.importOPML(rootExternalID: rootExternalID, items: normalizedItems) { _ in - self.refreshAll(for: account, downloadFeeds: false) + self.refreshAll(for: account, downloadFeeds: false, completion: completion) } } @@ -493,7 +491,7 @@ final class CloudKitAccountDelegate: AccountDelegate { switch result { case .success(let externalID): account.externalID = externalID - self.refreshAll(for: account, downloadFeeds: false) + self.refreshAll(for: account, downloadFeeds: false) { _ in } case .failure(let error): os_log(.error, log: self.log, "Error adding account container: %@", error.localizedDescription) } @@ -533,7 +531,7 @@ final class CloudKitAccountDelegate: AccountDelegate { private extension CloudKitAccountDelegate { - func refreshAll(for account: Account, downloadFeeds: Bool) { + func refreshAll(for account: Account, downloadFeeds: Bool, completion: @escaping (Result) -> Void) { let intialWebFeedsCount = downloadFeeds ? account.flattenedWebFeeds().count : 0 refreshProgress.addToNumberOfTasksAndRemaining(3 + intialWebFeedsCount) @@ -541,8 +539,7 @@ private extension CloudKitAccountDelegate { func fail(_ error: Error) { self.processAccountError(account, error) self.refreshProgress.clear() - self.refreshAllCompletion?(.failure(error)) - self.refreshAllCompletion = nil + completion(.failure(error)) } BatchUpdate.shared.start() @@ -569,12 +566,24 @@ private extension CloudKitAccountDelegate { self.refreshProgress.completeTask() guard downloadFeeds else { - self.refreshAllCompletion?(.success(())) - self.refreshAllCompletion = nil + completion(.success(())) return } - self.refresher?.refreshFeeds(webFeeds) + self.refresher?.refreshFeeds(webFeeds) { + + account.metadata.lastArticleFetchEndTime = Date() + + self.sendArticleStatus(for: account) { result in + switch result { + case .success: + completion(.success(())) + case .failure(let error): + fail(error) + } + } + + } case .failure(let error): fail(error) @@ -605,6 +614,15 @@ private extension CloudKitAccountDelegate { } extension CloudKitAccountDelegate: LocalAccountRefresherDelegate { + + func localAccountRefresher(_ refresher: LocalAccountRefresher, didProcess newAndUpdatedArticles: NewAndUpdatedArticles) { + if let newArticles = newAndUpdatedArticles.newArticles { + let syncStatuses = newArticles.map { article in + return SyncStatus(articleID: article.articleID, key: .read, flag: false) + } + database.insertStatuses(syncStatuses) + } + } func localAccountRefresher(_ refresher: LocalAccountRefresher, requestCompletedFor: WebFeed) { refreshProgress.completeTask() @@ -612,9 +630,6 @@ extension CloudKitAccountDelegate: LocalAccountRefresherDelegate { func localAccountRefresherDidFinish(_ refresher: LocalAccountRefresher) { self.refreshProgress.clear() - account?.metadata.lastArticleFetchEndTime = Date() - refreshAllCompletion?(.success(())) - refreshAllCompletion = nil } } diff --git a/Frameworks/Account/CloudKit/CloudKitArticlesZoneDelegate.swift b/Frameworks/Account/CloudKit/CloudKitArticlesZoneDelegate.swift index 12b807fb3..58469ebb2 100644 --- a/Frameworks/Account/CloudKit/CloudKitArticlesZoneDelegate.swift +++ b/Frameworks/Account/CloudKit/CloudKitArticlesZoneDelegate.swift @@ -138,9 +138,9 @@ private extension CloudKitArticlesZoneDelegate { for receivedStarredArticle in receivedStarredArticles { if let parsedItem = makeParsedItem(receivedStarredArticle) { group.enter() - self.account?.update(parsedItem.feedURL, with: Set([parsedItem])) { databaseError in + self.account?.update(parsedItem.feedURL, with: Set([parsedItem])) { result in group.leave() - if let databaseError = databaseError { + if case .failure(let databaseError) = result { os_log(.error, log: self.log, "Error occurred while storing starred items: %@", databaseError.localizedDescription) } } diff --git a/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift b/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift index dd7309c72..113841b64 100644 --- a/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift +++ b/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift @@ -10,6 +10,7 @@ import Foundation import RSCore import RSParser import Articles +import ArticlesDatabase import RSWeb public enum LocalAccountDelegateError: String, Error { @@ -233,6 +234,9 @@ final class LocalAccountDelegate: AccountDelegate { extension LocalAccountDelegate: LocalAccountRefresherDelegate { + func localAccountRefresher(_ refresher: LocalAccountRefresher, didProcess newAndUpdatedArticles: NewAndUpdatedArticles) { + } + func localAccountRefresher(_ refresher: LocalAccountRefresher, requestCompletedFor: WebFeed) { refreshProgress.completeTask() } diff --git a/Frameworks/Account/LocalAccount/LocalAccountRefresher.swift b/Frameworks/Account/LocalAccount/LocalAccountRefresher.swift index 508a6cf21..f109c7b59 100644 --- a/Frameworks/Account/LocalAccount/LocalAccountRefresher.swift +++ b/Frameworks/Account/LocalAccount/LocalAccountRefresher.swift @@ -11,8 +11,10 @@ import RSCore import RSParser import RSWeb import Articles +import ArticlesDatabase protocol LocalAccountRefresherDelegate { + func localAccountRefresher(_ refresher: LocalAccountRefresher, didProcess: NewAndUpdatedArticles) func localAccountRefresher(_ refresher: LocalAccountRefresher, requestCompletedFor: WebFeed) func localAccountRefresherDidFinish(_ refresher: LocalAccountRefresher) } @@ -97,12 +99,12 @@ extension LocalAccountRefresher: DownloadSessionDelegate { return } - account.update(feed, with: parsedFeed) { error in - if error == nil { + account.update(feed, with: parsedFeed) { result in + if case .success(let newAndUpdatedArticles) = result { + self.delegate?.localAccountRefresher(self, didProcess: newAndUpdatedArticles) if let httpResponse = response as? HTTPURLResponse { feed.conditionalGetInfo = HTTPConditionalGetInfo(urlResponse: httpResponse) } - feed.contentHash = dataHash } completion() @@ -157,9 +159,9 @@ extension LocalAccountRefresher: DownloadSessionDelegate { } func downloadSessionDidCompleteDownloadObjects(_ downloadSession: DownloadSession) { - delegate?.localAccountRefresherDidFinish(self) completions.forEach({ $0() }) completions = [() -> Void]() + delegate?.localAccountRefresherDidFinish(self) } } diff --git a/Frameworks/ArticlesDatabase/ArticlesDatabase.swift b/Frameworks/ArticlesDatabase/ArticlesDatabase.swift index fd30105c2..83e2411db 100644 --- a/Frameworks/ArticlesDatabase/ArticlesDatabase.swift +++ b/Frameworks/ArticlesDatabase/ArticlesDatabase.swift @@ -27,6 +27,16 @@ public typealias SingleUnreadCountCompletionBlock = (SingleUnreadCountResult) -> public struct NewAndUpdatedArticles { public let newArticles: Set
? public let updatedArticles: Set
? + + public init() { + self.newArticles = Set
() + self.updatedArticles = Set
() + } + + public init(newArticles: Set
?, updatedArticles: Set
?) { + self.newArticles = newArticles + self.updatedArticles = updatedArticles + } } public typealias UpdateArticlesResult = Result