Merge branch 'master' of https://github.com/brentsimmons/NetNewsWire
This commit is contained in:
commit
56cdac6519
|
@ -1115,7 +1115,7 @@ private extension FeedbinAccountDelegate {
|
|||
|
||||
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)])
|
||||
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)
|
||||
|
|
|
@ -10,7 +10,7 @@ import Foundation
|
|||
import RSParser
|
||||
import RSCore
|
||||
|
||||
struct FeedbinEntry: Codable {
|
||||
final class FeedbinEntry: Codable {
|
||||
|
||||
let articleID: Int
|
||||
let feedID: Int
|
||||
|
@ -23,6 +23,19 @@ struct FeedbinEntry: Codable {
|
|||
let dateArrived: String?
|
||||
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 {
|
||||
case articleID = "id"
|
||||
case feedID = "feed_id"
|
||||
|
@ -35,19 +48,6 @@ struct FeedbinEntry: Codable {
|
|||
case dateArrived = "created_at"
|
||||
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 {
|
||||
|
|
|
@ -20,6 +20,7 @@ final class ArticlesTable: DatabaseTable {
|
|||
private let statusesTable: StatusesTable
|
||||
private let authorsLookupTable: DatabaseLookupTable
|
||||
private let attachmentsLookupTable: DatabaseLookupTable
|
||||
private var databaseArticlesCache = [String: DatabaseArticle]()
|
||||
|
||||
private lazy var searchTable: SearchTable = {
|
||||
return SearchTable(queue: queue, articlesTable: self)
|
||||
|
@ -482,16 +483,21 @@ private extension ArticlesTable {
|
|||
func makeDatabaseArticles(with resultSet: FMResultSet) -> Set<DatabaseArticle> {
|
||||
let articles = resultSet.mapToSet { (row) -> DatabaseArticle? in
|
||||
|
||||
// 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) else {
|
||||
assertionFailure("Expected status.")
|
||||
guard let articleID = row.string(forColumn: DatabaseKey.articleID) else {
|
||||
assertionFailure("Expected articleID.")
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let articleID = row.string(forColumn: DatabaseKey.articleID) else {
|
||||
assertionFailure("Expected articleID.")
|
||||
// Articles are removed from the cache when they’re updated.
|
||||
// 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
|
||||
}
|
||||
guard let feedID = row.string(forColumn: DatabaseKey.feedID) else {
|
||||
|
@ -514,7 +520,9 @@ private extension ArticlesTable {
|
|||
let datePublished = row.date(forColumn: DatabaseKey.datePublished)
|
||||
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
|
||||
|
@ -670,6 +678,7 @@ private extension ArticlesTable {
|
|||
|
||||
|
||||
func saveUpdatedArticles(_ updatedArticles: Set<Article>, _ fetchedArticles: [String: Article], _ database: FMDatabase) {
|
||||
removeArticlesFromDatabaseArticlesCache(updatedArticles)
|
||||
saveUpdatedRelatedObjects(updatedArticles, fetchedArticles, database)
|
||||
|
||||
for updatedArticle in updatedArticles {
|
||||
|
@ -690,10 +699,17 @@ private extension ArticlesTable {
|
|||
// Not unexpected. There may be no changes.
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
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 {
|
||||
// Ignorable articles: either userDeleted==1 or (not starred and arrival date > 4 months).
|
||||
if status.userDeleted {
|
||||
|
|
|
@ -105,17 +105,21 @@ final class StatusesTable: DatabaseTable {
|
|||
guard let articleID = row.string(forColumn: DatabaseKey.articleID) else {
|
||||
return nil
|
||||
}
|
||||
return statusWithRow(row, articleID: articleID)
|
||||
}
|
||||
|
||||
func statusWithRow(_ row: FMResultSet, articleID: String) ->ArticleStatus? {
|
||||
if let cachedStatus = cache[articleID] {
|
||||
return cachedStatus
|
||||
}
|
||||
|
||||
|
||||
guard let dateArrived = row.date(forColumn: DatabaseKey.dateArrived) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let articleStatus = ArticleStatus(articleID: articleID, dateArrived: dateArrived, row: row)
|
||||
cache.addStatusIfNotCached(articleStatus)
|
||||
|
||||
|
||||
return articleStatus
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ Here’s [How to Support NetNewsWire](Technotes/HowToSupportNetNewsWire.markdown
|
|||
|
||||
#### Community
|
||||
|
||||
[Join the Slack group](https://join.slack.com/t/netnewswire/shared_invite/enQtNjM4MDA1MjQzMDkzLTNlNjBhOWVhYzdhYjA4ZWFhMzQ1MTUxYjU0NTE5ZGY0YzYwZWJhNjYwNTNmNTg2NjIwYWY4YzhlYzk5NmU3ZTc) to talk with other NetNewsWire users — and to help out, if you’d like to, by testing, coding, writing, providing feedback, or just helping us think things through. Everybody is welcome and encouraged to join.
|
||||
[Join the Slack group](https://ranchero.com/netnewswire/slack) to talk with other NetNewsWire users — and to help out, if you’d like to, by testing, coding, writing, providing feedback, or just helping us think things through. Everybody is welcome and encouraged to join.
|
||||
|
||||
Every community member is expected to abide by the code of conduct which is included in the [Contributing](CONTRIBUTING.md) page.
|
||||
|
||||
|
|
Loading…
Reference in New Issue