Move the operation queue from Account to ArticlesDatabase, which is the rightful owner.
This commit is contained in:
parent
d40eaed1f5
commit
01fc60916e
@ -231,14 +231,6 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||||||
return delegate.refreshProgress
|
return delegate.refreshProgress
|
||||||
}
|
}
|
||||||
|
|
||||||
private var operationQueue = MainThreadOperationQueue()
|
|
||||||
private enum OperationName {
|
|
||||||
static let FetchAllUnreadCounts = "FetchAllUnreadCounts"
|
|
||||||
static let FetchFeedUnreadCount = "FetchFeedUnreadCount"
|
|
||||||
static let FetchUnreadCountsForFeeds = "FetchUnreadCountsForFeeds"
|
|
||||||
}
|
|
||||||
private static let discardableOperationNames = [OperationName.FetchAllUnreadCounts, OperationName.FetchFeedUnreadCount, OperationName.FetchUnreadCountsForFeeds]
|
|
||||||
|
|
||||||
init?(dataFolder: String, type: AccountType, accountID: String, transport: Transport? = nil) {
|
init?(dataFolder: String, type: AccountType, accountID: String, transport: Transport? = nil) {
|
||||||
switch type {
|
switch type {
|
||||||
case .onMyMac:
|
case .onMyMac:
|
||||||
@ -422,9 +414,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func suspendDatabase() {
|
public func suspendDatabase() {
|
||||||
operationQueue.suspend()
|
database.cancelAndSuspend()
|
||||||
cancelDiscardableOperations()
|
|
||||||
database.suspend()
|
|
||||||
save()
|
save()
|
||||||
metadataFile.suspend()
|
metadataFile.suspend()
|
||||||
webFeedMetadataFile.suspend()
|
webFeedMetadataFile.suspend()
|
||||||
@ -436,7 +426,6 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||||||
public func resumeDatabaseAndDelegate() {
|
public func resumeDatabaseAndDelegate() {
|
||||||
database.resume()
|
database.resume()
|
||||||
delegate.resume()
|
delegate.resume()
|
||||||
operationQueue.resume()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reload OPML, etc.
|
/// Reload OPML, etc.
|
||||||
@ -460,12 +449,6 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||||||
delegate.accountWillBeDeleted(self)
|
delegate.accountWillBeDeleted(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func cancelDiscardableOperations() {
|
|
||||||
for operationName in Self.discardableOperationNames {
|
|
||||||
operationQueue.cancelOperations(named: operationName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadOPMLItems(_ items: [RSOPMLItem], parentFolder: Folder?) {
|
func loadOPMLItems(_ items: [RSOPMLItem], parentFolder: Folder?) {
|
||||||
var feedsToAdd = Set<WebFeed>()
|
var feedsToAdd = Set<WebFeed>()
|
||||||
|
|
||||||
@ -1242,43 +1225,34 @@ private extension Account {
|
|||||||
fetchUnreadCounts(feeds, completion)
|
fetchUnreadCounts(feeds, completion)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
fetchAllUnreadCounts()
|
fetchAllUnreadCounts(completion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchUnreadCount(_ feed: WebFeed, _ completion: VoidCompletionBlock?) {
|
func fetchUnreadCount(_ feed: WebFeed, _ completion: VoidCompletionBlock?) {
|
||||||
let operation = database.createFetchFeedUnreadCountOperation(feedID: feed.webFeedID)
|
database.fetchUnreadCount(feed.webFeedID) { result in
|
||||||
operation.name = OperationName.FetchFeedUnreadCount
|
if let unreadCount = try? result.get() {
|
||||||
operation.completionBlock = { operation in
|
|
||||||
let fetchOperation = operation as! FetchFeedUnreadCountOperation
|
|
||||||
if let unreadCount = fetchOperation.unreadCount {
|
|
||||||
feed.unreadCount = unreadCount
|
feed.unreadCount = unreadCount
|
||||||
}
|
}
|
||||||
completion?()
|
completion?()
|
||||||
}
|
}
|
||||||
|
|
||||||
operationQueue.add(operation)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchUnreadCounts(_ feeds: Set<WebFeed>, _ completion: VoidCompletionBlock?) {
|
func fetchUnreadCounts(_ feeds: Set<WebFeed>, _ completion: VoidCompletionBlock?) {
|
||||||
let feedIDs = feeds.map { $0.webFeedID }
|
let webFeedIDs = Set(feeds.map { $0.webFeedID })
|
||||||
let operation = database.createFetchUnreadCountsForFeedsOperation(feedIDs: Set(feedIDs))
|
database.fetchUnreadCounts(for: webFeedIDs) { result in
|
||||||
operation.name = OperationName.FetchUnreadCountsForFeeds
|
if let unreadCountDictionary = try? result.get() {
|
||||||
operation.completionBlock = { operation in
|
|
||||||
let fetchOperation = operation as! FetchUnreadCountsForFeedsOperation
|
|
||||||
if let unreadCountDictionary = fetchOperation.unreadCountDictionary {
|
|
||||||
self.processUnreadCounts(unreadCountDictionary: unreadCountDictionary, feeds: feeds)
|
self.processUnreadCounts(unreadCountDictionary: unreadCountDictionary, feeds: feeds)
|
||||||
}
|
}
|
||||||
completion?()
|
completion?()
|
||||||
}
|
}
|
||||||
|
|
||||||
operationQueue.add(operation)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchAllUnreadCounts() {
|
func fetchAllUnreadCounts(_ completion: VoidCompletionBlock? = nil) {
|
||||||
fetchingAllUnreadCounts = true
|
fetchingAllUnreadCounts = true
|
||||||
database.fetchAllUnreadCounts { (result) in
|
database.fetchAllUnreadCounts { result in
|
||||||
guard let unreadCountDictionary = try? result.get() else {
|
guard let unreadCountDictionary = try? result.get() else {
|
||||||
|
completion?()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.processUnreadCounts(unreadCountDictionary: unreadCountDictionary, feeds: self.flattenedWebFeeds())
|
self.processUnreadCounts(unreadCountDictionary: unreadCountDictionary, feeds: self.flattenedWebFeeds())
|
||||||
@ -1290,6 +1264,7 @@ private extension Account {
|
|||||||
self.isUnreadCountsInitialized = true
|
self.isUnreadCountsInitialized = true
|
||||||
self.postUnreadCountDidInitializeNotification()
|
self.postUnreadCountDidInitializeNotification()
|
||||||
}
|
}
|
||||||
|
completion?()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,7 +148,27 @@ public final class ArticlesDatabase {
|
|||||||
}
|
}
|
||||||
operationQueue.add(operation)
|
operationQueue.add(operation)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fetch unread count for a single feed.
|
||||||
|
public func fetchUnreadCount(_ webFeedID: String, _ completion: @escaping SingleUnreadCountCompletionBlock) {
|
||||||
|
let operation = FetchFeedUnreadCountOperation(webFeedID: webFeedID, databaseQueue: queue, cutoffDate: articlesTable.articleCutoffDate)
|
||||||
|
operation.completionBlock = { operation in
|
||||||
|
let fetchOperation = operation as! FetchFeedUnreadCountOperation
|
||||||
|
completion(fetchOperation.result)
|
||||||
|
}
|
||||||
|
operationQueue.add(operation)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch non-zero unread counts for given webFeedIDs.
|
||||||
|
public func fetchUnreadCounts(for webFeedIDs: Set<String>, _ completion: @escaping UnreadCountDictionaryCompletionBlock) {
|
||||||
|
let operation = FetchUnreadCountsForFeedsOperation(webFeedIDs: webFeedIDs, databaseQueue: queue, cutoffDate: articlesTable.articleCutoffDate)
|
||||||
|
operation.completionBlock = { operation in
|
||||||
|
let fetchOperation = operation as! FetchUnreadCountsForFeedsOperation
|
||||||
|
completion(fetchOperation.result)
|
||||||
|
}
|
||||||
|
operationQueue.add(operation)
|
||||||
|
}
|
||||||
|
|
||||||
public func fetchUnreadCountForToday(for webFeedIDs: Set<String>, completion: @escaping SingleUnreadCountCompletionBlock) {
|
public func fetchUnreadCountForToday(for webFeedIDs: Set<String>, completion: @escaping SingleUnreadCountCompletionBlock) {
|
||||||
fetchUnreadCount(for: webFeedIDs, since: todayCutoffDate(), completion: completion)
|
fetchUnreadCount(for: webFeedIDs, since: todayCutoffDate(), completion: completion)
|
||||||
}
|
}
|
||||||
@ -199,33 +219,25 @@ public final class ArticlesDatabase {
|
|||||||
articlesTable.createStatusesIfNeeded(articleIDs, completion)
|
articlesTable.createStatusesIfNeeded(articleIDs, completion)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Operations
|
|
||||||
|
|
||||||
public func cancelOperations() {
|
|
||||||
operationQueue.cancelAllOperations()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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)
|
// MARK: - Suspend and Resume (for iOS)
|
||||||
|
|
||||||
|
/// Cancel current operations and close the database.
|
||||||
|
public func cancelAndSuspend() {
|
||||||
|
cancelOperations()
|
||||||
|
suspend()
|
||||||
|
}
|
||||||
|
|
||||||
/// Close the database and stop running database calls.
|
/// Close the database and stop running database calls.
|
||||||
/// Any pending calls will complete first.
|
/// Any pending calls will complete first.
|
||||||
public func suspend() {
|
public func suspend() {
|
||||||
|
operationQueue.suspend()
|
||||||
queue.suspend()
|
queue.suspend()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Open the database and allow for running database calls again.
|
/// Open the database and allow for running database calls again.
|
||||||
public func resume() {
|
public func resume() {
|
||||||
queue.resume()
|
queue.resume()
|
||||||
|
operationQueue.resume()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Caches
|
// MARK: - Caches
|
||||||
@ -272,4 +284,10 @@ private extension ArticlesDatabase {
|
|||||||
// 24 hours previous. This is used by the Today smart feed, which should not actually empty out at midnight.
|
// 24 hours previous. This is used by the Today smart feed, which should not actually empty out at midnight.
|
||||||
return Date(timeIntervalSinceNow: -(60 * 60 * 24)) // This does not need to be more precise.
|
return Date(timeIntervalSinceNow: -(60 * 60 * 24)) // This does not need to be more precise.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Operations
|
||||||
|
|
||||||
|
func cancelOperations() {
|
||||||
|
operationQueue.cancelAllOperations()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ import RSDatabase
|
|||||||
|
|
||||||
public final class FetchAllUnreadCountsOperation: MainThreadOperation {
|
public final class FetchAllUnreadCountsOperation: MainThreadOperation {
|
||||||
|
|
||||||
public var result: UnreadCountDictionaryCompletionResult = .failure(.isSuspended)
|
var result: UnreadCountDictionaryCompletionResult = .failure(.isSuspended)
|
||||||
|
|
||||||
// MainThreadOperation
|
// MainThreadOperation
|
||||||
public var isCanceled = false
|
public var isCanceled = false
|
||||||
|
@ -13,8 +13,7 @@ import RSDatabase
|
|||||||
/// Fetch the unread count for a single feed.
|
/// Fetch the unread count for a single feed.
|
||||||
public final class FetchFeedUnreadCountOperation: MainThreadOperation {
|
public final class FetchFeedUnreadCountOperation: MainThreadOperation {
|
||||||
|
|
||||||
public var unreadCount: Int?
|
var result: SingleUnreadCountResult = .failure(.isSuspended)
|
||||||
public let feedID: String
|
|
||||||
|
|
||||||
// MainThreadOperation
|
// MainThreadOperation
|
||||||
public var isCanceled = false
|
public var isCanceled = false
|
||||||
@ -25,9 +24,10 @@ public final class FetchFeedUnreadCountOperation: MainThreadOperation {
|
|||||||
|
|
||||||
private let queue: DatabaseQueue
|
private let queue: DatabaseQueue
|
||||||
private let cutoffDate: Date
|
private let cutoffDate: Date
|
||||||
|
private let webFeedID: String
|
||||||
|
|
||||||
init(feedID: String, databaseQueue: DatabaseQueue, cutoffDate: Date) {
|
init(webFeedID: String, databaseQueue: DatabaseQueue, cutoffDate: Date) {
|
||||||
self.feedID = feedID
|
self.webFeedID = webFeedID
|
||||||
self.queue = databaseQueue
|
self.queue = databaseQueue
|
||||||
self.cutoffDate = cutoffDate
|
self.cutoffDate = cutoffDate
|
||||||
}
|
}
|
||||||
@ -54,7 +54,7 @@ private extension FetchFeedUnreadCountOperation {
|
|||||||
func fetchUnreadCount(_ database: FMDatabase) {
|
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>?);"
|
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 {
|
guard let resultSet = database.executeQuery(sql, withArgumentsIn: [webFeedID, cutoffDate]) else {
|
||||||
informOperationDelegateOfCompletion()
|
informOperationDelegateOfCompletion()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -64,7 +64,8 @@ private extension FetchFeedUnreadCountOperation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if resultSet.next() {
|
if resultSet.next() {
|
||||||
unreadCount = resultSet.long(forColumnIndex: 0)
|
let unreadCount = resultSet.long(forColumnIndex: 0)
|
||||||
|
result = .success(unreadCount)
|
||||||
}
|
}
|
||||||
resultSet.close()
|
resultSet.close()
|
||||||
|
|
||||||
|
@ -13,8 +13,7 @@ import RSDatabase
|
|||||||
/// Fetch the unread counts for a number of feeds.
|
/// Fetch the unread counts for a number of feeds.
|
||||||
public final class FetchUnreadCountsForFeedsOperation: MainThreadOperation {
|
public final class FetchUnreadCountsForFeedsOperation: MainThreadOperation {
|
||||||
|
|
||||||
public var unreadCountDictionary: UnreadCountDictionary?
|
var result: UnreadCountDictionaryCompletionResult = .failure(.isSuspended)
|
||||||
public let feedIDs: Set<String>
|
|
||||||
|
|
||||||
// MainThreadOperation
|
// MainThreadOperation
|
||||||
public var isCanceled = false
|
public var isCanceled = false
|
||||||
@ -25,9 +24,10 @@ public final class FetchUnreadCountsForFeedsOperation: MainThreadOperation {
|
|||||||
|
|
||||||
private let queue: DatabaseQueue
|
private let queue: DatabaseQueue
|
||||||
private let cutoffDate: Date
|
private let cutoffDate: Date
|
||||||
|
private let webFeedIDs: Set<String>
|
||||||
|
|
||||||
init(feedIDs: Set<String>, databaseQueue: DatabaseQueue, cutoffDate: Date) {
|
init(webFeedIDs: Set<String>, databaseQueue: DatabaseQueue, cutoffDate: Date) {
|
||||||
self.feedIDs = feedIDs
|
self.webFeedIDs = webFeedIDs
|
||||||
self.queue = databaseQueue
|
self.queue = databaseQueue
|
||||||
self.cutoffDate = cutoffDate
|
self.cutoffDate = cutoffDate
|
||||||
}
|
}
|
||||||
@ -52,11 +52,11 @@ public final class FetchUnreadCountsForFeedsOperation: MainThreadOperation {
|
|||||||
private extension FetchUnreadCountsForFeedsOperation {
|
private extension FetchUnreadCountsForFeedsOperation {
|
||||||
|
|
||||||
func fetchUnreadCounts(_ database: FMDatabase) {
|
func fetchUnreadCounts(_ database: FMDatabase) {
|
||||||
let placeholders = NSString.rs_SQLValueList(withPlaceholders: UInt(feedIDs.count))!
|
let placeholders = NSString.rs_SQLValueList(withPlaceholders: UInt(webFeedIDs.count))!
|
||||||
let sql = "select distinct feedID, count(*) from articles natural join statuses where feedID in \(placeholders) and read=0 and userDeleted=0 and (starred=1 or dateArrived>?) group by feedID;"
|
let sql = "select distinct feedID, count(*) from articles natural join statuses where feedID in \(placeholders) and read=0 and userDeleted=0 and (starred=1 or dateArrived>?) group by feedID;"
|
||||||
|
|
||||||
var parameters = [Any]()
|
var parameters = [Any]()
|
||||||
parameters += Array(feedIDs) as [Any]
|
parameters += Array(webFeedIDs) as [Any]
|
||||||
parameters += [cutoffDate] as [Any]
|
parameters += [cutoffDate] as [Any]
|
||||||
|
|
||||||
guard let resultSet = database.executeQuery(sql, withArgumentsIn: parameters) else {
|
guard let resultSet = database.executeQuery(sql, withArgumentsIn: parameters) else {
|
||||||
@ -64,11 +64,12 @@ private extension FetchUnreadCountsForFeedsOperation {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if isCanceled {
|
if isCanceled {
|
||||||
|
resultSet.close()
|
||||||
informOperationDelegateOfCompletion()
|
informOperationDelegateOfCompletion()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var d = UnreadCountDictionary()
|
var unreadCountDictionary = UnreadCountDictionary()
|
||||||
while resultSet.next() {
|
while resultSet.next() {
|
||||||
if isCanceled {
|
if isCanceled {
|
||||||
resultSet.close()
|
resultSet.close()
|
||||||
@ -77,12 +78,12 @@ private extension FetchUnreadCountsForFeedsOperation {
|
|||||||
}
|
}
|
||||||
let unreadCount = resultSet.long(forColumnIndex: 1)
|
let unreadCount = resultSet.long(forColumnIndex: 1)
|
||||||
if let webFeedID = resultSet.string(forColumnIndex: 0) {
|
if let webFeedID = resultSet.string(forColumnIndex: 0) {
|
||||||
d[webFeedID] = unreadCount
|
unreadCountDictionary[webFeedID] = unreadCount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
resultSet.close()
|
resultSet.close()
|
||||||
|
|
||||||
unreadCountDictionary = d
|
result = .success(unreadCountDictionary)
|
||||||
informOperationDelegateOfCompletion()
|
informOperationDelegateOfCompletion()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user