diff --git a/Frameworks/ArticlesDatabase/ArticlesDatabase.swift b/Frameworks/ArticlesDatabase/ArticlesDatabase.swift index e8141ae2e..9f6b34ba1 100644 --- a/Frameworks/ArticlesDatabase/ArticlesDatabase.swift +++ b/Frameworks/ArticlesDatabase/ArticlesDatabase.swift @@ -193,9 +193,14 @@ public final class ArticlesDatabase { // MARK: - Operations - /// Create and return an operation that fetches all non-zero unread counts. + /// Create an operation that fetches all non-zero unread counts. public func createFetchAllUnreadCountsOperation() -> FetchAllUnreadCountsOperation { - return articlesTable.createFetchAllUnreadCountsOperation() + return FetchAllUnreadCountsOperation(databaseQueue: queue, cutoffDate: articlesTable.articleCutoffDate) + } + + /// Create an operation that fetches the unread count for a single given feedID. + public func createFetchFeedUnreadCountOperation(feedID: String) -> FetchFeedUnreadCountOperation { + return FetchFeedUnreadCountOperation(feedID: feedID, databaseQueue: queue, cutoffDate: articlesTable.articleCutoffDate) } // MARK: - Suspend and Resume (for iOS) diff --git a/Frameworks/ArticlesDatabase/ArticlesDatabase.xcodeproj/project.pbxproj b/Frameworks/ArticlesDatabase/ArticlesDatabase.xcodeproj/project.pbxproj index 840e814bd..7d1277606 100644 --- a/Frameworks/ArticlesDatabase/ArticlesDatabase.xcodeproj/project.pbxproj +++ b/Frameworks/ArticlesDatabase/ArticlesDatabase.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 51C451FF2264CF2100C03939 /* RSParser.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 51C451FE2264CF2100C03939 /* RSParser.framework */; }; + 84116B8923E01E86000B2E98 /* FetchFeedUnreadCountOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84116B8823E01E86000B2E98 /* FetchFeedUnreadCountOperation.swift */; }; 841D4D742106B59F00DD04E6 /* Articles.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 841D4D732106B59F00DD04E6 /* Articles.framework */; }; 84288A001F6A3C4400395871 /* DatabaseObject+Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842889FF1F6A3C4400395871 /* DatabaseObject+Database.swift */; }; 84288A021F6A3D8000395871 /* RelatedObjectsMap+Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84288A011F6A3D8000395871 /* RelatedObjectsMap+Database.swift */; }; @@ -112,6 +113,7 @@ /* Begin PBXFileReference section */ 518B2EA7235130CD00400001 /* ArticlesDatabase_project_test.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = ArticlesDatabase_project_test.xcconfig; sourceTree = ""; }; 51C451FE2264CF2100C03939 /* RSParser.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = RSParser.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 84116B8823E01E86000B2E98 /* FetchFeedUnreadCountOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchFeedUnreadCountOperation.swift; sourceTree = ""; }; 841D4D732106B59F00DD04E6 /* Articles.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Articles.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 842889FF1F6A3C4400395871 /* DatabaseObject+Database.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DatabaseObject+Database.swift"; sourceTree = ""; }; 84288A011F6A3D8000395871 /* RelatedObjectsMap+Database.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RelatedObjectsMap+Database.swift"; sourceTree = ""; }; @@ -244,6 +246,7 @@ isa = PBXGroup; children = ( 84C242C823DEB45C00C50516 /* FetchAllUnreadCountsOperation.swift */, + 84116B8823E01E86000B2E98 /* FetchFeedUnreadCountOperation.swift */, ); path = Operations; sourceTree = ""; @@ -525,6 +528,7 @@ 845580671F0AEBCD003CCFA1 /* Constants.swift in Sources */, 843CB9961F34174100EE6581 /* Author+Database.swift in Sources */, 845580761F0AF670003CCFA1 /* Article+Database.swift in Sources */, + 84116B8923E01E86000B2E98 /* FetchFeedUnreadCountOperation.swift in Sources */, 8455807A1F0AF67D003CCFA1 /* ArticleStatus+Database.swift in Sources */, 84288A021F6A3D8000395871 /* RelatedObjectsMap+Database.swift in Sources */, 84C242C923DEB45C00C50516 /* FetchAllUnreadCountsOperation.swift in Sources */, diff --git a/Frameworks/ArticlesDatabase/ArticlesTable.swift b/Frameworks/ArticlesDatabase/ArticlesTable.swift index 3fe6d7225..64cd7770a 100644 --- a/Frameworks/ArticlesDatabase/ArticlesTable.swift +++ b/Frameworks/ArticlesDatabase/ArticlesTable.swift @@ -26,7 +26,7 @@ final class ArticlesTable: DatabaseTable { }() // TODO: update articleCutoffDate as time passes and based on user preferences. - private let articleCutoffDate = Date().bySubtracting(days: 90) + let articleCutoffDate = Date().bySubtracting(days: 90) private typealias ArticlesFetchMethod = (FMDatabase) -> Set
@@ -521,10 +521,6 @@ final class ArticlesTable: DatabaseTable { } } } - - func createFetchAllUnreadCountsOperation() -> FetchAllUnreadCountsOperation { - return FetchAllUnreadCountsOperation(databaseQueue: queue, cutoffDate: articleCutoffDate) - } } // MARK: - Private diff --git a/Frameworks/ArticlesDatabase/Operations/FetchFeedUnreadCountOperation.swift b/Frameworks/ArticlesDatabase/Operations/FetchFeedUnreadCountOperation.swift new file mode 100644 index 000000000..397fbad4c --- /dev/null +++ b/Frameworks/ArticlesDatabase/Operations/FetchFeedUnreadCountOperation.swift @@ -0,0 +1,73 @@ +// +// FetchFeedUnreadCountOperation.swift +// ArticlesDatabase +// +// Created by Brent Simmons on 1/27/20. +// Copyright © 2020 Ranchero Software. All rights reserved. +// + +import Foundation +import RSCore +import RSDatabase + +/// Fetch the unread count for a single feed. +public final class FetchFeedUnreadCountOperation: MainThreadOperation { + + public var unreadCount: Int? + public let feedID: String + + // MainThreadOperation + public var isCanceled = false + public var id: Int? + public weak var operationDelegate: MainThreadOperationDelegate? + public var name: String? + public var completionBlock: MainThreadOperation.MainThreadOperationCompletionBlock? + + private let queue: DatabaseQueue + private let cutoffDate: Date + + init(feedID: String, databaseQueue: DatabaseQueue, cutoffDate: Date) { + self.feedID = feedID + self.queue = databaseQueue + self.cutoffDate = cutoffDate + } + + public func run() { + queue.runInDatabase { databaseResult in + if self.isCanceled { + self.informOperationDelegateOfCompletion() + return + } + + switch databaseResult { + case .success(let database): + self.fetchUnreadCount(database) + case .failure: + self.informOperationDelegateOfCompletion() + } + } + } +} + +private extension FetchFeedUnreadCountOperation { + + func fetchUnreadCount(_ database: FMDatabase) { + let sql = "select count(*) from articles natural join statuses where feedID=? and read=0 and userDeleted=0 and (starred=1 or dateArrived>?);" + + guard let resultSet = database.executeQuery(sql, withArgumentsIn: [feedID, cutoffDate]) else { + informOperationDelegateOfCompletion() + return + } + if isCanceled { + informOperationDelegateOfCompletion() + return + } + + if resultSet.next() { + unreadCount = resultSet.long(forColumnIndex: 0) + } + resultSet.close() + + informOperationDelegateOfCompletion() + } +}