Add the unread statuses on receipt to CloudKit.
This commit is contained in:
parent
983138366f
commit
a8dcf3eeee
@ -715,7 +715,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||||||
webFeedDictionariesNeedUpdate = true
|
webFeedDictionariesNeedUpdate = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(_ webFeed: WebFeed, with parsedFeed: ParsedFeed, _ completion: @escaping DatabaseCompletionBlock) {
|
func update(_ webFeed: WebFeed, with parsedFeed: ParsedFeed, _ completion: @escaping UpdateArticlesCompletionBlock) {
|
||||||
// Used only by an On My Mac or iCloud account.
|
// Used only by an On My Mac or iCloud account.
|
||||||
precondition(Thread.isMainThread)
|
precondition(Thread.isMainThread)
|
||||||
precondition(type == .onMyMac || type == .cloudKit)
|
precondition(type == .onMyMac || type == .cloudKit)
|
||||||
@ -723,14 +723,14 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||||||
webFeed.takeSettings(from: parsedFeed)
|
webFeed.takeSettings(from: parsedFeed)
|
||||||
let parsedItems = parsedFeed.items
|
let parsedItems = parsedFeed.items
|
||||||
guard !parsedItems.isEmpty else {
|
guard !parsedItems.isEmpty else {
|
||||||
completion(nil)
|
completion(.success(NewAndUpdatedArticles()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
update(webFeed.webFeedID, with: parsedItems, completion: completion)
|
update(webFeed.webFeedID, with: parsedItems, completion: completion)
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(_ webFeedID: String, with parsedItems: Set<ParsedItem>, completion: @escaping DatabaseCompletionBlock) {
|
func update(_ webFeedID: String, with parsedItems: Set<ParsedItem>, completion: @escaping UpdateArticlesCompletionBlock) {
|
||||||
// Used only by an On My Mac or iCloud account.
|
// Used only by an On My Mac or iCloud account.
|
||||||
precondition(Thread.isMainThread)
|
precondition(Thread.isMainThread)
|
||||||
precondition(type == .onMyMac || type == .cloudKit)
|
precondition(type == .onMyMac || type == .cloudKit)
|
||||||
@ -739,9 +739,9 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||||||
switch updateArticlesResult {
|
switch updateArticlesResult {
|
||||||
case .success(let newAndUpdatedArticles):
|
case .success(let newAndUpdatedArticles):
|
||||||
self.sendNotificationAbout(newAndUpdatedArticles)
|
self.sendNotificationAbout(newAndUpdatedArticles)
|
||||||
completion(nil)
|
completion(.success(newAndUpdatedArticles))
|
||||||
case .failure(let databaseError):
|
case .failure(let databaseError):
|
||||||
completion(databaseError)
|
completion(.failure(databaseError))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import SyncDatabase
|
|||||||
import RSCore
|
import RSCore
|
||||||
import RSParser
|
import RSParser
|
||||||
import Articles
|
import Articles
|
||||||
|
import ArticlesDatabase
|
||||||
import RSWeb
|
import RSWeb
|
||||||
|
|
||||||
public enum CloudKitAccountDelegateError: String, Error {
|
public enum CloudKitAccountDelegateError: String, Error {
|
||||||
@ -50,7 +51,6 @@ final class CloudKitAccountDelegate: AccountDelegate {
|
|||||||
var accountMetadata: AccountMetadata?
|
var accountMetadata: AccountMetadata?
|
||||||
|
|
||||||
var refreshProgress = DownloadProgress(numberOfTasks: 0)
|
var refreshProgress = DownloadProgress(numberOfTasks: 0)
|
||||||
var refreshAllCompletion: ((Result<Void, Error>) -> Void)? = nil
|
|
||||||
|
|
||||||
init(dataFolder: String) {
|
init(dataFolder: String) {
|
||||||
accountZone = CloudKitAccountZone(container: container)
|
accountZone = CloudKitAccountZone(container: container)
|
||||||
@ -79,13 +79,12 @@ final class CloudKitAccountDelegate: AccountDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func refreshAll(for account: Account, completion: @escaping (Result<Void, Error>) -> Void) {
|
func refreshAll(for account: Account, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||||
guard refreshAllCompletion == nil else {
|
guard refreshProgress.isComplete else {
|
||||||
completion(.success(()))
|
completion(.success(()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
refreshAllCompletion = completion
|
|
||||||
|
|
||||||
refreshAll(for: account, downloadFeeds: true)
|
refreshAll(for: account, downloadFeeds: true, completion: completion)
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendArticleStatus(for account: Account, completion: @escaping ((Result<Void, Error>) -> Void)) {
|
func sendArticleStatus(for account: Account, completion: @escaping ((Result<Void, Error>) -> Void)) {
|
||||||
@ -155,11 +154,10 @@ final class CloudKitAccountDelegate: AccountDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func importOPML(for account:Account, opmlFile: URL, completion: @escaping (Result<Void, Error>) -> Void) {
|
func importOPML(for account:Account, opmlFile: URL, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||||
guard refreshAllCompletion == nil else {
|
guard refreshProgress.isComplete else {
|
||||||
completion(.success(()))
|
completion(.success(()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
refreshAllCompletion = completion
|
|
||||||
|
|
||||||
var fileData: Data?
|
var fileData: Data?
|
||||||
|
|
||||||
@ -214,7 +212,7 @@ final class CloudKitAccountDelegate: AccountDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.accountZone.importOPML(rootExternalID: rootExternalID, items: normalizedItems) { _ in
|
self.accountZone.importOPML(rootExternalID: rootExternalID, items: normalizedItems) { _ in
|
||||||
self.refreshAll(for: account, downloadFeeds: false)
|
self.refreshAll(for: account, downloadFeeds: false, completion: completion)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -493,7 +491,7 @@ final class CloudKitAccountDelegate: AccountDelegate {
|
|||||||
switch result {
|
switch result {
|
||||||
case .success(let externalID):
|
case .success(let externalID):
|
||||||
account.externalID = externalID
|
account.externalID = externalID
|
||||||
self.refreshAll(for: account, downloadFeeds: false)
|
self.refreshAll(for: account, downloadFeeds: false) { _ in }
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
os_log(.error, log: self.log, "Error adding account container: %@", error.localizedDescription)
|
os_log(.error, log: self.log, "Error adding account container: %@", error.localizedDescription)
|
||||||
}
|
}
|
||||||
@ -533,7 +531,7 @@ final class CloudKitAccountDelegate: AccountDelegate {
|
|||||||
|
|
||||||
private extension CloudKitAccountDelegate {
|
private extension CloudKitAccountDelegate {
|
||||||
|
|
||||||
func refreshAll(for account: Account, downloadFeeds: Bool) {
|
func refreshAll(for account: Account, downloadFeeds: Bool, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||||
|
|
||||||
let intialWebFeedsCount = downloadFeeds ? account.flattenedWebFeeds().count : 0
|
let intialWebFeedsCount = downloadFeeds ? account.flattenedWebFeeds().count : 0
|
||||||
refreshProgress.addToNumberOfTasksAndRemaining(3 + intialWebFeedsCount)
|
refreshProgress.addToNumberOfTasksAndRemaining(3 + intialWebFeedsCount)
|
||||||
@ -541,8 +539,7 @@ private extension CloudKitAccountDelegate {
|
|||||||
func fail(_ error: Error) {
|
func fail(_ error: Error) {
|
||||||
self.processAccountError(account, error)
|
self.processAccountError(account, error)
|
||||||
self.refreshProgress.clear()
|
self.refreshProgress.clear()
|
||||||
self.refreshAllCompletion?(.failure(error))
|
completion(.failure(error))
|
||||||
self.refreshAllCompletion = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BatchUpdate.shared.start()
|
BatchUpdate.shared.start()
|
||||||
@ -569,12 +566,24 @@ private extension CloudKitAccountDelegate {
|
|||||||
self.refreshProgress.completeTask()
|
self.refreshProgress.completeTask()
|
||||||
|
|
||||||
guard downloadFeeds else {
|
guard downloadFeeds else {
|
||||||
self.refreshAllCompletion?(.success(()))
|
completion(.success(()))
|
||||||
self.refreshAllCompletion = nil
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
self.refresher?.refreshFeeds(webFeeds)
|
self.refresher?.refreshFeeds(webFeeds) {
|
||||||
|
|
||||||
|
account.metadata.lastArticleFetchEndTime = Date()
|
||||||
|
|
||||||
|
self.sendArticleStatus(for: account) { result in
|
||||||
|
switch result {
|
||||||
|
case .success:
|
||||||
|
completion(.success(()))
|
||||||
|
case .failure(let error):
|
||||||
|
fail(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
fail(error)
|
fail(error)
|
||||||
@ -605,6 +614,15 @@ private extension CloudKitAccountDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension CloudKitAccountDelegate: LocalAccountRefresherDelegate {
|
extension CloudKitAccountDelegate: LocalAccountRefresherDelegate {
|
||||||
|
|
||||||
|
func localAccountRefresher(_ refresher: LocalAccountRefresher, didProcess newAndUpdatedArticles: NewAndUpdatedArticles) {
|
||||||
|
if let newArticles = newAndUpdatedArticles.newArticles {
|
||||||
|
let syncStatuses = newArticles.map { article in
|
||||||
|
return SyncStatus(articleID: article.articleID, key: .read, flag: false)
|
||||||
|
}
|
||||||
|
database.insertStatuses(syncStatuses)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func localAccountRefresher(_ refresher: LocalAccountRefresher, requestCompletedFor: WebFeed) {
|
func localAccountRefresher(_ refresher: LocalAccountRefresher, requestCompletedFor: WebFeed) {
|
||||||
refreshProgress.completeTask()
|
refreshProgress.completeTask()
|
||||||
@ -612,9 +630,6 @@ extension CloudKitAccountDelegate: LocalAccountRefresherDelegate {
|
|||||||
|
|
||||||
func localAccountRefresherDidFinish(_ refresher: LocalAccountRefresher) {
|
func localAccountRefresherDidFinish(_ refresher: LocalAccountRefresher) {
|
||||||
self.refreshProgress.clear()
|
self.refreshProgress.clear()
|
||||||
account?.metadata.lastArticleFetchEndTime = Date()
|
|
||||||
refreshAllCompletion?(.success(()))
|
|
||||||
refreshAllCompletion = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -138,9 +138,9 @@ private extension CloudKitArticlesZoneDelegate {
|
|||||||
for receivedStarredArticle in receivedStarredArticles {
|
for receivedStarredArticle in receivedStarredArticles {
|
||||||
if let parsedItem = makeParsedItem(receivedStarredArticle) {
|
if let parsedItem = makeParsedItem(receivedStarredArticle) {
|
||||||
group.enter()
|
group.enter()
|
||||||
self.account?.update(parsedItem.feedURL, with: Set([parsedItem])) { databaseError in
|
self.account?.update(parsedItem.feedURL, with: Set([parsedItem])) { result in
|
||||||
group.leave()
|
group.leave()
|
||||||
if let databaseError = databaseError {
|
if case .failure(let databaseError) = result {
|
||||||
os_log(.error, log: self.log, "Error occurred while storing starred items: %@", databaseError.localizedDescription)
|
os_log(.error, log: self.log, "Error occurred while storing starred items: %@", databaseError.localizedDescription)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import Foundation
|
|||||||
import RSCore
|
import RSCore
|
||||||
import RSParser
|
import RSParser
|
||||||
import Articles
|
import Articles
|
||||||
|
import ArticlesDatabase
|
||||||
import RSWeb
|
import RSWeb
|
||||||
|
|
||||||
public enum LocalAccountDelegateError: String, Error {
|
public enum LocalAccountDelegateError: String, Error {
|
||||||
@ -233,6 +234,9 @@ final class LocalAccountDelegate: AccountDelegate {
|
|||||||
|
|
||||||
extension LocalAccountDelegate: LocalAccountRefresherDelegate {
|
extension LocalAccountDelegate: LocalAccountRefresherDelegate {
|
||||||
|
|
||||||
|
func localAccountRefresher(_ refresher: LocalAccountRefresher, didProcess newAndUpdatedArticles: NewAndUpdatedArticles) {
|
||||||
|
}
|
||||||
|
|
||||||
func localAccountRefresher(_ refresher: LocalAccountRefresher, requestCompletedFor: WebFeed) {
|
func localAccountRefresher(_ refresher: LocalAccountRefresher, requestCompletedFor: WebFeed) {
|
||||||
refreshProgress.completeTask()
|
refreshProgress.completeTask()
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,10 @@ import RSCore
|
|||||||
import RSParser
|
import RSParser
|
||||||
import RSWeb
|
import RSWeb
|
||||||
import Articles
|
import Articles
|
||||||
|
import ArticlesDatabase
|
||||||
|
|
||||||
protocol LocalAccountRefresherDelegate {
|
protocol LocalAccountRefresherDelegate {
|
||||||
|
func localAccountRefresher(_ refresher: LocalAccountRefresher, didProcess: NewAndUpdatedArticles)
|
||||||
func localAccountRefresher(_ refresher: LocalAccountRefresher, requestCompletedFor: WebFeed)
|
func localAccountRefresher(_ refresher: LocalAccountRefresher, requestCompletedFor: WebFeed)
|
||||||
func localAccountRefresherDidFinish(_ refresher: LocalAccountRefresher)
|
func localAccountRefresherDidFinish(_ refresher: LocalAccountRefresher)
|
||||||
}
|
}
|
||||||
@ -97,12 +99,12 @@ extension LocalAccountRefresher: DownloadSessionDelegate {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
account.update(feed, with: parsedFeed) { error in
|
account.update(feed, with: parsedFeed) { result in
|
||||||
if error == nil {
|
if case .success(let newAndUpdatedArticles) = result {
|
||||||
|
self.delegate?.localAccountRefresher(self, didProcess: newAndUpdatedArticles)
|
||||||
if let httpResponse = response as? HTTPURLResponse {
|
if let httpResponse = response as? HTTPURLResponse {
|
||||||
feed.conditionalGetInfo = HTTPConditionalGetInfo(urlResponse: httpResponse)
|
feed.conditionalGetInfo = HTTPConditionalGetInfo(urlResponse: httpResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
feed.contentHash = dataHash
|
feed.contentHash = dataHash
|
||||||
}
|
}
|
||||||
completion()
|
completion()
|
||||||
@ -157,9 +159,9 @@ extension LocalAccountRefresher: DownloadSessionDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func downloadSessionDidCompleteDownloadObjects(_ downloadSession: DownloadSession) {
|
func downloadSessionDidCompleteDownloadObjects(_ downloadSession: DownloadSession) {
|
||||||
delegate?.localAccountRefresherDidFinish(self)
|
|
||||||
completions.forEach({ $0() })
|
completions.forEach({ $0() })
|
||||||
completions = [() -> Void]()
|
completions = [() -> Void]()
|
||||||
|
delegate?.localAccountRefresherDidFinish(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,16 @@ public typealias SingleUnreadCountCompletionBlock = (SingleUnreadCountResult) ->
|
|||||||
public struct NewAndUpdatedArticles {
|
public struct NewAndUpdatedArticles {
|
||||||
public let newArticles: Set<Article>?
|
public let newArticles: Set<Article>?
|
||||||
public let updatedArticles: Set<Article>?
|
public let updatedArticles: Set<Article>?
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
self.newArticles = Set<Article>()
|
||||||
|
self.updatedArticles = Set<Article>()
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(newArticles: Set<Article>?, updatedArticles: Set<Article>?) {
|
||||||
|
self.newArticles = newArticles
|
||||||
|
self.updatedArticles = updatedArticles
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public typealias UpdateArticlesResult = Result<NewAndUpdatedArticles, DatabaseError>
|
public typealias UpdateArticlesResult = Result<NewAndUpdatedArticles, DatabaseError>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user