2017-07-03 20:20:14 +02:00
|
|
|
//
|
2017-07-29 21:08:10 +02:00
|
|
|
// ArticlesTable.swift
|
2017-07-03 20:20:14 +02:00
|
|
|
// Evergreen
|
|
|
|
//
|
|
|
|
// Created by Brent Simmons on 5/9/16.
|
|
|
|
// Copyright © 2016 Ranchero Software, LLC. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
import Foundation
|
2017-08-23 22:23:12 +02:00
|
|
|
import RSCore
|
2017-07-29 21:29:05 +02:00
|
|
|
import RSDatabase
|
2017-08-23 22:23:12 +02:00
|
|
|
import RSParser
|
2017-07-03 20:20:14 +02:00
|
|
|
import Data
|
|
|
|
|
2017-08-21 22:31:14 +02:00
|
|
|
final class ArticlesTable: DatabaseTable {
|
|
|
|
|
|
|
|
let name: String
|
|
|
|
let databaseIDKey = DatabaseKey.articleID
|
2017-08-23 22:23:12 +02:00
|
|
|
private let statusesTable: StatusesTable
|
|
|
|
private let authorsLookupTable: DatabaseLookupTable
|
|
|
|
private let attachmentsLookupTable: DatabaseLookupTable
|
|
|
|
private let tagsLookupTable: DatabaseLookupTable
|
2017-08-24 06:30:28 +02:00
|
|
|
private let articleCache = ArticleCache()
|
|
|
|
|
2017-08-21 22:31:14 +02:00
|
|
|
init(name: String) {
|
2017-07-29 21:29:05 +02:00
|
|
|
|
2017-08-21 22:31:14 +02:00
|
|
|
self.name = name
|
2017-08-23 22:23:12 +02:00
|
|
|
|
|
|
|
self.statusesTable = StatusesTable(name: DatabaseTableName.statuses)
|
|
|
|
let authorsTable = AuthorsTable(name: DatabaseTableName.authors)
|
|
|
|
self.authorsLookupTable = DatabaseLookupTable(name: DatabaseTableName.authorsLookup, objectIDKey: DatabaseKey.articleID, relatedObjectIDKey: DatabaseKey.authorID, relatedTable: authorsTable, relationshipName: RelationshipName.authors)
|
|
|
|
|
|
|
|
let tagsTable = TagsTable(name: DatabaseTableName.tags)
|
|
|
|
self.tagsLookupTable = DatabaseLookupTable(name: DatabaseTableName.tags, objectIDKey: DatabaseKey.articleID, relatedObjectIDKey: DatabaseKey.tagName, relatedTable: tagsTable, relationshipName: RelationshipName.tags)
|
|
|
|
|
|
|
|
let attachmentsTable = AttachmentsTable(name: DatabaseTableName.attachments)
|
|
|
|
self.attachmentsLookupTable = DatabaseLookupTable(name: DatabaseTableName.attachmentsLookup, objectIDKey: DatabaseKey.articleID, relatedObjectIDKey: DatabaseKey.attachmentID, relatedTable: attachmentsTable, relationshipName: RelationshipName.attachments)
|
2017-08-21 22:31:14 +02:00
|
|
|
}
|
2017-07-29 21:29:05 +02:00
|
|
|
|
2017-08-21 22:31:14 +02:00
|
|
|
// MARK: DatabaseTable Methods
|
|
|
|
|
|
|
|
func objectWithRow(_ row: FMResultSet) -> DatabaseObject? {
|
|
|
|
|
|
|
|
// if let article = articleWithRow(row) {
|
|
|
|
//
|
|
|
|
// }
|
|
|
|
return nil // TODO
|
|
|
|
}
|
|
|
|
|
|
|
|
func save(_ objects: [DatabaseObject], in database: FMDatabase) {
|
2017-08-23 22:23:12 +02:00
|
|
|
|
2017-08-21 22:31:14 +02:00
|
|
|
// TODO
|
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: Fetching
|
|
|
|
|
2017-08-23 22:23:12 +02:00
|
|
|
func fetchArticles(_ feed: Feed) -> Set<Article> {
|
2017-08-21 22:31:14 +02:00
|
|
|
|
|
|
|
return Set<Article>() // TODO
|
|
|
|
}
|
|
|
|
|
2017-08-23 22:23:12 +02:00
|
|
|
func fetchArticlesAsync(_ feed: Feed, _ resultBlock: @escaping ArticleResultBlock) {
|
2017-08-21 22:31:14 +02:00
|
|
|
|
|
|
|
// TODO
|
|
|
|
}
|
|
|
|
|
2017-08-23 22:23:12 +02:00
|
|
|
func fetchUnreadArticles(_ feeds: Set<Feed>) -> Set<Article> {
|
2017-08-21 22:31:14 +02:00
|
|
|
|
|
|
|
return Set<Article>() // TODO
|
|
|
|
}
|
|
|
|
|
2017-08-23 22:23:12 +02:00
|
|
|
// MARK: Updating
|
|
|
|
|
|
|
|
func update(_ feed: Feed, _ parsedFeed: ParsedFeed, _ completion: @escaping RSVoidCompletionBlock) {
|
|
|
|
|
|
|
|
// TODO
|
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: Unread Counts
|
|
|
|
|
|
|
|
func fetchUnreadCounts(_ feeds: Set<Feed>, _ completion: @escaping UnreadCountCompletionBlock) {
|
|
|
|
|
|
|
|
// TODO
|
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: Status
|
|
|
|
|
|
|
|
func mark(_ articles: Set<Article>, _ statusKey: String, _ flag: Bool) {
|
|
|
|
|
|
|
|
// TODO
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-08-21 07:43:46 +02:00
|
|
|
// func uniquedArticles(_ fetchedArticles: Set<Article>, statusesTable: StatusesTable) -> Set<Article> {
|
|
|
|
//
|
|
|
|
// var articles = Set<Article>()
|
|
|
|
//
|
|
|
|
// for oneArticle in fetchedArticles {
|
|
|
|
//
|
|
|
|
// assert(oneArticle.status != nil)
|
|
|
|
//
|
|
|
|
// if let existingArticle = cachedArticle(oneArticle.databaseID) {
|
|
|
|
// articles.insert(existingArticle)
|
|
|
|
// }
|
|
|
|
// else {
|
|
|
|
// cacheArticle(oneArticle)
|
|
|
|
// articles.insert(oneArticle)
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// statusesTable.attachCachedStatuses(articles)
|
|
|
|
//
|
|
|
|
// return articles
|
|
|
|
// }
|
2017-07-29 21:29:05 +02:00
|
|
|
|
2017-08-21 07:43:46 +02:00
|
|
|
// typealias FeedCountCallback = (Int) -> Void
|
|
|
|
//
|
|
|
|
// func numberOfArticlesWithFeedID(_ feedID: String, callback: @escaping FeedCountCallback) {
|
|
|
|
//
|
|
|
|
// queue.fetch { (database: FMDatabase!)
|
|
|
|
//
|
|
|
|
// let sql = "select count(*) from articles where feedID = ?;"
|
|
|
|
// var numberOfArticles = -1
|
|
|
|
//
|
|
|
|
// if let resultSet = database.executeQuery(sql, withArgumentsIn: [feedID]) {
|
|
|
|
//
|
|
|
|
// while (resultSet.next()) {
|
|
|
|
// numberOfArticles = resultSet.long(forColumnIndex: 0)
|
|
|
|
// break
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// DispatchQueue.main.async() {
|
|
|
|
// callback(numberOfArticles)
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// }
|
2017-08-21 22:31:14 +02:00
|
|
|
}
|
2017-07-29 21:29:05 +02:00
|
|
|
|
2017-08-21 07:43:46 +02:00
|
|
|
//private extension ArticlesTable {
|
2017-07-29 21:29:05 +02:00
|
|
|
|
2017-08-21 07:43:46 +02:00
|
|
|
// func cachedArticle(_ articleID: String) -> Article? {
|
|
|
|
//
|
|
|
|
// return cachedArticles.object(forKey: articleID as NSString)
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// func cacheArticle(_ article: Article) {
|
|
|
|
//
|
|
|
|
// cachedArticles.setObject(article, forKey: article.databaseID as NSString)
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// func cacheArticles(_ articles: Set<Article>) {
|
|
|
|
//
|
|
|
|
// articles.forEach { cacheArticle($0) }
|
|
|
|
// }
|
|
|
|
//}
|
2017-07-29 21:29:05 +02:00
|
|
|
|
2017-08-24 06:30:28 +02:00
|
|
|
private struct ArticleCache {
|
|
|
|
|
|
|
|
// Main thread only.
|
|
|
|
// The cache contains a given article only until all outside references are gone.
|
|
|
|
// Cache key is articleID.
|
|
|
|
|
|
|
|
private let articlesMapTable: NSMapTable<NSString, Article> = NSMapTable.weakToWeakObjects()
|
|
|
|
|
|
|
|
func uniquedArticles(_ articles: Set<Article>) -> Set<Article> {
|
|
|
|
|
|
|
|
var articlesToReturn = Set<Article>()
|
|
|
|
|
|
|
|
for article in articles {
|
|
|
|
let articleID = article.articleID
|
|
|
|
if let cachedArticle = cachedArticle(for: articleID) {
|
|
|
|
articlesToReturn.insert(cachedArticle)
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
articlesToReturn.insert(article)
|
|
|
|
addToCache(article)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return articlesToReturn
|
|
|
|
}
|
|
|
|
|
|
|
|
private func cachedArticle(for articleID: String) -> Article? {
|
|
|
|
|
|
|
|
return articlesMapTable.object(forKey: articleID as NSString)
|
|
|
|
}
|
|
|
|
|
|
|
|
private func addToCache(_ article: Article) {
|
|
|
|
|
|
|
|
articlesMapTable.setObject(article, forKey: article.articleID as NSString)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|