Retrieve articles when we have a status but don't have an article on full refresh.
This commit is contained in:
parent
0a9bf2aef0
commit
9c159d21f6
|
@ -569,6 +569,10 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
|||
return database.fetchStarredArticleIDs()
|
||||
}
|
||||
|
||||
public func fetchArticleIDsForStatusesWithoutArticles() -> Set<String> {
|
||||
return database.fetchArticleIDsForStatusesWithoutArticles()
|
||||
}
|
||||
|
||||
public func opmlDocument() -> String {
|
||||
let escapedTitle = nameForDisplay.rs_stringByEscapingSpecialXMLCharacters()
|
||||
let openingText =
|
||||
|
|
|
@ -347,6 +347,33 @@ final class FeedbinAPICaller: NSObject {
|
|||
|
||||
}
|
||||
|
||||
func retrieveEntries(articleIDs: [String], completion: @escaping (Result<([FeedbinEntry]?), Error>) -> Void) {
|
||||
|
||||
guard !articleIDs.isEmpty else {
|
||||
completion(.success(([FeedbinEntry]())))
|
||||
return
|
||||
}
|
||||
|
||||
let concatIDs = articleIDs.reduce("") { param, articleID in return param + ",\(articleID)" }
|
||||
let paramIDs = String(concatIDs.dropFirst())
|
||||
|
||||
var callURL = URLComponents(url: feedbinBaseURL.appendingPathComponent("entries.json"), resolvingAgainstBaseURL: false)!
|
||||
callURL.queryItems = [URLQueryItem(name: "ids", value: paramIDs)]
|
||||
let request = URLRequest(url: callURL.url!, credentials: credentials)
|
||||
|
||||
transport.send(request: request, resultType: [FeedbinEntry].self) { [weak self] result in
|
||||
|
||||
switch result {
|
||||
case .success(let (_, entries)):
|
||||
completion(.success((entries)))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func retrieveEntries(feedID: String, completion: @escaping (Result<([FeedbinEntry]?, String?), Error>) -> Void) {
|
||||
|
||||
let since = Calendar.current.date(byAdding: .month, value: -3, to: Date()) ?? Date()
|
||||
|
@ -407,25 +434,6 @@ final class FeedbinAPICaller: NSObject {
|
|||
|
||||
}
|
||||
|
||||
func extractPageNumber(link: String?) -> Int? {
|
||||
|
||||
guard let link = link else {
|
||||
return nil
|
||||
}
|
||||
|
||||
if let lowerBound = link.range(of: "page=")?.upperBound {
|
||||
if let upperBound = link.range(of: "&")?.lowerBound {
|
||||
return Int(link[lowerBound..<upperBound])
|
||||
}
|
||||
if let upperBound = link.range(of: ">")?.lowerBound {
|
||||
return Int(link[lowerBound..<upperBound])
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func retrieveEntries(page: String, completion: @escaping (Result<([FeedbinEntry]?, String?), Error>) -> Void) {
|
||||
|
||||
guard let callURL = URL(string: page) else {
|
||||
|
@ -532,4 +540,23 @@ extension FeedbinAPICaller {
|
|||
}
|
||||
}
|
||||
|
||||
func extractPageNumber(link: String?) -> Int? {
|
||||
|
||||
guard let link = link else {
|
||||
return nil
|
||||
}
|
||||
|
||||
if let lowerBound = link.range(of: "page=")?.upperBound {
|
||||
if let upperBound = link.range(of: "&")?.lowerBound {
|
||||
return Int(link[lowerBound..<upperBound])
|
||||
}
|
||||
if let upperBound = link.range(of: ">")?.lowerBound {
|
||||
return Int(link[lowerBound..<upperBound])
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@ final class FeedbinAccountDelegate: AccountDelegate {
|
|||
|
||||
func refreshAll(for account: Account, completion: (() -> Void)? = nil) {
|
||||
|
||||
refreshProgress.addToNumberOfTasksAndRemaining(5)
|
||||
refreshProgress.addToNumberOfTasksAndRemaining(6)
|
||||
|
||||
refreshAccount(account) { [weak self] result in
|
||||
switch result {
|
||||
|
@ -88,8 +88,12 @@ final class FeedbinAccountDelegate: AccountDelegate {
|
|||
|
||||
self?.refreshArticles(account) {
|
||||
self?.refreshArticleStatus(for: account) {
|
||||
self?.refreshProgress.clear()
|
||||
completion?()
|
||||
self?.refreshMissingArticles(account) {
|
||||
self?.refreshProgress.clear()
|
||||
DispatchQueue.main.async {
|
||||
completion?()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -794,24 +798,32 @@ private extension FeedbinAccountDelegate {
|
|||
return
|
||||
}
|
||||
|
||||
let group = DispatchGroup()
|
||||
|
||||
let articleIDs = statuses.compactMap { Int($0.articleID) }
|
||||
let articleIDGroups = articleIDs.chunked(into: 1000)
|
||||
for articleIDGroup in articleIDGroups {
|
||||
|
||||
group.enter()
|
||||
apiCall(articleIDGroup) { [weak self] result in
|
||||
switch result {
|
||||
case .success:
|
||||
self?.database.deleteSelectedForProcessing(articleIDGroup.map { String($0) } )
|
||||
completion()
|
||||
group.leave()
|
||||
case .failure(let error):
|
||||
guard let self = self else { return }
|
||||
os_log(.error, log: self.log, "Article status sync call failed: %@.", error.localizedDescription)
|
||||
self.database.resetSelectedForProcessing(articleIDGroup.map { String($0) } )
|
||||
completion()
|
||||
group.leave()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
group.notify(queue: DispatchQueue.main) {
|
||||
completion()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func processRestoredFeed(for account: Account, feed: Feed, editedName: String?, folder: Folder?, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
|
@ -991,6 +1003,46 @@ private extension FeedbinAccountDelegate {
|
|||
|
||||
}
|
||||
|
||||
func refreshMissingArticles(_ account: Account, completion: @escaping (() -> Void)) {
|
||||
|
||||
os_log(.debug, log: log, "Refreshing missing articles...")
|
||||
let articleIDs = Array(account.fetchArticleIDsForStatusesWithoutArticles())
|
||||
|
||||
let group = DispatchGroup()
|
||||
|
||||
let chunkedArticleIDs = articleIDs.chunked(into: 100)
|
||||
refreshProgress.addToNumberOfTasks(chunkedArticleIDs.count - 1)
|
||||
|
||||
for chunk in chunkedArticleIDs {
|
||||
|
||||
group.enter()
|
||||
caller.retrieveEntries(articleIDs: chunk) { [weak self] result in
|
||||
|
||||
switch result {
|
||||
case .success(let entries):
|
||||
|
||||
self?.processEntries(account: account, entries: entries) {
|
||||
self?.refreshProgress.completeTask()
|
||||
group.leave()
|
||||
}
|
||||
|
||||
case .failure(let error):
|
||||
guard let self = self else { return }
|
||||
os_log(.error, log: self.log, "Refresh missing articles failed: %@.", error.localizedDescription)
|
||||
group.leave()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
group.notify(queue: DispatchQueue.main) {
|
||||
os_log(.debug, log: self.log, "Done refreshing missing articles.")
|
||||
completion()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func refreshArticles(_ account: Account, page: String?, completion: @escaping (() -> Void)) {
|
||||
|
||||
guard let page = page else {
|
||||
|
|
|
@ -112,6 +112,10 @@ public final class ArticlesDatabase {
|
|||
return articlesTable.fetchStarredArticleIDs()
|
||||
}
|
||||
|
||||
public func fetchArticleIDsForStatusesWithoutArticles() -> Set<String> {
|
||||
return articlesTable.fetchArticleIDsForStatusesWithoutArticles()
|
||||
}
|
||||
|
||||
public func mark(_ articles: Set<Article>, statusKey: ArticleStatus.Key, flag: Bool) -> Set<ArticleStatus>? {
|
||||
return articlesTable.mark(articles, statusKey, flag)
|
||||
}
|
||||
|
|
|
@ -307,6 +307,10 @@ final class ArticlesTable: DatabaseTable {
|
|||
return statusesTable.fetchStarredArticleIDs()
|
||||
}
|
||||
|
||||
func fetchArticleIDsForStatusesWithoutArticles() -> Set<String> {
|
||||
return statusesTable.fetchArticleIDsForStatusesWithoutArticles()
|
||||
}
|
||||
|
||||
func mark(_ articles: Set<Article>, _ statusKey: ArticleStatus.Key, _ flag: Bool) -> Set<ArticleStatus>? {
|
||||
|
||||
return statusesTable.mark(articles.statuses(), statusKey, flag)
|
||||
|
|
|
@ -86,6 +86,10 @@ final class StatusesTable: DatabaseTable {
|
|||
return fetchArticleIDs("select articleID from statuses where starred=1 and userDeleted=0;")
|
||||
}
|
||||
|
||||
func fetchArticleIDsForStatusesWithoutArticles() -> Set<String> {
|
||||
return fetchArticleIDs("select articleID from statuses s where userDeleted=0 and not exists (select 1 from articles a where a.articleID = s.articleID);")
|
||||
}
|
||||
|
||||
func fetchArticleIDs(_ sql: String) -> Set<String> {
|
||||
|
||||
var statuses: Set<String>? = nil
|
||||
|
|
Loading…
Reference in New Issue