Use the new FetchUnreadCountsForFeedsOperation.
This commit is contained in:
parent
9628b3021b
commit
3f4c84e442
@ -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<WebFeed>, 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<Article> {
|
||||
@ -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<WebFeed>, _ 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<WebFeed>, _ 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<WebFeed>) {
|
||||
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
|
||||
|
@ -137,10 +137,6 @@ public final class ArticlesDatabase {
|
||||
|
||||
// MARK: - Unread Counts
|
||||
|
||||
public func fetchUnreadCounts(for webFeedIDs: Set<String>, _ completion: @escaping UnreadCountDictionaryCompletionBlock) {
|
||||
articlesTable.fetchUnreadCounts(webFeedIDs, completion)
|
||||
}
|
||||
|
||||
public func fetchUnreadCountForToday(for webFeedIDs: Set<String>, 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<String>) -> FetchUnreadCountsForFeedsOperation {
|
||||
return FetchUnreadCountsForFeedsOperation(feedIDs: feedIDs, databaseQueue: queue, cutoffDate: articlesTable.articleCutoffDate)
|
||||
}
|
||||
|
||||
// MARK: - Suspend and Resume (for iOS)
|
||||
|
||||
/// Close the database and stop running database calls.
|
||||
|
@ -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 = "<group>"; };
|
||||
845580751F0AF670003CCFA1 /* Article+Database.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Article+Database.swift"; path = "Extensions/Article+Database.swift"; sourceTree = "<group>"; };
|
||||
845580791F0AF67D003CCFA1 /* ArticleStatus+Database.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "ArticleStatus+Database.swift"; path = "Extensions/ArticleStatus+Database.swift"; sourceTree = "<group>"; };
|
||||
84611DCB23E62FE200BC630C /* FetchUnreadCountsForFeedsOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchUnreadCountsForFeedsOperation.swift; sourceTree = "<group>"; };
|
||||
8461461E1F0ABC7300870CB3 /* RSParser.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RSParser.xcodeproj; path = ../RSParser/RSParser.xcodeproj; sourceTree = "<group>"; };
|
||||
8477ACBB2221E76F00DF7F37 /* SearchTable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTable.swift; sourceTree = "<group>"; };
|
||||
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 = "<group>";
|
||||
@ -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 */,
|
||||
|
@ -237,31 +237,6 @@ final class ArticlesTable: DatabaseTable {
|
||||
|
||||
// MARK: - Unread Counts
|
||||
|
||||
func fetchUnreadCounts(_ webFeedIDs: Set<String>, _ 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<String>, _ 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<String>, _ 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<Article> {
|
||||
let sql = "select rowid from search where search match ?;"
|
||||
|
Loading…
x
Reference in New Issue
Block a user