From 3f4c84e4424ade269c73800bfbc61c2a3f36c7c1 Mon Sep 17 00:00:00 2001 From: Brent Simmons Date: Sat, 1 Feb 2020 15:01:47 -0800 Subject: [PATCH] Use the new FetchUnreadCountsForFeedsOperation. --- Frameworks/Account/Account.swift | 65 +++++++++------ .../ArticlesDatabase/ArticlesDatabase.swift | 9 +- .../project.pbxproj | 6 +- .../ArticlesDatabase/ArticlesTable.swift | 83 ++----------------- 4 files changed, 59 insertions(+), 104 deletions(-) diff --git a/Frameworks/Account/Account.swift b/Frameworks/Account/Account.swift index f2c11aa1b..740a7f349 100644 --- a/Frameworks/Account/Account.swift +++ b/Frameworks/Account/Account.swift @@ -235,8 +235,9 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, private enum OperationName { static let FetchAllUnreadCounts = "FetchAllUnreadCounts" static let FetchFeedUnreadCount = "FetchFeedUnreadCount" + static let FetchUnreadCountsForFeeds = "FetchUnreadCountsForFeeds" } - private static let discardableOperationNames = [OperationName.FetchAllUnreadCounts, OperationName.FetchFeedUnreadCount] + private static let discardableOperationNames = [OperationName.FetchAllUnreadCounts, OperationName.FetchFeedUnreadCount, OperationName.FetchUnreadCountsForFeeds] init?(dataFolder: String, type: AccountType, accountID: String, transport: Transport? = nil) { switch type { @@ -622,27 +623,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, } public func updateUnreadCounts(for webFeeds: Set, completion: VoidCompletionBlock? = nil) { - if webFeeds.isEmpty { - completion?() - return - } - - if webFeeds.count == 1, let feed = webFeeds.first { - fetchUnreadCount(feed, completion) - } - else { - database.fetchUnreadCounts(for: webFeeds.webFeedIDs()) { unreadCountDictionaryResult in - if let unreadCountDictionary = try? unreadCountDictionaryResult.get() { - for webFeed in webFeeds { - if let unreadCount = unreadCountDictionary[webFeed.webFeedID] { - webFeed.unreadCount = unreadCount - } - } - } - - completion?() - } - } + fetchUnreadCounts(webFeeds, completion) } public func fetchArticles(_ fetchType: FetchType) throws -> Set
{ @@ -1246,6 +1227,25 @@ private extension Account { NotificationCenter.default.post(name: .StatusesDidChange, object: self, userInfo: [UserInfoKey.articleIDs: articleIDs]) } + /// Fetch unread counts for zero or more feeds. + /// + /// Uses the most efficient method based on how many feeds were passed in. + func fetchUnreadCounts(for feeds: Set, _ completion: VoidCompletionBlock?) { + if feeds.isEmpty { + completion?() + return + } + if feeds.count == 1, let feed = feeds.first { + fetchUnreadCount(feed, completion) + } + else if feeds.count < 10 { + fetchUnreadCounts(feeds, completion) + } + else { + fetchAllUnreadCounts() + } + } + func fetchUnreadCount(_ feed: WebFeed, _ completion: VoidCompletionBlock?) { let operation = database.createFetchFeedUnreadCountOperation(feedID: feed.webFeedID) operation.name = OperationName.FetchFeedUnreadCount @@ -1260,6 +1260,21 @@ private extension Account { operationQueue.addOperation(operation) } + func fetchUnreadCounts(_ feeds: Set, _ completion: VoidCompletionBlock?) { + let feedIDs = feeds.map { $0.webFeedID } + let operation = database.createFetchUnreadCountsForFeedsOperation(feedIDs: Set(feedIDs)) + operation.name = OperationName.FetchUnreadCountsForFeeds + operation.completionBlock = { operation in + let fetchOperation = operation as! FetchUnreadCountsForFeedsOperation + if let unreadCountDictionary = fetchOperation.unreadCountDictionary { + self.processUnreadCounts(unreadCountDictionary: unreadCountDictionary, feeds: feeds) + } + completion?() + } + + operationQueue.addOperation(operation) + } + func fetchAllUnreadCounts() { fetchingAllUnreadCounts = true operationQueue.cancelOperations(named: OperationName.FetchAllUnreadCounts) @@ -1271,7 +1286,7 @@ private extension Account { guard let unreadCountDictionary = fetchOperation.unreadCountDictionary else { return } - self.processUnreadCounts(unreadCountDictionary: unreadCountDictionary) + self.processUnreadCounts(unreadCountDictionary: unreadCountDictionary, feeds: self.flattenedWebFeeds()) self.fetchingAllUnreadCounts = false self.updateUnreadCount() @@ -1282,8 +1297,8 @@ private extension Account { operationQueue.addOperation(operation) } - func processUnreadCounts(unreadCountDictionary: UnreadCountDictionary) { - for feed in flattenedWebFeeds() { + func processUnreadCounts(unreadCountDictionary: UnreadCountDictionary, feeds: Set) { + for feed in feeds { // When the unread count is zero, it won’t appear in unreadCountDictionary. let unreadCount = unreadCountDictionary[feed.webFeedID] ?? 0 feed.unreadCount = unreadCount diff --git a/Frameworks/ArticlesDatabase/ArticlesDatabase.swift b/Frameworks/ArticlesDatabase/ArticlesDatabase.swift index 9f6b34ba1..bb341bc4f 100644 --- a/Frameworks/ArticlesDatabase/ArticlesDatabase.swift +++ b/Frameworks/ArticlesDatabase/ArticlesDatabase.swift @@ -137,10 +137,6 @@ public final class ArticlesDatabase { // MARK: - Unread Counts - public func fetchUnreadCounts(for webFeedIDs: Set, _ completion: @escaping UnreadCountDictionaryCompletionBlock) { - articlesTable.fetchUnreadCounts(webFeedIDs, completion) - } - public func fetchUnreadCountForToday(for webFeedIDs: Set, completion: @escaping SingleUnreadCountCompletionBlock) { fetchUnreadCount(for: webFeedIDs, since: todayCutoffDate(), completion: completion) } @@ -203,6 +199,11 @@ public final class ArticlesDatabase { return FetchFeedUnreadCountOperation(feedID: feedID, databaseQueue: queue, cutoffDate: articlesTable.articleCutoffDate) } + /// Create an operation that fetches unread counts for a number of feedIDs. + public func createFetchUnreadCountsForFeedsOperation(feedIDs: Set) -> FetchUnreadCountsForFeedsOperation { + return FetchUnreadCountsForFeedsOperation(feedIDs: feedIDs, databaseQueue: queue, cutoffDate: articlesTable.articleCutoffDate) + } + // MARK: - Suspend and Resume (for iOS) /// Close the database and stop running database calls. diff --git a/Frameworks/ArticlesDatabase/ArticlesDatabase.xcodeproj/project.pbxproj b/Frameworks/ArticlesDatabase/ArticlesDatabase.xcodeproj/project.pbxproj index 7d1277606..055dd02a4 100644 --- a/Frameworks/ArticlesDatabase/ArticlesDatabase.xcodeproj/project.pbxproj +++ b/Frameworks/ArticlesDatabase/ArticlesDatabase.xcodeproj/project.pbxproj @@ -20,6 +20,7 @@ 845580671F0AEBCD003CCFA1 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845580661F0AEBCD003CCFA1 /* Constants.swift */; }; 845580761F0AF670003CCFA1 /* Article+Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845580751F0AF670003CCFA1 /* Article+Database.swift */; }; 8455807A1F0AF67D003CCFA1 /* ArticleStatus+Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845580791F0AF67D003CCFA1 /* ArticleStatus+Database.swift */; }; + 84611DCC23E62FE200BC630C /* FetchUnreadCountsForFeedsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84611DCB23E62FE200BC630C /* FetchUnreadCountsForFeedsOperation.swift */; }; 8477ACBC2221E76F00DF7F37 /* SearchTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8477ACBB2221E76F00DF7F37 /* SearchTable.swift */; }; 848E3EB920FBCFD20004B7ED /* RSCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 848E3EB820FBCFD20004B7ED /* RSCore.framework */; }; 848E3EBD20FBCFDE0004B7ED /* RSDatabase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 848E3EBC20FBCFDE0004B7ED /* RSDatabase.framework */; }; @@ -126,6 +127,7 @@ 845580661F0AEBCD003CCFA1 /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; 845580751F0AF670003CCFA1 /* Article+Database.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Article+Database.swift"; path = "Extensions/Article+Database.swift"; sourceTree = ""; }; 845580791F0AF67D003CCFA1 /* ArticleStatus+Database.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "ArticleStatus+Database.swift"; path = "Extensions/ArticleStatus+Database.swift"; sourceTree = ""; }; + 84611DCB23E62FE200BC630C /* FetchUnreadCountsForFeedsOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchUnreadCountsForFeedsOperation.swift; sourceTree = ""; }; 8461461E1F0ABC7300870CB3 /* RSParser.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RSParser.xcodeproj; path = ../RSParser/RSParser.xcodeproj; sourceTree = ""; }; 8477ACBB2221E76F00DF7F37 /* SearchTable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTable.swift; sourceTree = ""; }; 848E3EB820FBCFD20004B7ED /* RSCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = RSCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -245,8 +247,9 @@ 84C242C723DEB42700C50516 /* Operations */ = { isa = PBXGroup; children = ( - 84C242C823DEB45C00C50516 /* FetchAllUnreadCountsOperation.swift */, 84116B8823E01E86000B2E98 /* FetchFeedUnreadCountOperation.swift */, + 84611DCB23E62FE200BC630C /* FetchUnreadCountsForFeedsOperation.swift */, + 84C242C823DEB45C00C50516 /* FetchAllUnreadCountsOperation.swift */, ); path = Operations; sourceTree = ""; @@ -535,6 +538,7 @@ 84F20F8F1F180D8700D8E682 /* AuthorsTable.swift in Sources */, 84288A001F6A3C4400395871 /* DatabaseObject+Database.swift in Sources */, 8477ACBC2221E76F00DF7F37 /* SearchTable.swift in Sources */, + 84611DCC23E62FE200BC630C /* FetchUnreadCountsForFeedsOperation.swift in Sources */, 843702C31F70D15D00B18807 /* ParsedArticle+Database.swift in Sources */, 84E156EC1F0AB80E00F8CC05 /* ArticlesTable.swift in Sources */, 84E156EE1F0AB81400F8CC05 /* StatusesTable.swift in Sources */, diff --git a/Frameworks/ArticlesDatabase/ArticlesTable.swift b/Frameworks/ArticlesDatabase/ArticlesTable.swift index 64cd7770a..1f0ab8414 100644 --- a/Frameworks/ArticlesDatabase/ArticlesTable.swift +++ b/Frameworks/ArticlesDatabase/ArticlesTable.swift @@ -237,31 +237,6 @@ final class ArticlesTable: DatabaseTable { // MARK: - Unread Counts - func fetchUnreadCounts(_ webFeedIDs: Set, _ completion: @escaping UnreadCountDictionaryCompletionBlock) { - if webFeedIDs.isEmpty { - completion(.success(UnreadCountDictionary())) - return - } - - fetchAllUnreadCounts { (unreadCountsResult) in - - func createUnreadCountDictionary(_ unreadCountDictionary: UnreadCountDictionary) -> UnreadCountDictionary { - var d = UnreadCountDictionary() - for webFeedID in webFeedIDs { - d[webFeedID] = unreadCountDictionary[webFeedID] ?? 0 - } - return d - } - - switch unreadCountsResult { - case .success(let unreadCountDictionary): - completion(.success(createUnreadCountDictionary(unreadCountDictionary))) - case .failure(let databaseError): - completion(.failure(databaseError)) - } - } - } - func fetchUnreadCount(_ webFeedIDs: Set, _ since: Date, _ completion: @escaping SingleUnreadCountCompletionBlock) { // Get unread count for today, for instance. if webFeedIDs.isEmpty { @@ -298,46 +273,6 @@ final class ArticlesTable: DatabaseTable { } } - func fetchAllUnreadCounts(_ completion: @escaping UnreadCountDictionaryCompletionBlock) { - // Returns only where unreadCount > 0. - - let cutoffDate = articleCutoffDate - queue.runInDatabase { databaseResult in - - func makeDatabaseCalls(_ database: FMDatabase) { - let sql = "select distinct feedID, count(*) from articles natural join statuses where read=0 and userDeleted=0 and (starred=1 or dateArrived>?) group by feedID;" - - guard let resultSet = database.executeQuery(sql, withArgumentsIn: [cutoffDate]) else { - DispatchQueue.main.async { - completion(.success(UnreadCountDictionary())) - } - return - } - - var d = UnreadCountDictionary() - while resultSet.next() { - let unreadCount = resultSet.long(forColumnIndex: 1) - if let webFeedID = resultSet.string(forColumnIndex: 0) { - d[webFeedID] = unreadCount - } - } - - DispatchQueue.main.async { - completion(.success(d)) - } - } - - switch databaseResult { - case .success(let database): - makeDatabaseCalls(database) - case .failure(let databaseError): - DispatchQueue.main.async { - completion(.failure(databaseError)) - } - } - } - } - func fetchStarredAndUnreadCount(_ webFeedIDs: Set, _ completion: @escaping SingleUnreadCountCompletionBlock) { if webFeedIDs.isEmpty { completion(.success(0)) @@ -630,15 +565,15 @@ private extension ArticlesTable { } } - func fetchUnreadCount(_ webFeedID: String, _ database: FMDatabase) -> Int { - // Count only the articles that would appear in the UI. - // * Must be unread. - // * Must not be deleted. - // * Must be either 1) starred or 2) dateArrived must be newer than cutoff date. - - let sql = "select count(*) from articles natural join statuses where feedID=? and read=0 and userDeleted=0 and (starred=1 or dateArrived>?);" - return numberWithSQLAndParameters(sql, [webFeedID, articleCutoffDate], in: database) - } +// func fetchUnreadCount(_ webFeedID: String, _ database: FMDatabase) -> Int { +// // Count only the articles that would appear in the UI. +// // * Must be unread. +// // * Must not be deleted. +// // * Must be either 1) starred or 2) dateArrived must be newer than cutoff date. +// +// let sql = "select count(*) from articles natural join statuses where feedID=? and read=0 and userDeleted=0 and (starred=1 or dateArrived>?);" +// return numberWithSQLAndParameters(sql, [webFeedID, articleCutoffDate], in: database) +// } func fetchArticlesMatching(_ searchString: String, _ database: FMDatabase) -> Set
{ let sql = "select rowid from search where search match ?;"