Merge branch 'ios-release'
This commit is contained in:
commit
ac45ba57a9
@ -735,7 +735,7 @@ 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(.success(NewAndUpdatedArticles()))
|
completion(.success(ArticleChanges()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1302,30 +1302,39 @@ 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
|
||||||
self.updateUnreadCounts(for: webFeeds)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let updatedArticles = newAndUpdatedArticles.updatedArticles, !updatedArticles.isEmpty {
|
if let updatedArticles = articleChanges.updatedArticles, !updatedArticles.isEmpty {
|
||||||
shouldSendNotification = true
|
shouldSendNotification = true
|
||||||
userInfo[UserInfoKey.updatedArticles] = updatedArticles
|
userInfo[UserInfoKey.updatedArticles] = updatedArticles
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let deletedArticles = articleChanges.deletedArticles, !deletedArticles.isEmpty {
|
||||||
|
shouldUpdateUnreadCounts = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if shouldUpdateUnreadCounts {
|
||||||
|
self.updateUnreadCounts(for: webFeeds)
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
|
@ -627,8 +627,8 @@ private extension CloudKitAccountDelegate {
|
|||||||
|
|
||||||
extension CloudKitAccountDelegate: LocalAccountRefresherDelegate {
|
extension CloudKitAccountDelegate: LocalAccountRefresherDelegate {
|
||||||
|
|
||||||
func localAccountRefresher(_ refresher: LocalAccountRefresher, didProcess newAndUpdatedArticles: NewAndUpdatedArticles, completion: @escaping () -> Void) {
|
func localAccountRefresher(_ refresher: LocalAccountRefresher, didProcess articleChanges: ArticleChanges, completion: @escaping () -> Void) {
|
||||||
if let newArticles = newAndUpdatedArticles.newArticles {
|
if let newArticles = articleChanges.newArticles {
|
||||||
articlesZone.sendNewArticles(newArticles) { _ in
|
articlesZone.sendNewArticles(newArticles) { _ in
|
||||||
completion()
|
completion()
|
||||||
}
|
}
|
||||||
|
@ -202,7 +202,7 @@ private extension CloudKitArticlesZoneDelegate {
|
|||||||
|
|
||||||
extension CloudKitArticlesZoneDelegate: LocalAccountRefresherDelegate {
|
extension CloudKitArticlesZoneDelegate: LocalAccountRefresherDelegate {
|
||||||
|
|
||||||
func localAccountRefresher(_ refresher: LocalAccountRefresher, didProcess newAndUpdatedArticles: NewAndUpdatedArticles, completion: @escaping () -> Void) {
|
func localAccountRefresher(_ refresher: LocalAccountRefresher, didProcess articleChanges: ArticleChanges, completion: @escaping () -> Void) {
|
||||||
completion()
|
completion()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,7 +234,7 @@ final class LocalAccountDelegate: AccountDelegate {
|
|||||||
|
|
||||||
extension LocalAccountDelegate: LocalAccountRefresherDelegate {
|
extension LocalAccountDelegate: LocalAccountRefresherDelegate {
|
||||||
|
|
||||||
func localAccountRefresher(_ refresher: LocalAccountRefresher, didProcess newAndUpdatedArticles: NewAndUpdatedArticles, completion: @escaping () -> Void) {
|
func localAccountRefresher(_ refresher: LocalAccountRefresher, didProcess articleChanges: ArticleChanges, completion: @escaping () -> Void) {
|
||||||
completion()
|
completion()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ import Articles
|
|||||||
import ArticlesDatabase
|
import ArticlesDatabase
|
||||||
|
|
||||||
protocol LocalAccountRefresherDelegate {
|
protocol LocalAccountRefresherDelegate {
|
||||||
func localAccountRefresher(_ refresher: LocalAccountRefresher, didProcess: NewAndUpdatedArticles, completion: @escaping () -> Void)
|
func localAccountRefresher(_ refresher: LocalAccountRefresher, didProcess: ArticleChanges, completion: @escaping () -> Void)
|
||||||
func localAccountRefresher(_ refresher: LocalAccountRefresher, requestCompletedFor: WebFeed)
|
func localAccountRefresher(_ refresher: LocalAccountRefresher, requestCompletedFor: WebFeed)
|
||||||
func localAccountRefresherDidFinish(_ refresher: LocalAccountRefresher)
|
func localAccountRefresherDidFinish(_ refresher: LocalAccountRefresher)
|
||||||
}
|
}
|
||||||
|
@ -24,22 +24,26 @@ 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 init() {
|
public init() {
|
||||||
self.newArticles = Set<Article>()
|
self.newArticles = Set<Article>()
|
||||||
self.updatedArticles = Set<Article>()
|
self.updatedArticles = Set<Article>()
|
||||||
|
self.deletedArticles = Set<Article>()
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(newArticles: Set<Article>?, updatedArticles: Set<Article>?) {
|
public init(newArticles: Set<Article>?, updatedArticles: Set<Article>?, deletedArticles: Set<Article>?) {
|
||||||
self.newArticles = newArticles
|
self.newArticles = newArticles
|
||||||
self.updatedArticles = updatedArticles
|
self.updatedArticles = updatedArticles
|
||||||
|
self.deletedArticles = deletedArticles
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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>
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,10 +28,6 @@ struct HTMLMetadataDownloader {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if let error = error {
|
|
||||||
appDelegate.logMessage("Error downloading metadata at \(url): \(error)", type: .warning)
|
|
||||||
}
|
|
||||||
|
|
||||||
completion(nil)
|
completion(nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
// High Level Settings common to both the iOS application and any extensions we bundle with it
|
// High Level Settings common to both the iOS application and any extensions we bundle with it
|
||||||
MARKETING_VERSION = 5.0.1
|
MARKETING_VERSION = 5.0.1
|
||||||
CURRENT_PROJECT_VERSION = 40
|
CURRENT_PROJECT_VERSION = 41
|
||||||
|
|
||||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon
|
||||||
|
Loading…
x
Reference in New Issue
Block a user