Merge pull request #1619 from kielgillard/feedly-article-status-switching
Fix read articles switching back to unread in Feedly
This commit is contained in:
commit
971abee39d
|
@ -89,6 +89,7 @@ class FeedlyAddNewFeedOperationTests: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
let progress = DownloadProgress(numberOfTasks: 0)
|
let progress = DownloadProgress(numberOfTasks: 0)
|
||||||
|
let container = support.makeTestDatabaseContainer()
|
||||||
let _ = expectationForCompletion(of: progress)
|
let _ = expectationForCompletion(of: progress)
|
||||||
|
|
||||||
let addNewFeed = try! FeedlyAddNewFeedOperation(account: account,
|
let addNewFeed = try! FeedlyAddNewFeedOperation(account: account,
|
||||||
|
@ -99,6 +100,7 @@ class FeedlyAddNewFeedOperationTests: XCTestCase {
|
||||||
addToCollectionService: caller,
|
addToCollectionService: caller,
|
||||||
syncUnreadIdsService: caller,
|
syncUnreadIdsService: caller,
|
||||||
getStreamContentsService: caller,
|
getStreamContentsService: caller,
|
||||||
|
database: container.database,
|
||||||
container: folder,
|
container: folder,
|
||||||
progress: progress,
|
progress: progress,
|
||||||
log: support.log)
|
log: support.log)
|
||||||
|
@ -126,6 +128,7 @@ class FeedlyAddNewFeedOperationTests: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
let progress = DownloadProgress(numberOfTasks: 0)
|
let progress = DownloadProgress(numberOfTasks: 0)
|
||||||
|
let container = support.makeTestDatabaseContainer()
|
||||||
let _ = expectationForCompletion(of: progress)
|
let _ = expectationForCompletion(of: progress)
|
||||||
|
|
||||||
let subdirectory = "feedly-add-new-feed"
|
let subdirectory = "feedly-add-new-feed"
|
||||||
|
@ -145,6 +148,7 @@ class FeedlyAddNewFeedOperationTests: XCTestCase {
|
||||||
addToCollectionService: caller,
|
addToCollectionService: caller,
|
||||||
syncUnreadIdsService: caller,
|
syncUnreadIdsService: caller,
|
||||||
getStreamContentsService: caller,
|
getStreamContentsService: caller,
|
||||||
|
database: container.database,
|
||||||
container: folder,
|
container: folder,
|
||||||
progress: progress,
|
progress: progress,
|
||||||
log: support.log)
|
log: support.log)
|
||||||
|
@ -191,6 +195,7 @@ class FeedlyAddNewFeedOperationTests: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
let progress = DownloadProgress(numberOfTasks: 0)
|
let progress = DownloadProgress(numberOfTasks: 0)
|
||||||
|
let container = support.makeTestDatabaseContainer()
|
||||||
let _ = expectationForCompletion(of: progress)
|
let _ = expectationForCompletion(of: progress)
|
||||||
|
|
||||||
let subdirectory = "feedly-add-new-feed"
|
let subdirectory = "feedly-add-new-feed"
|
||||||
|
@ -220,6 +225,7 @@ class FeedlyAddNewFeedOperationTests: XCTestCase {
|
||||||
addToCollectionService: service,
|
addToCollectionService: service,
|
||||||
syncUnreadIdsService: caller,
|
syncUnreadIdsService: caller,
|
||||||
getStreamContentsService: caller,
|
getStreamContentsService: caller,
|
||||||
|
database: container.database,
|
||||||
container: folder,
|
container: folder,
|
||||||
progress: progress,
|
progress: progress,
|
||||||
log: support.log)
|
log: support.log)
|
||||||
|
|
|
@ -156,7 +156,7 @@ final class FeedlyAccountDelegate: AccountDelegate {
|
||||||
|
|
||||||
let group = DispatchGroup()
|
let group = DispatchGroup()
|
||||||
|
|
||||||
let ingestUnread = FeedlyIngestUnreadArticleIdsOperation(account: account, credentials: credentials, service: caller, newerThan: nil, log: log)
|
let ingestUnread = FeedlyIngestUnreadArticleIdsOperation(account: account, credentials: credentials, service: caller, database: database, newerThan: nil, log: log)
|
||||||
|
|
||||||
group.enter()
|
group.enter()
|
||||||
ingestUnread.completionBlock = {
|
ingestUnread.completionBlock = {
|
||||||
|
@ -164,7 +164,7 @@ final class FeedlyAccountDelegate: AccountDelegate {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let ingestStarred = FeedlyIngestStarredArticleIdsOperation(account: account, credentials: credentials, service: caller, newerThan: nil, log: log)
|
let ingestStarred = FeedlyIngestStarredArticleIdsOperation(account: account, credentials: credentials, service: caller, database: database, newerThan: nil, log: log)
|
||||||
|
|
||||||
group.enter()
|
group.enter()
|
||||||
ingestStarred.completionBlock = {
|
ingestStarred.completionBlock = {
|
||||||
|
@ -297,6 +297,7 @@ final class FeedlyAccountDelegate: AccountDelegate {
|
||||||
addToCollectionService: caller,
|
addToCollectionService: caller,
|
||||||
syncUnreadIdsService: caller,
|
syncUnreadIdsService: caller,
|
||||||
getStreamContentsService: caller,
|
getStreamContentsService: caller,
|
||||||
|
database: database,
|
||||||
container: container,
|
container: container,
|
||||||
progress: refreshProgress,
|
progress: refreshProgress,
|
||||||
log: log)
|
log: log)
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import os.log
|
import os.log
|
||||||
|
import SyncDatabase
|
||||||
import RSWeb
|
import RSWeb
|
||||||
|
|
||||||
class FeedlyAddNewFeedOperation: FeedlyOperation, FeedlyOperationDelegate, FeedlySearchOperationDelegate, FeedlyCheckpointOperationDelegate {
|
class FeedlyAddNewFeedOperation: FeedlyOperation, FeedlyOperationDelegate, FeedlySearchOperationDelegate, FeedlyCheckpointOperationDelegate {
|
||||||
|
@ -17,6 +18,7 @@ class FeedlyAddNewFeedOperation: FeedlyOperation, FeedlyOperationDelegate, Feedl
|
||||||
private let url: String
|
private let url: String
|
||||||
private let account: Account
|
private let account: Account
|
||||||
private let credentials: Credentials
|
private let credentials: Credentials
|
||||||
|
private let database: SyncDatabase
|
||||||
private let feedName: String?
|
private let feedName: String?
|
||||||
private let addToCollectionService: FeedlyAddFeedToCollectionService
|
private let addToCollectionService: FeedlyAddFeedToCollectionService
|
||||||
private let syncUnreadIdsService: FeedlyGetStreamIdsService
|
private let syncUnreadIdsService: FeedlyGetStreamIdsService
|
||||||
|
@ -25,7 +27,7 @@ class FeedlyAddNewFeedOperation: FeedlyOperation, FeedlyOperationDelegate, Feedl
|
||||||
|
|
||||||
var addCompletionHandler: ((Result<WebFeed, Error>) -> ())?
|
var addCompletionHandler: ((Result<WebFeed, Error>) -> ())?
|
||||||
|
|
||||||
init(account: Account, credentials: Credentials, url: String, feedName: String?, searchService: FeedlySearchService, addToCollectionService: FeedlyAddFeedToCollectionService, syncUnreadIdsService: FeedlyGetStreamIdsService, getStreamContentsService: FeedlyGetStreamContentsService, container: Container, progress: DownloadProgress, log: OSLog) throws {
|
init(account: Account, credentials: Credentials, url: String, feedName: String?, searchService: FeedlySearchService, addToCollectionService: FeedlyAddFeedToCollectionService, syncUnreadIdsService: FeedlyGetStreamIdsService, getStreamContentsService: FeedlyGetStreamContentsService, database: SyncDatabase, container: Container, progress: DownloadProgress, log: OSLog) throws {
|
||||||
|
|
||||||
let validator = FeedlyFeedContainerValidator(container: container, userId: credentials.username)
|
let validator = FeedlyFeedContainerValidator(container: container, userId: credentials.username)
|
||||||
(self.folder, self.collectionId) = try validator.getValidContainer()
|
(self.folder, self.collectionId) = try validator.getValidContainer()
|
||||||
|
@ -35,6 +37,7 @@ class FeedlyAddNewFeedOperation: FeedlyOperation, FeedlyOperationDelegate, Feedl
|
||||||
self.operationQueue.isSuspended = true
|
self.operationQueue.isSuspended = true
|
||||||
self.account = account
|
self.account = account
|
||||||
self.credentials = credentials
|
self.credentials = credentials
|
||||||
|
self.database = database
|
||||||
self.feedName = feedName
|
self.feedName = feedName
|
||||||
self.addToCollectionService = addToCollectionService
|
self.addToCollectionService = addToCollectionService
|
||||||
self.syncUnreadIdsService = syncUnreadIdsService
|
self.syncUnreadIdsService = syncUnreadIdsService
|
||||||
|
@ -92,7 +95,7 @@ class FeedlyAddNewFeedOperation: FeedlyOperation, FeedlyOperationDelegate, Feedl
|
||||||
createFeeds.downloadProgress = downloadProgress
|
createFeeds.downloadProgress = downloadProgress
|
||||||
self.operationQueue.addOperation(createFeeds)
|
self.operationQueue.addOperation(createFeeds)
|
||||||
|
|
||||||
let syncUnread = FeedlyIngestUnreadArticleIdsOperation(account: account, credentials: credentials, service: syncUnreadIdsService, newerThan: nil, log: log)
|
let syncUnread = FeedlyIngestUnreadArticleIdsOperation(account: account, credentials: credentials, service: syncUnreadIdsService, database: database, newerThan: nil, log: log)
|
||||||
syncUnread.addDependency(createFeeds)
|
syncUnread.addDependency(createFeeds)
|
||||||
syncUnread.downloadProgress = downloadProgress
|
syncUnread.downloadProgress = downloadProgress
|
||||||
self.operationQueue.addOperation(syncUnread)
|
self.operationQueue.addOperation(syncUnread)
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import os.log
|
import os.log
|
||||||
|
import SyncDatabase
|
||||||
|
|
||||||
/// Single responsibility is to clone locally the remote starred article state.
|
/// Single responsibility is to clone locally the remote starred article state.
|
||||||
///
|
///
|
||||||
|
@ -19,19 +20,20 @@ final class FeedlyIngestStarredArticleIdsOperation: FeedlyOperation {
|
||||||
private let account: Account
|
private let account: Account
|
||||||
private let resource: FeedlyResourceId
|
private let resource: FeedlyResourceId
|
||||||
private let service: FeedlyGetStreamIdsService
|
private let service: FeedlyGetStreamIdsService
|
||||||
private let entryIdsProvider: FeedlyEntryIdentifierProvider
|
private let database: SyncDatabase
|
||||||
|
private var remoteEntryIds = Set<String>()
|
||||||
private let log: OSLog
|
private let log: OSLog
|
||||||
|
|
||||||
convenience init(account: Account, credentials: Credentials, service: FeedlyGetStreamIdsService, newerThan: Date?, log: OSLog) {
|
convenience init(account: Account, credentials: Credentials, service: FeedlyGetStreamIdsService, database: SyncDatabase, newerThan: Date?, log: OSLog) {
|
||||||
let resource = FeedlyTagResourceId.Global.saved(for: credentials.username)
|
let resource = FeedlyTagResourceId.Global.saved(for: credentials.username)
|
||||||
self.init(account: account, resource: resource, service: service, newerThan: newerThan, log: log)
|
self.init(account: account, resource: resource, service: service, database: database, newerThan: newerThan, log: log)
|
||||||
}
|
}
|
||||||
|
|
||||||
init(account: Account, resource: FeedlyResourceId, service: FeedlyGetStreamIdsService, newerThan: Date?, log: OSLog) {
|
init(account: Account, resource: FeedlyResourceId, service: FeedlyGetStreamIdsService, database: SyncDatabase, newerThan: Date?, log: OSLog) {
|
||||||
self.account = account
|
self.account = account
|
||||||
self.resource = resource
|
self.resource = resource
|
||||||
self.service = service
|
self.service = service
|
||||||
self.entryIdsProvider = FeedlyEntryIdentifierProvider()
|
self.database = database
|
||||||
self.log = log
|
self.log = log
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,10 +59,10 @@ final class FeedlyIngestStarredArticleIdsOperation: FeedlyOperation {
|
||||||
switch result {
|
switch result {
|
||||||
case .success(let streamIds):
|
case .success(let streamIds):
|
||||||
|
|
||||||
entryIdsProvider.addEntryIds(in: streamIds.ids)
|
remoteEntryIds.formUnion(streamIds.ids)
|
||||||
|
|
||||||
guard let continuation = streamIds.continuation else {
|
guard let continuation = streamIds.continuation else {
|
||||||
updateStarredStatuses()
|
removeEntryIdsWithPendingStatus()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,6 +73,26 @@ final class FeedlyIngestStarredArticleIdsOperation: FeedlyOperation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Do not override pending statuses with the remote statuses of the same articles, otherwise an article will temporarily re-acquire the remote status before the pending status is pushed and subseqently pulled.
|
||||||
|
private func removeEntryIdsWithPendingStatus() {
|
||||||
|
guard !isCancelled else {
|
||||||
|
didFinish()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
database.selectPendingStarredStatusArticleIDs { result in
|
||||||
|
switch result {
|
||||||
|
case .success(let pendingArticleIds):
|
||||||
|
self.remoteEntryIds.subtract(pendingArticleIds)
|
||||||
|
|
||||||
|
self.updateStarredStatuses()
|
||||||
|
|
||||||
|
case .failure(let error):
|
||||||
|
self.didFinish(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func updateStarredStatuses() {
|
private func updateStarredStatuses() {
|
||||||
guard !isCancelled else {
|
guard !isCancelled else {
|
||||||
didFinish()
|
didFinish()
|
||||||
|
@ -94,7 +116,7 @@ final class FeedlyIngestStarredArticleIdsOperation: FeedlyOperation {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let remoteStarredArticleIDs = entryIdsProvider.entryIds
|
let remoteStarredArticleIDs = remoteEntryIds
|
||||||
|
|
||||||
let group = DispatchGroup()
|
let group = DispatchGroup()
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import os.log
|
import os.log
|
||||||
import RSParser
|
import RSParser
|
||||||
|
import SyncDatabase
|
||||||
|
|
||||||
/// Single responsibility is to clone locally the remote unread article state.
|
/// Single responsibility is to clone locally the remote unread article state.
|
||||||
///
|
///
|
||||||
|
@ -20,19 +21,20 @@ final class FeedlyIngestUnreadArticleIdsOperation: FeedlyOperation {
|
||||||
private let account: Account
|
private let account: Account
|
||||||
private let resource: FeedlyResourceId
|
private let resource: FeedlyResourceId
|
||||||
private let service: FeedlyGetStreamIdsService
|
private let service: FeedlyGetStreamIdsService
|
||||||
private let entryIdsProvider: FeedlyEntryIdentifierProvider
|
private let database: SyncDatabase
|
||||||
|
private var remoteEntryIds = Set<String>()
|
||||||
private let log: OSLog
|
private let log: OSLog
|
||||||
|
|
||||||
convenience init(account: Account, credentials: Credentials, service: FeedlyGetStreamIdsService, newerThan: Date?, log: OSLog) {
|
convenience init(account: Account, credentials: Credentials, service: FeedlyGetStreamIdsService, database: SyncDatabase, newerThan: Date?, log: OSLog) {
|
||||||
let resource = FeedlyCategoryResourceId.Global.all(for: credentials.username)
|
let resource = FeedlyCategoryResourceId.Global.all(for: credentials.username)
|
||||||
self.init(account: account, resource: resource, service: service, newerThan: newerThan, log: log)
|
self.init(account: account, resource: resource, service: service, database: database, newerThan: newerThan, log: log)
|
||||||
}
|
}
|
||||||
|
|
||||||
init(account: Account, resource: FeedlyResourceId, service: FeedlyGetStreamIdsService, newerThan: Date?, log: OSLog) {
|
init(account: Account, resource: FeedlyResourceId, service: FeedlyGetStreamIdsService, database: SyncDatabase, newerThan: Date?, log: OSLog) {
|
||||||
self.account = account
|
self.account = account
|
||||||
self.resource = resource
|
self.resource = resource
|
||||||
self.service = service
|
self.service = service
|
||||||
self.entryIdsProvider = FeedlyEntryIdentifierProvider()
|
self.database = database
|
||||||
self.log = log
|
self.log = log
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,10 +60,10 @@ final class FeedlyIngestUnreadArticleIdsOperation: FeedlyOperation {
|
||||||
switch result {
|
switch result {
|
||||||
case .success(let streamIds):
|
case .success(let streamIds):
|
||||||
|
|
||||||
entryIdsProvider.addEntryIds(in: streamIds.ids)
|
remoteEntryIds.formUnion(streamIds.ids)
|
||||||
|
|
||||||
guard let continuation = streamIds.continuation else {
|
guard let continuation = streamIds.continuation else {
|
||||||
updateUnreadStatuses()
|
removeEntryIdsWithPendingStatus()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,6 +74,26 @@ final class FeedlyIngestUnreadArticleIdsOperation: FeedlyOperation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Do not override pending statuses with the remote statuses of the same articles, otherwise an article will temporarily re-acquire the remote status before the pending status is pushed and subseqently pulled.
|
||||||
|
private func removeEntryIdsWithPendingStatus() {
|
||||||
|
guard !isCancelled else {
|
||||||
|
didFinish()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
database.selectPendingReadStatusArticleIDs { result in
|
||||||
|
switch result {
|
||||||
|
case .success(let pendingArticleIds):
|
||||||
|
self.remoteEntryIds.subtract(pendingArticleIds)
|
||||||
|
|
||||||
|
self.updateUnreadStatuses()
|
||||||
|
|
||||||
|
case .failure(let error):
|
||||||
|
self.didFinish(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func updateUnreadStatuses() {
|
private func updateUnreadStatuses() {
|
||||||
guard !isCancelled else {
|
guard !isCancelled else {
|
||||||
didFinish()
|
didFinish()
|
||||||
|
@ -95,7 +117,7 @@ final class FeedlyIngestUnreadArticleIdsOperation: FeedlyOperation {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let remoteUnreadArticleIDs = entryIdsProvider.entryIds
|
let remoteUnreadArticleIDs = remoteEntryIds
|
||||||
let group = DispatchGroup()
|
let group = DispatchGroup()
|
||||||
|
|
||||||
final class ReadStatusResults {
|
final class ReadStatusResults {
|
||||||
|
|
|
@ -73,7 +73,7 @@ final class FeedlySyncAllOperation: FeedlyOperation {
|
||||||
self.operationQueue.addOperation(getAllArticleIds)
|
self.operationQueue.addOperation(getAllArticleIds)
|
||||||
|
|
||||||
// Get each page of unread article ids in the global.all stream for the last 31 days (nil = Feedly API default).
|
// Get each page of unread article ids in the global.all stream for the last 31 days (nil = Feedly API default).
|
||||||
let getUnread = FeedlyIngestUnreadArticleIdsOperation(account: account, credentials: credentials, service: getUnreadService, newerThan: nil, log: log)
|
let getUnread = FeedlyIngestUnreadArticleIdsOperation(account: account, credentials: credentials, service: getUnreadService, database: database, newerThan: nil, log: log)
|
||||||
getUnread.delegate = self
|
getUnread.delegate = self
|
||||||
getUnread.addDependency(getAllArticleIds)
|
getUnread.addDependency(getAllArticleIds)
|
||||||
getUnread.downloadProgress = downloadProgress
|
getUnread.downloadProgress = downloadProgress
|
||||||
|
@ -88,7 +88,7 @@ final class FeedlySyncAllOperation: FeedlyOperation {
|
||||||
self.operationQueue.addOperation(getUpdated)
|
self.operationQueue.addOperation(getUpdated)
|
||||||
|
|
||||||
// Get each page of the article ids for starred articles.
|
// Get each page of the article ids for starred articles.
|
||||||
let getStarred = FeedlyIngestStarredArticleIdsOperation(account: account, credentials: credentials, service: getStarredService, newerThan: nil, log: log)
|
let getStarred = FeedlyIngestStarredArticleIdsOperation(account: account, credentials: credentials, service: getStarredService, database: database, newerThan: nil, log: log)
|
||||||
getStarred.delegate = self
|
getStarred.delegate = self
|
||||||
getStarred.downloadProgress = downloadProgress
|
getStarred.downloadProgress = downloadProgress
|
||||||
getStarred.addDependency(createFeedsOperation)
|
getStarred.addDependency(createFeedsOperation)
|
||||||
|
|
Loading…
Reference in New Issue