Add code to process new article entries.

This commit is contained in:
Maurice Parker 2020-04-10 16:25:58 -05:00
parent 4418a4bb02
commit 983138366f
8 changed files with 65 additions and 22 deletions

View File

@ -801,16 +801,16 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
/// Mark articleIDs statuses based on statusKey and flag. /// Mark articleIDs statuses based on statusKey and flag.
/// Will create statuses in the database and in memory as needed. Sends a .StatusesDidChange notification. /// Will create statuses in the database and in memory as needed. Sends a .StatusesDidChange notification.
/// Returns a set of new article statuses. /// Returns a set of new article statuses.
func markAndFetchNew(articleIDs: Set<String>, statusKey: ArticleStatus.Key, flag: Bool, completion: ArticleStatusesResultBlock? = nil) { func markAndFetchNew(articleIDs: Set<String>, statusKey: ArticleStatus.Key, flag: Bool, completion: ArticleIDsCompletionBlock? = nil) {
guard !articleIDs.isEmpty else { guard !articleIDs.isEmpty else {
completion?(.success(Set<ArticleStatus>())) completion?(.success(Set<String>()))
return return
} }
database.markAndFetchNew(articleIDs: articleIDs, statusKey: statusKey, flag: flag) { result in database.markAndFetchNew(articleIDs: articleIDs, statusKey: statusKey, flag: flag) { result in
switch result { switch result {
case .success(let newArticleStatuses): case .success(let newArticleStatusIDs):
self.noteStatusesForArticleIDsDidChange(articleIDs) self.noteStatusesForArticleIDsDidChange(articleIDs)
completion?(.success(newArticleStatuses)) completion?(.success(newArticleStatusIDs))
case .failure(let databaseError): case .failure(let databaseError):
completion?(.failure(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. /// 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. /// Returns a set of new article statuses.
func markAsRead(_ articleIDs: Set<String>, completion: ArticleStatusesResultBlock? = nil) { func markAsRead(_ articleIDs: Set<String>, completion: ArticleIDsCompletionBlock? = nil) {
markAndFetchNew(articleIDs: articleIDs, statusKey: .read, flag: true, completion: completion) 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. /// 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. /// Returns a set of new article statuses.
func markAsUnread(_ articleIDs: Set<String>, completion: ArticleStatusesResultBlock? = nil) { func markAsUnread(_ articleIDs: Set<String>, completion: ArticleIDsCompletionBlock? = nil) {
markAndFetchNew(articleIDs: articleIDs, statusKey: .read, flag: false, completion: completion) 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. /// 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. /// Returns a set of new article statuses.
func markAsStarred(_ articleIDs: Set<String>, completion: ArticleStatusesResultBlock? = nil) { func markAsStarred(_ articleIDs: Set<String>, completion: ArticleIDsCompletionBlock? = nil) {
markAndFetchNew(articleIDs: articleIDs, statusKey: .starred, flag: true, completion: completion) 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. /// 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. /// Returns a set of new article statuses.
func markAsUnstarred(_ articleIDs: Set<String>, completion: ArticleStatusesResultBlock? = nil) { func markAsUnstarred(_ articleIDs: Set<String>, completion: ArticleIDsCompletionBlock? = nil) {
markAndFetchNew(articleIDs: articleIDs, statusKey: .starred, flag: false, completion: completion) markAndFetchNew(articleIDs: articleIDs, statusKey: .starred, flag: false, completion: completion)
} }
@ -893,7 +893,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
public func debugDropConditionalGetInfo() { public func debugDropConditionalGetInfo() {
#if DEBUG #if DEBUG
flattenedWebFeeds().forEach{ $0.debugDropConditionalGetInfo() } flattenedWebFeeds().forEach{ $0.dropConditionalGetInfo() }
#endif #endif
} }

View File

@ -481,7 +481,11 @@ final class CloudKitAccountDelegate: AccountDelegate {
self.account = account self.account = account
accountZone.delegate = CloudKitAcountZoneDelegate(account: account, refreshProgress: refreshProgress) 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 // Check to see if this is a new account and initialize anything we need
if account.externalID == nil { if account.externalID == nil {

View File

@ -9,6 +9,7 @@
import Foundation import Foundation
import os.log import os.log
import RSParser import RSParser
import RSWeb
import CloudKit import CloudKit
import SyncDatabase import SyncDatabase
@ -19,11 +20,15 @@ class CloudKitArticlesZoneDelegate: CloudKitZoneDelegate {
weak var account: Account? weak var account: Account?
var database: SyncDatabase var database: SyncDatabase
weak var articlesZone: CloudKitArticlesZone? 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.account = account
self.database = database self.database = database
self.articlesZone = articlesZone self.articlesZone = articlesZone
self.refresher = refresher
self.refreshProgress = refreshProgress
} }
func cloudKitDidChange(record: CKRecord) { func cloudKitDidChange(record: CKRecord) {
@ -82,8 +87,37 @@ private extension CloudKitArticlesZoneDelegate {
let group = DispatchGroup() let group = DispatchGroup()
group.enter() group.enter()
account?.markAsUnread(updateableUnreadArticleIDs) { _ in account?.markAsUnread(updateableUnreadArticleIDs) { result in
switch result {
case .success(let newArticleStatusIDs):
if newArticleStatusIDs.isEmpty {
group.leave() 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() group.enter()
@ -119,7 +153,6 @@ private extension CloudKitArticlesZoneDelegate {
} }
func makeParsedItem(_ articleRecord: CKRecord) -> ParsedItem? { func makeParsedItem(_ articleRecord: CKRecord) -> ParsedItem? {
var parsedAuthors = Set<ParsedAuthor>() var parsedAuthors = Set<ParsedAuthor>()

View File

@ -19,6 +19,7 @@ protocol LocalAccountRefresherDelegate {
final class LocalAccountRefresher { final class LocalAccountRefresher {
private var completions = [() -> Void]()
private var isSuspended = false private var isSuspended = false
var delegate: LocalAccountRefresherDelegate? var delegate: LocalAccountRefresherDelegate?
@ -26,7 +27,10 @@ final class LocalAccountRefresher {
return DownloadSession(delegate: self) return DownloadSession(delegate: self)
}() }()
public func refreshFeeds(_ feeds: Set<WebFeed>) { public func refreshFeeds(_ feeds: Set<WebFeed>, completion: (() -> Void)? = nil) {
if let completion = completion {
completions.append(completion)
}
downloadSession.downloadObjects(feeds as NSSet) downloadSession.downloadObjects(feeds as NSSet)
} }
@ -154,6 +158,8 @@ extension LocalAccountRefresher: DownloadSessionDelegate {
func downloadSessionDidCompleteDownloadObjects(_ downloadSession: DownloadSession) { func downloadSessionDidCompleteDownloadObjects(_ downloadSession: DownloadSession) {
delegate?.localAccountRefresherDidFinish(self) delegate?.localAccountRefresherDidFinish(self)
completions.forEach({ $0() })
completions = [() -> Void]()
} }
} }

View File

@ -221,9 +221,9 @@ public final class WebFeed: Feed, Renamable, Hashable {
self.metadata = metadata self.metadata = metadata
} }
// MARK: - Debug // MARK: - API
public func debugDropConditionalGetInfo() { public func dropConditionalGetInfo() {
conditionalGetInfo = nil conditionalGetInfo = nil
contentHash = nil contentHash = nil
} }

View File

@ -222,7 +222,7 @@ public final class ArticlesDatabase {
return try articlesTable.mark(articles, statusKey, flag) return try articlesTable.mark(articles, statusKey, flag)
} }
public func markAndFetchNew(articleIDs: Set<String>, statusKey: ArticleStatus.Key, flag: Bool, completion: @escaping ArticleStatusesResultBlock) { public func markAndFetchNew(articleIDs: Set<String>, statusKey: ArticleStatus.Key, flag: Bool, completion: @escaping ArticleIDsCompletionBlock) {
articlesTable.markAndFetchNew(articleIDs, statusKey, flag, completion) articlesTable.markAndFetchNew(articleIDs, statusKey, flag, completion)
} }

View File

@ -418,7 +418,7 @@ final class ArticlesTable: DatabaseTable {
return statuses return statuses
} }
func markAndFetchNew(_ articleIDs: Set<String>, _ statusKey: ArticleStatus.Key, _ flag: Bool, _ completion: @escaping ArticleStatusesResultBlock) { func markAndFetchNew(_ articleIDs: Set<String>, _ statusKey: ArticleStatus.Key, _ flag: Bool, _ completion: @escaping ArticleIDsCompletionBlock) {
queue.runInTransaction { databaseResult in queue.runInTransaction { databaseResult in
switch databaseResult { switch databaseResult {
case .success(let database): case .success(let database):

View File

@ -85,11 +85,11 @@ final class StatusesTable: DatabaseTable {
return updatedStatuses return updatedStatuses
} }
func markAndFetchNew(_ articleIDs: Set<String>, _ statusKey: ArticleStatus.Key, _ flag: Bool, _ database: FMDatabase) -> Set<ArticleStatus> { func markAndFetchNew(_ articleIDs: Set<String>, _ statusKey: ArticleStatus.Key, _ flag: Bool, _ database: FMDatabase) -> Set<String> {
let (statusesDictionary, newStatusIDs) = ensureStatusesForArticleIDs(articleIDs, flag, database) let (statusesDictionary, newStatusIDs) = ensureStatusesForArticleIDs(articleIDs, flag, database)
let statuses = Set(statusesDictionary.values) let statuses = Set(statusesDictionary.values)
mark(statuses, statusKey, flag, database) mark(statuses, statusKey, flag, database)
return Set(newStatusIDs.compactMap({ statusesDictionary[$0] })) return newStatusIDs
} }
// MARK: - Fetching // MARK: - Fetching