diff --git a/Account/Sources/Account/Account.swift b/Account/Sources/Account/Account.swift index b40b18e6f..f67ff4a8a 100644 --- a/Account/Sources/Account/Account.swift +++ b/Account/Sources/Account/Account.swift @@ -668,8 +668,8 @@ public enum FetchType { structureDidChange() } - public func updateUnreadCounts(for feeds: Set, completion: VoidCompletionBlock? = nil) { - fetchUnreadCounts(for: feeds, completion: completion) + public func updateUnreadCounts(for feeds: Set) { + fetchUnreadCounts(for: feeds) } public func fetchUnreadArticlesBetween(limit: Int?, before: Date?, after: Date?) throws -> Set
{ @@ -1454,50 +1454,46 @@ private extension Account { /// 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?) { + func fetchUnreadCounts(for feeds: Set) { if feeds.isEmpty { - completion?() return } if feeds.count == 1, let feed = feeds.first { - fetchUnreadCount(feed, completion) + fetchUnreadCount(feed) } else if feeds.count < 10 { - fetchUnreadCounts(feeds, completion) + fetchUnreadCounts(feeds) } else { - fetchAllUnreadCounts(completion) + fetchAllUnreadCounts() } } - func fetchUnreadCount(_ feed: Feed, _ completion: VoidCompletionBlock?) { + func fetchUnreadCount(_ feed: Feed) { Task { @MainActor in if let unreadCount = try? await database.unreadCountForFeed(feed.feedID) { feed.unreadCount = unreadCount } - completion?() } } - func fetchUnreadCounts(_ feeds: Set, _ completion: VoidCompletionBlock?) { - let feedIDs = Set(feeds.map { $0.feedID }) - database.fetchUnreadCounts(for: feedIDs) { result in - Task { @MainActor in - if let unreadCountDictionary = try? result.get() { - self.processUnreadCounts(unreadCountDictionary: unreadCountDictionary, feeds: feeds) - } - completion?() - } + func fetchUnreadCounts(_ feeds: Set) { + + Task { @MainActor in + let feedIDs = Set(feeds.map { $0.feedID }) + + if let unreadCountDictionary = try? await database.unreadCountsForFeedIDs(feedIDs) { + self.processUnreadCounts(unreadCountDictionary: unreadCountDictionary, feeds: feeds) + } } } - func fetchAllUnreadCounts(_ completion: VoidCompletionBlock? = nil) { + func fetchAllUnreadCounts() { fetchingAllUnreadCounts = true database.fetchAllUnreadCounts { result in Task { @MainActor in guard let unreadCountDictionary = try? result.get() else { - completion?() - return + return } self.processUnreadCounts(unreadCountDictionary: unreadCountDictionary, feeds: self.flattenedFeeds()) @@ -1508,7 +1504,6 @@ private extension Account { self.isUnreadCountsInitialized = true self.postUnreadCountDidInitializeNotification() } - completion?() } } } diff --git a/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesDatabase.swift b/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesDatabase.swift index 02f6603cb..f959b1ef6 100644 --- a/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesDatabase.swift +++ b/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesDatabase.swift @@ -185,14 +185,8 @@ public typealias ArticleStatusesResultBlock = (ArticleStatusesResult) -> Void try await articlesTable.unreadCountForFeedID(feedID) } - /// Fetch non-zero unread counts for given feedIDs. - public func fetchUnreadCounts(for feedIDs: Set, _ completion: @escaping UnreadCountDictionaryCompletionBlock) { - let operation = FetchUnreadCountsForFeedsOperation(feedIDs: feedIDs, databaseQueue: queue) - operation.completionBlock = { operation in - let fetchOperation = operation as! FetchUnreadCountsForFeedsOperation - completion(fetchOperation.result) - } - operationQueue.add(operation) + public func unreadCountsForFeedIDs(_ feedIDs: Set) async throws -> UnreadCountDictionary { + try await articlesTable.unreadCountsForFeedIDs(feedIDs) } public func fetchUnreadCountForToday(for feedIDs: Set, completion: @escaping SingleUnreadCountCompletionBlock) { diff --git a/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesTable.swift b/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesTable.swift index 36d74f01c..a60032a89 100644 --- a/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesTable.swift +++ b/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesTable.swift @@ -420,6 +420,44 @@ final class ArticlesTable: DatabaseTable { } } + func unreadCountsForFeedIDs(_ feedIDs: Set) async throws -> UnreadCountDictionary { + + try await withCheckedThrowingContinuation { continuation in + queue.runInDatabase { databaseResult in + + func fetchUnreadCounts(_ database: FMDatabase) -> UnreadCountDictionary { + + let placeholders = NSString.rs_SQLValueList(withPlaceholders: UInt(feedIDs.count))! + let sql = "select distinct feedID, count(*) from articles natural join statuses where feedID in \(placeholders) and read=0 group by feedID;" + let parameters = Array(feedIDs) as [Any] + + guard let resultSet = database.executeQuery(sql, withArgumentsIn: parameters) else { + return UnreadCountDictionary() + } + + var unreadCountDictionary = UnreadCountDictionary() + while resultSet.next() { + let unreadCount = resultSet.long(forColumnIndex: 1) + if let feedID = resultSet.string(forColumnIndex: 0) { + unreadCountDictionary[feedID] = unreadCount + } + } + resultSet.close() + + return unreadCountDictionary + } + + switch databaseResult { + case .success(let database): + let unreadCountDictionary = fetchUnreadCounts(database) + continuation.resume(returning: unreadCountDictionary) + case .failure(let databaseError): + continuation.resume(throwing: databaseError) + } + } + } + } + func fetchUnreadCount(_ feedIDs: Set, _ since: Date, _ completion: @escaping SingleUnreadCountCompletionBlock) { // Get unread count for today, for instance. if feedIDs.isEmpty { diff --git a/ArticlesDatabase/Sources/ArticlesDatabase/Operations/FetchUnreadCountsForFeedsOperation.swift b/ArticlesDatabase/Sources/ArticlesDatabase/Operations/FetchUnreadCountsForFeedsOperation.swift deleted file mode 100644 index c3bcff808..000000000 --- a/ArticlesDatabase/Sources/ArticlesDatabase/Operations/FetchUnreadCountsForFeedsOperation.swift +++ /dev/null @@ -1,86 +0,0 @@ -// -// FetchUnreadCountsForFeedsOperation.swift -// ArticlesDatabase -// -// Created by Brent Simmons on 2/1/20. -// Copyright © 2020 Ranchero Software. All rights reserved. -// - -import Foundation -import RSCore -import RSDatabase -import RSDatabaseObjC - -/// Fetch the unread counts for a number of feeds. -public final class FetchUnreadCountsForFeedsOperation: MainThreadOperation { - - var result: UnreadCountDictionaryCompletionResult = .failure(.isSuspended) - - // MainThreadOperation - public var isCanceled = false - public var id: Int? - public weak var operationDelegate: MainThreadOperationDelegate? - public var name: String? = "FetchUnreadCountsForFeedsOperation" - public var completionBlock: MainThreadOperation.MainThreadOperationCompletionBlock? - - private let queue: DatabaseQueue - private let feedIDs: Set - - init(feedIDs: Set, databaseQueue: DatabaseQueue) { - self.feedIDs = feedIDs - self.queue = databaseQueue - } - - public func run() { - queue.runInDatabase { databaseResult in - if self.isCanceled { - self.informOperationDelegateOfCompletion() - return - } - - switch databaseResult { - case .success(let database): - self.fetchUnreadCounts(database) - case .failure: - self.informOperationDelegateOfCompletion() - } - } - } -} - -private extension FetchUnreadCountsForFeedsOperation { - - func fetchUnreadCounts(_ database: FMDatabase) { - let placeholders = NSString.rs_SQLValueList(withPlaceholders: UInt(feedIDs.count))! - let sql = "select distinct feedID, count(*) from articles natural join statuses where feedID in \(placeholders) and read=0 group by feedID;" - - let parameters = Array(feedIDs) as [Any] - - guard let resultSet = database.executeQuery(sql, withArgumentsIn: parameters) else { - informOperationDelegateOfCompletion() - return - } - if isCanceled { - resultSet.close() - informOperationDelegateOfCompletion() - return - } - - var unreadCountDictionary = UnreadCountDictionary() - while resultSet.next() { - if isCanceled { - resultSet.close() - informOperationDelegateOfCompletion() - return - } - let unreadCount = resultSet.long(forColumnIndex: 1) - if let feedID = resultSet.string(forColumnIndex: 0) { - unreadCountDictionary[feedID] = unreadCount - } - } - resultSet.close() - - result = .success(unreadCountDictionary) - informOperationDelegateOfCompletion() - } -}