Implement revised retention policy for feed-based accounts. Keep all articles currently in the feed and all articles in the last 30 days.

This commit is contained in:
Brent Simmons 2020-04-22 21:43:10 -07:00
parent f00df4a8e9
commit c55d8c540e
3 changed files with 38 additions and 22 deletions

View File

@ -1248,32 +1248,41 @@ private extension Account {
} }
} }
func sendNotificationAbout(_ newAndUpdatedArticles: NewAndUpdatedArticles) { func sendNotificationAbout(_ articleChanges: ArticleChanges) {
var webFeeds = Set<WebFeed>() var webFeeds = Set<WebFeed>()
if let newArticles = newAndUpdatedArticles.newArticles { if let newArticles = articleChanges.newArticles {
webFeeds.formUnion(Set(newArticles.compactMap { $0.webFeed })) webFeeds.formUnion(Set(newArticles.compactMap { $0.webFeed }))
} }
if let updatedArticles = newAndUpdatedArticles.updatedArticles { if let updatedArticles = articleChanges.updatedArticles {
webFeeds.formUnion(Set(updatedArticles.compactMap { $0.webFeed })) webFeeds.formUnion(Set(updatedArticles.compactMap { $0.webFeed }))
} }
var shouldSendNotification = false var shouldSendNotification = false
var shouldUpdateUnreadCounts = false
var userInfo = [String: Any]() var userInfo = [String: Any]()
if let newArticles = newAndUpdatedArticles.newArticles, !newArticles.isEmpty { if let newArticles = articleChanges.newArticles, !newArticles.isEmpty {
shouldSendNotification = true shouldSendNotification = true
shouldUpdateUnreadCounts = true
userInfo[UserInfoKey.newArticles] = newArticles userInfo[UserInfoKey.newArticles] = newArticles
}
if let updatedArticles = articleChanges.updatedArticles, !updatedArticles.isEmpty {
shouldSendNotification = true
userInfo[UserInfoKey.updatedArticles] = updatedArticles
}
if let deletedArticles = articleChanges.deletedArticles, !deletedArticles.isEmpty {
shouldUpdateUnreadCounts = true
}
if shouldUpdateUnreadCounts {
self.updateUnreadCounts(for: webFeeds) { self.updateUnreadCounts(for: webFeeds) {
NotificationCenter.default.post(name: .DownloadArticlesDidUpdateUnreadCounts, object: self, userInfo: nil) NotificationCenter.default.post(name: .DownloadArticlesDidUpdateUnreadCounts, object: self, userInfo: nil)
} }
} }
if let updatedArticles = newAndUpdatedArticles.updatedArticles, !updatedArticles.isEmpty {
shouldSendNotification = true
userInfo[UserInfoKey.updatedArticles] = updatedArticles
}
if shouldSendNotification { if shouldSendNotification {
userInfo[UserInfoKey.webFeeds] = webFeeds userInfo[UserInfoKey.webFeeds] = webFeeds
NotificationCenter.default.post(name: .AccountDidDownloadArticles, object: self, userInfo: userInfo) NotificationCenter.default.post(name: .AccountDidDownloadArticles, object: self, userInfo: userInfo)

View File

@ -24,12 +24,13 @@ public typealias UnreadCountDictionaryCompletionBlock = (UnreadCountDictionaryCo
public typealias SingleUnreadCountResult = Result<Int, DatabaseError> public typealias SingleUnreadCountResult = Result<Int, DatabaseError>
public typealias SingleUnreadCountCompletionBlock = (SingleUnreadCountResult) -> Void public typealias SingleUnreadCountCompletionBlock = (SingleUnreadCountResult) -> Void
public struct NewAndUpdatedArticles { public struct ArticleChanges {
public let newArticles: Set<Article>? public let newArticles: Set<Article>?
public let updatedArticles: Set<Article>? public let updatedArticles: Set<Article>?
public let deletedArticles: Set<Article>?
} }
public typealias UpdateArticlesResult = Result<NewAndUpdatedArticles, DatabaseError> public typealias UpdateArticlesResult = Result<ArticleChanges, DatabaseError>
public typealias UpdateArticlesCompletionBlock = (UpdateArticlesResult) -> Void public typealias UpdateArticlesCompletionBlock = (UpdateArticlesResult) -> Void
public typealias ArticleSetResult = Result<Set<Article>, DatabaseError> public typealias ArticleSetResult = Result<Set<Article>, DatabaseError>

View File

@ -175,7 +175,7 @@ final class ArticlesTable: DatabaseTable {
func update(_ parsedItems: Set<ParsedItem>, _ webFeedID: String, _ completion: @escaping UpdateArticlesCompletionBlock) { func update(_ parsedItems: Set<ParsedItem>, _ webFeedID: String, _ completion: @escaping UpdateArticlesCompletionBlock) {
precondition(retentionStyle == .feedBased) precondition(retentionStyle == .feedBased)
if parsedItems.isEmpty { if parsedItems.isEmpty {
callUpdateArticlesCompletionBlock(nil, nil, completion) callUpdateArticlesCompletionBlock(nil, nil, nil, completion)
return return
} }
@ -199,7 +199,7 @@ final class ArticlesTable: DatabaseTable {
let incomingArticles = Article.articlesWithParsedItems(parsedItems, webFeedID, self.accountID, statusesDictionary) //2 let incomingArticles = Article.articlesWithParsedItems(parsedItems, webFeedID, self.accountID, statusesDictionary) //2
if incomingArticles.isEmpty { if incomingArticles.isEmpty {
self.callUpdateArticlesCompletionBlock(nil, nil, completion) self.callUpdateArticlesCompletionBlock(nil, nil, nil, completion)
return return
} }
@ -209,13 +209,19 @@ final class ArticlesTable: DatabaseTable {
let newArticles = self.findAndSaveNewArticles(incomingArticles, fetchedArticlesDictionary, database) //5 let newArticles = self.findAndSaveNewArticles(incomingArticles, fetchedArticlesDictionary, database) //5
let updatedArticles = self.findAndSaveUpdatedArticles(incomingArticles, fetchedArticlesDictionary, database) //6 let updatedArticles = self.findAndSaveUpdatedArticles(incomingArticles, fetchedArticlesDictionary, database) //6
self.callUpdateArticlesCompletionBlock(newArticles, updatedArticles, completion) //7 // Articles to delete are 1) no longer in feed and 2) older than 30 days.
let cutoffDate = Date().bySubtracting(days: 30)
let articlesToDelete = fetchedArticles.filter { (article) -> Bool in
return article.status.dateArrived < cutoffDate && !articleIDs.contains(article.articleID)
}
self.callUpdateArticlesCompletionBlock(newArticles, updatedArticles, articlesToDelete, completion) //7
self.addArticlesToCache(newArticles) self.addArticlesToCache(newArticles)
self.addArticlesToCache(updatedArticles) self.addArticlesToCache(updatedArticles)
// 8. Delete articles no longer in feed. // 8. Delete articles no longer in feed.
let articleIDsToDelete = fetchedArticles.articleIDs().filter { !(articleIDs.contains($0)) } let articleIDsToDelete = articlesToDelete.articleIDs()
if !articleIDsToDelete.isEmpty { if !articleIDsToDelete.isEmpty {
self.removeArticles(articleIDsToDelete, database) self.removeArticles(articleIDsToDelete, database)
self.removeArticleIDsFromCache(articleIDsToDelete) self.removeArticleIDsFromCache(articleIDsToDelete)
@ -244,7 +250,7 @@ final class ArticlesTable: DatabaseTable {
func update(_ webFeedIDsAndItems: [String: Set<ParsedItem>], _ read: Bool, _ completion: @escaping UpdateArticlesCompletionBlock) { func update(_ webFeedIDsAndItems: [String: Set<ParsedItem>], _ read: Bool, _ completion: @escaping UpdateArticlesCompletionBlock) {
precondition(retentionStyle == .syncSystem) precondition(retentionStyle == .syncSystem)
if webFeedIDsAndItems.isEmpty { if webFeedIDsAndItems.isEmpty {
callUpdateArticlesCompletionBlock(nil, nil, completion) callUpdateArticlesCompletionBlock(nil, nil, nil, completion)
return return
} }
@ -270,13 +276,13 @@ final class ArticlesTable: DatabaseTable {
let allIncomingArticles = Article.articlesWithWebFeedIDsAndItems(webFeedIDsAndItems, self.accountID, statusesDictionary) //2 let allIncomingArticles = Article.articlesWithWebFeedIDsAndItems(webFeedIDsAndItems, self.accountID, statusesDictionary) //2
if allIncomingArticles.isEmpty { if allIncomingArticles.isEmpty {
self.callUpdateArticlesCompletionBlock(nil, nil, completion) self.callUpdateArticlesCompletionBlock(nil, nil, nil, completion)
return return
} }
let incomingArticles = self.filterIncomingArticles(allIncomingArticles) //3 let incomingArticles = self.filterIncomingArticles(allIncomingArticles) //3
if incomingArticles.isEmpty { if incomingArticles.isEmpty {
self.callUpdateArticlesCompletionBlock(nil, nil, completion) self.callUpdateArticlesCompletionBlock(nil, nil, nil, completion)
return return
} }
@ -287,7 +293,7 @@ final class ArticlesTable: DatabaseTable {
let newArticles = self.findAndSaveNewArticles(incomingArticles, fetchedArticlesDictionary, database) //5 let newArticles = self.findAndSaveNewArticles(incomingArticles, fetchedArticlesDictionary, database) //5
let updatedArticles = self.findAndSaveUpdatedArticles(incomingArticles, fetchedArticlesDictionary, database) //6 let updatedArticles = self.findAndSaveUpdatedArticles(incomingArticles, fetchedArticlesDictionary, database) //6
self.callUpdateArticlesCompletionBlock(newArticles, updatedArticles, completion) //7 self.callUpdateArticlesCompletionBlock(newArticles, updatedArticles, nil, completion) //7
self.addArticlesToCache(newArticles) self.addArticlesToCache(newArticles)
self.addArticlesToCache(updatedArticles) self.addArticlesToCache(updatedArticles)
@ -849,10 +855,10 @@ private extension ArticlesTable {
// MARK: - Saving Parsed Items // MARK: - Saving Parsed Items
func callUpdateArticlesCompletionBlock(_ newArticles: Set<Article>?, _ updatedArticles: Set<Article>?, _ completion: @escaping UpdateArticlesCompletionBlock) { func callUpdateArticlesCompletionBlock(_ newArticles: Set<Article>?, _ updatedArticles: Set<Article>?, _ deletedArticles: Set<Article>?, _ completion: @escaping UpdateArticlesCompletionBlock) {
let newAndUpdatedArticles = NewAndUpdatedArticles(newArticles: newArticles, updatedArticles: updatedArticles) let articleChanges = ArticleChanges(newArticles: newArticles, updatedArticles: updatedArticles, deletedArticles: deletedArticles)
DispatchQueue.main.async { DispatchQueue.main.async {
completion(.success(newAndUpdatedArticles)) completion(.success(articleChanges))
} }
} }