Merge branch 'mac-release'

This commit is contained in:
Brent Simmons 2019-09-28 13:52:07 -07:00
commit 13eb6450c7
4 changed files with 47 additions and 27 deletions

View File

@ -1115,7 +1115,7 @@ private extension FeedbinAccountDelegate {
let parsedItems: [ParsedItem] = entries.map { entry in let parsedItems: [ParsedItem] = entries.map { entry in
let authors = Set([ParsedAuthor(name: entry.authorName, url: entry.jsonFeed?.jsonFeedAuthor?.url, avatarURL: entry.jsonFeed?.jsonFeedAuthor?.avatarURL, emailAddress: nil)]) let authors = Set([ParsedAuthor(name: entry.authorName, url: entry.jsonFeed?.jsonFeedAuthor?.url, avatarURL: entry.jsonFeed?.jsonFeedAuthor?.avatarURL, emailAddress: nil)])
return ParsedItem(syncServiceID: String(entry.articleID), uniqueID: String(entry.articleID), feedURL: String(entry.feedID), url: nil, externalURL: entry.url, title: entry.title, contentHTML: entry.contentHTML, contentText: nil, summary: entry.summary, imageURL: nil, bannerImageURL: nil, datePublished: entry.parseDatePublished(), dateModified: nil, authors: authors, tags: nil, attachments: nil) return ParsedItem(syncServiceID: String(entry.articleID), uniqueID: String(entry.articleID), feedURL: String(entry.feedID), url: nil, externalURL: entry.url, title: entry.title, contentHTML: entry.contentHTML, contentText: nil, summary: entry.summary, imageURL: nil, bannerImageURL: nil, datePublished: entry.parsedDatePublished, dateModified: nil, authors: authors, tags: nil, attachments: nil)
} }
return Set(parsedItems) return Set(parsedItems)

View File

@ -10,7 +10,7 @@ import Foundation
import RSParser import RSParser
import RSCore import RSCore
struct FeedbinEntry: Codable { final class FeedbinEntry: Codable {
let articleID: Int let articleID: Int
let feedID: Int let feedID: Int
@ -23,6 +23,19 @@ struct FeedbinEntry: Codable {
let dateArrived: String? let dateArrived: String?
let jsonFeed: FeedbinEntryJSONFeed? let jsonFeed: FeedbinEntryJSONFeed?
// Feedbin dates can't be decoded by the JSONDecoding 8601 decoding strategy. Feedbin
// requires a very specific date formatter to work and even then it fails occasionally.
// Rather than loose all the entries we only lose the one date by decoding as a string
// and letting the one date fail when parsed.
lazy var parsedDatePublished: Date? = {
if let datePublished = datePublished {
return RSDateWithString(datePublished)
}
else {
return nil
}
}()
enum CodingKeys: String, CodingKey { enum CodingKeys: String, CodingKey {
case articleID = "id" case articleID = "id"
case feedID = "feed_id" case feedID = "feed_id"
@ -35,19 +48,6 @@ struct FeedbinEntry: Codable {
case dateArrived = "created_at" case dateArrived = "created_at"
case jsonFeed = "json_feed" case jsonFeed = "json_feed"
} }
// Feedbin dates can't be decoded by the JSONDecoding 8601 decoding strategy. Feedbin
// requires a very specific date formatter to work and even then it fails occasionally.
// Rather than loose all the entries we only lose the one date by decoding as a string
// and letting the one date fail when parsed.
func parseDatePublished() -> Date? {
if datePublished != nil {
return FeedbinDate.formatter.date(from: datePublished!)
} else {
return nil
}
}
} }
struct FeedbinEntryJSONFeed: Codable { struct FeedbinEntryJSONFeed: Codable {

View File

@ -20,6 +20,7 @@ final class ArticlesTable: DatabaseTable {
private let statusesTable: StatusesTable private let statusesTable: StatusesTable
private let authorsLookupTable: DatabaseLookupTable private let authorsLookupTable: DatabaseLookupTable
private let attachmentsLookupTable: DatabaseLookupTable private let attachmentsLookupTable: DatabaseLookupTable
private var databaseArticlesCache = [String: DatabaseArticle]()
private lazy var searchTable: SearchTable = { private lazy var searchTable: SearchTable = {
return SearchTable(queue: queue, articlesTable: self) return SearchTable(queue: queue, articlesTable: self)
@ -482,16 +483,21 @@ private extension ArticlesTable {
func makeDatabaseArticles(with resultSet: FMResultSet) -> Set<DatabaseArticle> { func makeDatabaseArticles(with resultSet: FMResultSet) -> Set<DatabaseArticle> {
let articles = resultSet.mapToSet { (row) -> DatabaseArticle? in let articles = resultSet.mapToSet { (row) -> DatabaseArticle? in
// The resultSet is a result of a JOIN query with the statuses table, guard let articleID = row.string(forColumn: DatabaseKey.articleID) else {
// so we can get the statuses at the same time and avoid additional database lookups. assertionFailure("Expected articleID.")
guard let status = statusesTable.statusWithRow(resultSet) else {
assertionFailure("Expected status.")
return nil return nil
} }
guard let articleID = row.string(forColumn: DatabaseKey.articleID) else { // Articles are removed from the cache when theyre updated.
assertionFailure("Expected articleID.") // See saveUpdatedArticles.
if let databaseArticle = databaseArticlesCache[articleID] {
return databaseArticle
}
// The resultSet is a result of a JOIN query with the statuses table,
// so we can get the statuses at the same time and avoid additional database lookups.
guard let status = statusesTable.statusWithRow(resultSet, articleID: articleID) else {
assertionFailure("Expected status.")
return nil return nil
} }
guard let feedID = row.string(forColumn: DatabaseKey.feedID) else { guard let feedID = row.string(forColumn: DatabaseKey.feedID) else {
@ -514,7 +520,9 @@ private extension ArticlesTable {
let datePublished = row.date(forColumn: DatabaseKey.datePublished) let datePublished = row.date(forColumn: DatabaseKey.datePublished)
let dateModified = row.date(forColumn: DatabaseKey.dateModified) let dateModified = row.date(forColumn: DatabaseKey.dateModified)
return DatabaseArticle(articleID: articleID, feedID: feedID, uniqueID: uniqueID, title: title, contentHTML: contentHTML, contentText: contentText, url: url, externalURL: externalURL, summary: summary, imageURL: imageURL, bannerImageURL: bannerImageURL, datePublished: datePublished, dateModified: dateModified, status: status) let databaseArticle = DatabaseArticle(articleID: articleID, feedID: feedID, uniqueID: uniqueID, title: title, contentHTML: contentHTML, contentText: contentText, url: url, externalURL: externalURL, summary: summary, imageURL: imageURL, bannerImageURL: bannerImageURL, datePublished: datePublished, dateModified: dateModified, status: status)
databaseArticlesCache[articleID] = databaseArticle
return databaseArticle
} }
return articles return articles
@ -670,6 +678,7 @@ private extension ArticlesTable {
func saveUpdatedArticles(_ updatedArticles: Set<Article>, _ fetchedArticles: [String: Article], _ database: FMDatabase) { func saveUpdatedArticles(_ updatedArticles: Set<Article>, _ fetchedArticles: [String: Article], _ database: FMDatabase) {
removeArticlesFromDatabaseArticlesCache(updatedArticles)
saveUpdatedRelatedObjects(updatedArticles, fetchedArticles, database) saveUpdatedRelatedObjects(updatedArticles, fetchedArticles, database)
for updatedArticle in updatedArticles { for updatedArticle in updatedArticles {
@ -694,6 +703,13 @@ private extension ArticlesTable {
updateRowsWithDictionary(changesDictionary, whereKey: DatabaseKey.articleID, matches: updatedArticle.articleID, database: database) updateRowsWithDictionary(changesDictionary, whereKey: DatabaseKey.articleID, matches: updatedArticle.articleID, database: database)
} }
func removeArticlesFromDatabaseArticlesCache(_ updatedArticles: Set<Article>) {
let articleIDs = updatedArticles.articleIDs()
for articleID in articleIDs {
databaseArticlesCache[articleID] = nil
}
}
func statusIndicatesArticleIsIgnorable(_ status: ArticleStatus) -> Bool { func statusIndicatesArticleIsIgnorable(_ status: ArticleStatus) -> Bool {
// Ignorable articles: either userDeleted==1 or (not starred and arrival date > 4 months). // Ignorable articles: either userDeleted==1 or (not starred and arrival date > 4 months).
if status.userDeleted { if status.userDeleted {

View File

@ -105,6 +105,10 @@ final class StatusesTable: DatabaseTable {
guard let articleID = row.string(forColumn: DatabaseKey.articleID) else { guard let articleID = row.string(forColumn: DatabaseKey.articleID) else {
return nil return nil
} }
return statusWithRow(row, articleID: articleID)
}
func statusWithRow(_ row: FMResultSet, articleID: String) ->ArticleStatus? {
if let cachedStatus = cache[articleID] { if let cachedStatus = cache[articleID] {
return cachedStatus return cachedStatus
} }