diff --git a/Frameworks/Account/Account.swift b/Frameworks/Account/Account.swift index e3b2e4890..ecc3f2b6e 100644 --- a/Frameworks/Account/Account.swift +++ b/Frameworks/Account/Account.swift @@ -801,16 +801,16 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, /// Mark articleIDs statuses based on statusKey and flag. /// Will create statuses in the database and in memory as needed. Sends a .StatusesDidChange notification. /// Returns a set of new article statuses. - func markAndFetchNew(articleIDs: Set, statusKey: ArticleStatus.Key, flag: Bool, completion: ArticleStatusesResultBlock? = nil) { + func markAndFetchNew(articleIDs: Set, statusKey: ArticleStatus.Key, flag: Bool, completion: ArticleIDsCompletionBlock? = nil) { guard !articleIDs.isEmpty else { - completion?(.success(Set())) + completion?(.success(Set())) return } database.markAndFetchNew(articleIDs: articleIDs, statusKey: statusKey, flag: flag) { result in switch result { - case .success(let newArticleStatuses): + case .success(let newArticleStatusIDs): self.noteStatusesForArticleIDsDidChange(articleIDs) - completion?(.success(newArticleStatuses)) + completion?(.success(newArticleStatusIDs)) case .failure(let databaseError): completion?(.failure(databaseError)) } @@ -819,25 +819,25 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, /// Mark articleIDs as read. Will create statuses in the database and in memory as needed. Sends a .StatusesDidChange notification. /// Returns a set of new article statuses. - func markAsRead(_ articleIDs: Set, completion: ArticleStatusesResultBlock? = nil) { + func markAsRead(_ articleIDs: Set, completion: ArticleIDsCompletionBlock? = nil) { markAndFetchNew(articleIDs: articleIDs, statusKey: .read, flag: true, completion: completion) } /// Mark articleIDs as unread. Will create statuses in the database and in memory as needed. Sends a .StatusesDidChange notification. /// Returns a set of new article statuses. - func markAsUnread(_ articleIDs: Set, completion: ArticleStatusesResultBlock? = nil) { + func markAsUnread(_ articleIDs: Set, completion: ArticleIDsCompletionBlock? = nil) { markAndFetchNew(articleIDs: articleIDs, statusKey: .read, flag: false, completion: completion) } /// Mark articleIDs as starred. Will create statuses in the database and in memory as needed. Sends a .StatusesDidChange notification. /// Returns a set of new article statuses. - func markAsStarred(_ articleIDs: Set, completion: ArticleStatusesResultBlock? = nil) { + func markAsStarred(_ articleIDs: Set, completion: ArticleIDsCompletionBlock? = nil) { markAndFetchNew(articleIDs: articleIDs, statusKey: .starred, flag: true, completion: completion) } /// Mark articleIDs as unstarred. Will create statuses in the database and in memory as needed. Sends a .StatusesDidChange notification. /// Returns a set of new article statuses. - func markAsUnstarred(_ articleIDs: Set, completion: ArticleStatusesResultBlock? = nil) { + func markAsUnstarred(_ articleIDs: Set, completion: ArticleIDsCompletionBlock? = nil) { markAndFetchNew(articleIDs: articleIDs, statusKey: .starred, flag: false, completion: completion) } @@ -893,7 +893,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, public func debugDropConditionalGetInfo() { #if DEBUG - flattenedWebFeeds().forEach{ $0.debugDropConditionalGetInfo() } + flattenedWebFeeds().forEach{ $0.dropConditionalGetInfo() } #endif } diff --git a/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift b/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift index df9d5c6be..4342525d9 100644 --- a/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift +++ b/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift @@ -481,7 +481,11 @@ final class CloudKitAccountDelegate: AccountDelegate { self.account = account accountZone.delegate = CloudKitAcountZoneDelegate(account: account, refreshProgress: refreshProgress) - articlesZone.delegate = CloudKitArticlesZoneDelegate(account: account, database: database, articlesZone: articlesZone) + articlesZone.delegate = CloudKitArticlesZoneDelegate(account: account, + database: database, + articlesZone: articlesZone, + refresher: refresher, + refreshProgress: refreshProgress) // Check to see if this is a new account and initialize anything we need if account.externalID == nil { diff --git a/Frameworks/Account/CloudKit/CloudKitArticlesZoneDelegate.swift b/Frameworks/Account/CloudKit/CloudKitArticlesZoneDelegate.swift index 6b712b932..12b807fb3 100644 --- a/Frameworks/Account/CloudKit/CloudKitArticlesZoneDelegate.swift +++ b/Frameworks/Account/CloudKit/CloudKitArticlesZoneDelegate.swift @@ -9,6 +9,7 @@ import Foundation import os.log import RSParser +import RSWeb import CloudKit import SyncDatabase @@ -19,11 +20,15 @@ class CloudKitArticlesZoneDelegate: CloudKitZoneDelegate { weak var account: Account? var database: SyncDatabase weak var articlesZone: CloudKitArticlesZone? + weak var refresher: LocalAccountRefresher? + weak var refreshProgress: DownloadProgress? - init(account: Account, database: SyncDatabase, articlesZone: CloudKitArticlesZone) { + init(account: Account, database: SyncDatabase, articlesZone: CloudKitArticlesZone, refresher: LocalAccountRefresher?, refreshProgress: DownloadProgress?) { self.account = account self.database = database self.articlesZone = articlesZone + self.refresher = refresher + self.refreshProgress = refreshProgress } func cloudKitDidChange(record: CKRecord) { @@ -82,8 +87,37 @@ private extension CloudKitArticlesZoneDelegate { let group = DispatchGroup() group.enter() - account?.markAsUnread(updateableUnreadArticleIDs) { _ in - group.leave() + account?.markAsUnread(updateableUnreadArticleIDs) { result in + switch result { + case .success(let newArticleStatusIDs): + + if newArticleStatusIDs.isEmpty { + group.leave() + } else { + self.account?.fetchArticlesAsync(FetchType.articleIDs(newArticleStatusIDs)) { result in + switch result { + case .success(let articles): + + if articles.isEmpty { + group.leave() + } else { + let webFeeds = Set(articles.compactMap({ $0.webFeed })) + webFeeds.forEach { $0.dropConditionalGetInfo() } + self.refreshProgress?.addToNumberOfTasksAndRemaining(webFeeds.count) + self.refresher?.refreshFeeds(webFeeds) { + group.leave() + } + } + + case .failure: + group.leave() + } + } + } + + case .failure: + group.leave() + } } group.enter() @@ -119,7 +153,6 @@ private extension CloudKitArticlesZoneDelegate { } - func makeParsedItem(_ articleRecord: CKRecord) -> ParsedItem? { var parsedAuthors = Set() diff --git a/Frameworks/Account/LocalAccount/LocalAccountRefresher.swift b/Frameworks/Account/LocalAccount/LocalAccountRefresher.swift index cb8c2f24e..508a6cf21 100644 --- a/Frameworks/Account/LocalAccount/LocalAccountRefresher.swift +++ b/Frameworks/Account/LocalAccount/LocalAccountRefresher.swift @@ -19,6 +19,7 @@ protocol LocalAccountRefresherDelegate { final class LocalAccountRefresher { + private var completions = [() -> Void]() private var isSuspended = false var delegate: LocalAccountRefresherDelegate? @@ -26,7 +27,10 @@ final class LocalAccountRefresher { return DownloadSession(delegate: self) }() - public func refreshFeeds(_ feeds: Set) { + public func refreshFeeds(_ feeds: Set, completion: (() -> Void)? = nil) { + if let completion = completion { + completions.append(completion) + } downloadSession.downloadObjects(feeds as NSSet) } @@ -154,6 +158,8 @@ extension LocalAccountRefresher: DownloadSessionDelegate { func downloadSessionDidCompleteDownloadObjects(_ downloadSession: DownloadSession) { delegate?.localAccountRefresherDidFinish(self) + completions.forEach({ $0() }) + completions = [() -> Void]() } } diff --git a/Frameworks/Account/WebFeed.swift b/Frameworks/Account/WebFeed.swift index a63d28fb0..a964bc70b 100644 --- a/Frameworks/Account/WebFeed.swift +++ b/Frameworks/Account/WebFeed.swift @@ -221,9 +221,9 @@ public final class WebFeed: Feed, Renamable, Hashable { self.metadata = metadata } - // MARK: - Debug - - public func debugDropConditionalGetInfo() { + // MARK: - API + + public func dropConditionalGetInfo() { conditionalGetInfo = nil contentHash = nil } diff --git a/Frameworks/ArticlesDatabase/ArticlesDatabase.swift b/Frameworks/ArticlesDatabase/ArticlesDatabase.swift index 179b3928f..fd30105c2 100644 --- a/Frameworks/ArticlesDatabase/ArticlesDatabase.swift +++ b/Frameworks/ArticlesDatabase/ArticlesDatabase.swift @@ -222,7 +222,7 @@ public final class ArticlesDatabase { return try articlesTable.mark(articles, statusKey, flag) } - public func markAndFetchNew(articleIDs: Set, statusKey: ArticleStatus.Key, flag: Bool, completion: @escaping ArticleStatusesResultBlock) { + public func markAndFetchNew(articleIDs: Set, statusKey: ArticleStatus.Key, flag: Bool, completion: @escaping ArticleIDsCompletionBlock) { articlesTable.markAndFetchNew(articleIDs, statusKey, flag, completion) } diff --git a/Frameworks/ArticlesDatabase/ArticlesTable.swift b/Frameworks/ArticlesDatabase/ArticlesTable.swift index a6c72b790..899cc853b 100644 --- a/Frameworks/ArticlesDatabase/ArticlesTable.swift +++ b/Frameworks/ArticlesDatabase/ArticlesTable.swift @@ -418,7 +418,7 @@ final class ArticlesTable: DatabaseTable { return statuses } - func markAndFetchNew(_ articleIDs: Set, _ statusKey: ArticleStatus.Key, _ flag: Bool, _ completion: @escaping ArticleStatusesResultBlock) { + func markAndFetchNew(_ articleIDs: Set, _ statusKey: ArticleStatus.Key, _ flag: Bool, _ completion: @escaping ArticleIDsCompletionBlock) { queue.runInTransaction { databaseResult in switch databaseResult { case .success(let database): diff --git a/Frameworks/ArticlesDatabase/StatusesTable.swift b/Frameworks/ArticlesDatabase/StatusesTable.swift index aa8d3b711..5858bc743 100644 --- a/Frameworks/ArticlesDatabase/StatusesTable.swift +++ b/Frameworks/ArticlesDatabase/StatusesTable.swift @@ -85,11 +85,11 @@ final class StatusesTable: DatabaseTable { return updatedStatuses } - func markAndFetchNew(_ articleIDs: Set, _ statusKey: ArticleStatus.Key, _ flag: Bool, _ database: FMDatabase) -> Set { + func markAndFetchNew(_ articleIDs: Set, _ statusKey: ArticleStatus.Key, _ flag: Bool, _ database: FMDatabase) -> Set { let (statusesDictionary, newStatusIDs) = ensureStatusesForArticleIDs(articleIDs, flag, database) let statuses = Set(statusesDictionary.values) mark(statuses, statusKey, flag, database) - return Set(newStatusIDs.compactMap({ statusesDictionary[$0] })) + return newStatusIDs } // MARK: - Fetching