Make status marking asynchronous.

This commit is contained in:
Maurice Parker 2020-10-24 17:17:46 -05:00
parent fa08b7d2e0
commit ec66c08f9a
12 changed files with 133 additions and 110 deletions

View File

@ -520,8 +520,8 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
addOPMLItems(OPMLNormalizer.normalize(items))
}
public func markArticles(_ articles: Set<Article>, statusKey: ArticleStatus.Key, flag: Bool) -> Set<Article>? {
return delegate.markArticles(for: self, articles: articles, statusKey: statusKey, flag: flag)
public func markArticles(_ articles: Set<Article>, statusKey: ArticleStatus.Key, flag: Bool) {
delegate.markArticles(for: self, articles: articles, statusKey: statusKey, flag: flag)
}
func existingContainer(withExternalID externalID: String) -> Container? {
@ -794,18 +794,23 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
}
}
@discardableResult
func update(_ articles: Set<Article>, statusKey: ArticleStatus.Key, flag: Bool) throws -> Set<Article>? {
func update(_ articles: Set<Article>, statusKey: ArticleStatus.Key, flag: Bool, completion: @escaping ArticleSetResultBlock) {
// Returns set of Articles whose statuses did change.
guard !articles.isEmpty, let updatedStatuses = try database.mark(articles, statusKey: statusKey, flag: flag) else {
return nil
guard !articles.isEmpty else {
completion(.success(Set<Article>()))
return
}
let updatedArticleIDs = updatedStatuses.articleIDs()
let updatedArticles = Set(articles.filter{ updatedArticleIDs.contains($0.articleID) })
noteStatusesForArticlesDidChange(updatedArticles)
return updatedArticles
database.mark(articles, statusKey: statusKey, flag: flag) { result in
switch result {
case .success(let updatedStatuses):
let updatedArticleIDs = updatedStatuses.articleIDs()
let updatedArticles = Set(articles.filter{ updatedArticleIDs.contains($0.articleID) })
self.noteStatusesForArticlesDidChange(updatedArticles)
case .failure(let error):
completion(.failure(error))
}
}
}
/// Make sure statuses exist. Any existing statuses wont be touched.

View File

@ -44,7 +44,7 @@ protocol AccountDelegate {
func restoreWebFeed(for account: Account, feed: WebFeed, container: Container, completion: @escaping (Result<Void, Error>) -> Void)
func restoreFolder(for account: Account, folder: Folder, completion: @escaping (Result<Void, Error>) -> Void)
func markArticles(for account: Account, articles: Set<Article>, statusKey: ArticleStatus.Key, flag: Bool) -> Set<Article>?
func markArticles(for account: Account, articles: Set<Article>, statusKey: ArticleStatus.Key, flag: Bool)
// Called at the end of accounts init method.
func accountDidInitialize(_ account: Account)

View File

@ -399,21 +399,25 @@ final class CloudKitAccountDelegate: AccountDelegate {
}
}
func markArticles(for account: Account, articles: Set<Article>, statusKey: ArticleStatus.Key, flag: Bool) -> Set<Article>? {
let syncStatuses = articles.map { article in
return SyncStatus(articleID: article.articleID, key: SyncStatus.Key(statusKey), flag: flag)
}
try? database.insertStatuses(syncStatuses)
let articles = try? account.update(articles, statusKey: statusKey, flag: flag)
func markArticles(for account: Account, articles: Set<Article>, statusKey: ArticleStatus.Key, flag: Bool) {
account.update(articles, statusKey: statusKey, flag: flag) { result in
switch result {
case .success(let articles):
let syncStatuses = articles.map { article in
return SyncStatus(articleID: article.articleID, key: SyncStatus.Key(statusKey), flag: flag)
}
database.selectPendingCount { result in
if let count = try? result.get(), count > 100 {
self.sendArticleStatus(for: account, showProgress: false) { _ in }
try? self.database.insertStatuses(syncStatuses)
self.database.selectPendingCount { result in
if let count = try? result.get(), count > 100 {
self.sendArticleStatus(for: account, showProgress: false) { _ in }
}
}
case .failure(let error):
os_log(.error, log: self.log, "Error marking article status: %@", error.localizedDescription)
}
}
return articles
}
func accountDidInitialize(_ account: Account) {

View File

@ -435,19 +435,27 @@ final class FeedWranglerAccountDelegate: AccountDelegate {
fatalError()
}
func markArticles(for account: Account, articles: Set<Article>, statusKey: ArticleStatus.Key, flag: Bool) -> Set<Article>? {
let syncStatuses = articles.map { SyncStatus(articleID: $0.articleID, key: SyncStatus.Key(statusKey), flag: flag)}
try? database.insertStatuses(syncStatuses)
database.selectPendingCount { result in
if let count = try? result.get(), count > 0 {
self.sendArticleStatus(for: account) { _ in }
func markArticles(for account: Account, articles: Set<Article>, statusKey: ArticleStatus.Key, flag: Bool) {
account.update(articles, statusKey: statusKey, flag: flag) { result in
switch result {
case .success(let articles):
let syncStatuses = articles.map { article in
return SyncStatus(articleID: article.articleID, key: SyncStatus.Key(statusKey), flag: flag)
}
try? self.database.insertStatuses(syncStatuses)
self.database.selectPendingCount { result in
if let count = try? result.get(), count > 100 {
self.sendArticleStatus(for: account) { _ in }
}
}
case .failure(let error):
os_log(.error, log: self.log, "Error marking article status: %@", error.localizedDescription)
}
}
return try? account.update(articles, statusKey: statusKey, flag: flag)
}
func accountDidInitialize(_ account: Account) {
credentials = try? account.retrieveCredentials(type: .feedWranglerToken)
}

View File

@ -534,22 +534,27 @@ final class FeedbinAccountDelegate: AccountDelegate {
}
func markArticles(for account: Account, articles: Set<Article>, statusKey: ArticleStatus.Key, flag: Bool) -> Set<Article>? {
func markArticles(for account: Account, articles: Set<Article>, statusKey: ArticleStatus.Key, flag: Bool) {
account.update(articles, statusKey: statusKey, flag: flag) { result in
switch result {
case .success(let articles):
let syncStatuses = articles.map { article in
return SyncStatus(articleID: article.articleID, key: SyncStatus.Key(statusKey), flag: flag)
}
let syncStatuses = articles.map { article in
return SyncStatus(articleID: article.articleID, key: SyncStatus.Key(statusKey), flag: flag)
}
try? database.insertStatuses(syncStatuses)
try? self.database.insertStatuses(syncStatuses)
database.selectPendingCount { result in
if let count = try? result.get(), count > 100 {
self.sendArticleStatus(for: account) { _ in }
self.database.selectPendingCount { result in
if let count = try? result.get(), count > 100 {
self.sendArticleStatus(for: account) { _ in }
}
}
case .failure(let error):
os_log(.error, log: self.log, "Error marking article status: %@", error.localizedDescription)
}
}
return try? account.update(articles, statusKey: statusKey, flag: flag)
}
func accountDidInitialize(_ account: Account) {
credentials = try? account.retrieveCredentials(type: .basic)
}

View File

@ -487,24 +487,27 @@ final class FeedlyAccountDelegate: AccountDelegate {
}
}
func markArticles(for account: Account, articles: Set<Article>, statusKey: ArticleStatus.Key, flag: Bool) -> Set<Article>? {
let syncStatuses = articles.map { article in
return SyncStatus(articleID: article.articleID, key: SyncStatus.Key(statusKey), flag: flag)
}
try? database.insertStatuses(syncStatuses)
os_log(.debug, log: log, "Marking %@ as %@.", articles.map { $0.title }, syncStatuses)
func markArticles(for account: Account, articles: Set<Article>, statusKey: ArticleStatus.Key, flag: Bool) {
account.update(articles, statusKey: statusKey, flag: flag) { result in
switch result {
case .success(let articles):
let syncStatuses = articles.map { article in
return SyncStatus(articleID: article.articleID, key: SyncStatus.Key(statusKey), flag: flag)
}
database.selectPendingCount { result in
if let count = try? result.get(), count > 100 {
self.sendArticleStatus(for: account) { _ in }
try? self.database.insertStatuses(syncStatuses)
self.database.selectPendingCount { result in
if let count = try? result.get(), count > 100 {
self.sendArticleStatus(for: account) { _ in }
}
}
case .failure(let error):
os_log(.error, log: self.log, "Error marking article status: %@", error.localizedDescription)
}
}
return try? account.update(articles, statusKey: statusKey, flag: flag)
}
func accountDidInitialize(_ account: Account) {
initializedAccount = account
credentials = try? account.retrieveCredentials(type: .oauthAccessToken)

View File

@ -203,8 +203,8 @@ final class LocalAccountDelegate: AccountDelegate {
completion(.success(()))
}
func markArticles(for account: Account, articles: Set<Article>, statusKey: ArticleStatus.Key, flag: Bool) -> Set<Article>? {
return try? account.update(articles, statusKey: statusKey, flag: flag)
func markArticles(for account: Account, articles: Set<Article>, statusKey: ArticleStatus.Key, flag: Bool) {
account.update(articles, statusKey: statusKey, flag: flag) { _ in }
}
func accountDidInitialize(_ account: Account) {

View File

@ -573,19 +573,25 @@ final class NewsBlurAccountDelegate: AccountDelegate {
}
}
func markArticles(for account: Account, articles: Set<Article>, statusKey: ArticleStatus.Key, flag: Bool) -> Set<Article>? {
let syncStatuses = articles.map { article in
return SyncStatus(articleID: article.articleID, key: SyncStatus.Key(statusKey), flag: flag)
}
try? database.insertStatuses(syncStatuses)
func markArticles(for account: Account, articles: Set<Article>, statusKey: ArticleStatus.Key, flag: Bool) {
account.update(articles, statusKey: statusKey, flag: flag) { result in
switch result {
case .success(let articles):
let syncStatuses = articles.map { article in
return SyncStatus(articleID: article.articleID, key: SyncStatus.Key(statusKey), flag: flag)
}
database.selectPendingCount { result in
if let count = try? result.get(), count > 100 {
self.sendArticleStatus(for: account) { _ in }
try? self.database.insertStatuses(syncStatuses)
self.database.selectPendingCount { result in
if let count = try? result.get(), count > 100 {
self.sendArticleStatus(for: account) { _ in }
}
}
case .failure(let error):
os_log(.error, log: self.log, "Error marking article status: %@", error.localizedDescription)
}
}
return try? account.update(articles, statusKey: statusKey, flag: flag)
}
func accountDidInitialize(_ account: Account) {

View File

@ -471,24 +471,27 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
}
func markArticles(for account: Account, articles: Set<Article>, statusKey: ArticleStatus.Key, flag: Bool) -> Set<Article>? {
let syncStatuses = articles.map { article in
return SyncStatus(articleID: article.articleID, key: SyncStatus.Key(statusKey), flag: flag)
}
try? database.insertStatuses(syncStatuses)
database.selectPendingCount { result in
if let count = try? result.get(), count > 100 {
self.sendArticleStatus(for: account) { _ in }
func markArticles(for account: Account, articles: Set<Article>, statusKey: ArticleStatus.Key, flag: Bool) {
account.update(articles, statusKey: statusKey, flag: flag) { result in
switch result {
case .success(let articles):
let syncStatuses = articles.map { article in
return SyncStatus(articleID: article.articleID, key: SyncStatus.Key(statusKey), flag: flag)
}
try? self.database.insertStatuses(syncStatuses)
self.database.selectPendingCount { result in
if let count = try? result.get(), count > 100 {
self.sendArticleStatus(for: account) { _ in }
}
}
case .failure(let error):
os_log(.error, log: self.log, "Error marking article status: %@", error.localizedDescription)
}
}
return try? account.update(articles, statusKey: statusKey, flag: flag)
}
func accountDidInitialize(_ account: Account) {
credentials = try? account.retrieveCredentials(type: .readerAPIKey)
}

View File

@ -237,8 +237,8 @@ public final class ArticlesDatabase {
articlesTable.fetchArticleIDsForStatusesWithoutArticlesNewerThanCutoffDate(completion)
}
public func mark(_ articles: Set<Article>, statusKey: ArticleStatus.Key, flag: Bool) throws -> Set<ArticleStatus>? {
return try articlesTable.mark(articles, statusKey, flag)
public func mark(_ articles: Set<Article>, statusKey: ArticleStatus.Key, flag: Bool, completion: @escaping ArticleStatusesResultBlock) {
return articlesTable.mark(articles, statusKey, flag, completion)
}
public func markAndFetchNew(articleIDs: Set<String>, statusKey: ArticleStatus.Key, flag: Bool, completion: @escaping ArticleIDsCompletionBlock) {

View File

@ -434,22 +434,20 @@ final class ArticlesTable: DatabaseTable {
statusesTable.fetchArticleIDsForStatusesWithoutArticlesNewerThan(articleCutoffDate, completion)
}
func mark(_ articles: Set<Article>, _ statusKey: ArticleStatus.Key, _ flag: Bool) throws -> Set<ArticleStatus>? {
var statuses: Set<ArticleStatus>?
var error: DatabaseError?
self.queue.runInTransactionSync { databaseResult in
func mark(_ articles: Set<Article>, _ statusKey: ArticleStatus.Key, _ flag: Bool, _ completion: @escaping ArticleStatusesResultBlock) {
self.queue.runInTransaction { databaseResult in
switch databaseResult {
case .success(let database):
statuses = self.statusesTable.mark(articles.statuses(), statusKey, flag, database)
let statuses = self.statusesTable.mark(articles.statuses(), statusKey, flag, database)
DispatchQueue.main.async {
completion(.success(statuses ?? Set<ArticleStatus>()))
}
case .failure(let databaseError):
error = databaseError
DispatchQueue.main.async {
completion(.failure(databaseError))
}
}
}
if let error = error {
throw error
}
return statuses
}
func markAndFetchNew(_ articleIDs: Set<String>, _ statusKey: ArticleStatus.Key, _ flag: Bool, _ completion: @escaping ArticleIDsCompletionBlock) {

View File

@ -13,25 +13,16 @@ import Account
// These handle multiple accounts.
@discardableResult
func markArticles(_ articles: Set<Article>, statusKey: ArticleStatus.Key, flag: Bool) -> Set<Article>? {
func markArticles(_ articles: Set<Article>, statusKey: ArticleStatus.Key, flag: Bool) {
let d: [String: Set<Article>] = accountAndArticlesDictionary(articles)
var updatedArticles = Set<Article>()
for (accountID, accountArticles) in d {
guard let account = AccountManager.shared.existingAccount(with: accountID) else {
continue
}
if let accountUpdatedArticles = account.markArticles(accountArticles, statusKey: statusKey, flag: flag) {
updatedArticles.formUnion(accountUpdatedArticles)
}
account.markArticles(accountArticles, statusKey: statusKey, flag: flag)
}
return updatedArticles
}
private func accountAndArticlesDictionary(_ articles: Set<Article>) -> [String: Set<Article>] {