Add unread story hashes query
This commit is contained in:
parent
9727219b09
commit
d37f70d2dd
|
@ -7,7 +7,9 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
179DB02FFBC17AC9798F0EBC /* NewsBlurArticle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179DB7399814F6FB3247825C /* NewsBlurArticle.swift */; };
|
||||
179DB28CF49F73A945EBF5DB /* NewsBlurLoginResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179DB088236E3236010462E8 /* NewsBlurLoginResponse.swift */; };
|
||||
179DB49A960F8B78C4924458 /* NewsBlurGenericCodingKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179DB66D933E976C29159DEE /* NewsBlurGenericCodingKeys.swift */; };
|
||||
179DBF4DE2562D4C532F6008 /* NewsBlurSubscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179DB1B909672E0E807B5E8C /* NewsBlurSubscription.swift */; };
|
||||
3B3A33E7238D3D6800314204 /* Secrets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B3A33E6238D3D6800314204 /* Secrets.swift */; };
|
||||
3B826DA72385C81C00FC1ADB /* FeedWranglerAuthorizationResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B826D9E2385C81C00FC1ADB /* FeedWranglerAuthorizationResult.swift */; };
|
||||
|
@ -226,6 +228,8 @@
|
|||
/* Begin PBXFileReference section */
|
||||
179DB088236E3236010462E8 /* NewsBlurLoginResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewsBlurLoginResponse.swift; sourceTree = "<group>"; };
|
||||
179DB1B909672E0E807B5E8C /* NewsBlurSubscription.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewsBlurSubscription.swift; sourceTree = "<group>"; };
|
||||
179DB66D933E976C29159DEE /* NewsBlurGenericCodingKeys.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewsBlurGenericCodingKeys.swift; sourceTree = "<group>"; };
|
||||
179DB7399814F6FB3247825C /* NewsBlurArticle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewsBlurArticle.swift; sourceTree = "<group>"; };
|
||||
3B3A33E6238D3D6800314204 /* Secrets.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Secrets.swift; path = ../../Shared/Secrets.swift; sourceTree = "<group>"; };
|
||||
3B826D9E2385C81C00FC1ADB /* FeedWranglerAuthorizationResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedWranglerAuthorizationResult.swift; sourceTree = "<group>"; };
|
||||
3B826D9F2385C81C00FC1ADB /* FeedWranglerFeedItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedWranglerFeedItem.swift; sourceTree = "<group>"; };
|
||||
|
@ -448,6 +452,8 @@
|
|||
children = (
|
||||
179DB088236E3236010462E8 /* NewsBlurLoginResponse.swift */,
|
||||
179DB1B909672E0E807B5E8C /* NewsBlurSubscription.swift */,
|
||||
179DB7399814F6FB3247825C /* NewsBlurArticle.swift */,
|
||||
179DB66D933E976C29159DEE /* NewsBlurGenericCodingKeys.swift */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1139,6 +1145,8 @@
|
|||
769F2BA02EF5F329CDE45F5A /* NewsBlurAPICaller.swift in Sources */,
|
||||
179DB28CF49F73A945EBF5DB /* NewsBlurLoginResponse.swift in Sources */,
|
||||
179DBF4DE2562D4C532F6008 /* NewsBlurSubscription.swift in Sources */,
|
||||
179DB02FFBC17AC9798F0EBC /* NewsBlurArticle.swift in Sources */,
|
||||
179DB49A960F8B78C4924458 /* NewsBlurGenericCodingKeys.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
//
|
||||
// NewsBlurArticle.swift
|
||||
// Account
|
||||
//
|
||||
// Created by Anh Quang Do on 2020-03-10.
|
||||
// Copyright (c) 2020 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RSCore
|
||||
import RSParser
|
||||
|
||||
typealias NewsBlurArticleHash = NewsBlurUnreadArticleHashesResponse.ArticleHash
|
||||
|
||||
struct NewsBlurUnreadArticleHashesResponse: Decodable {
|
||||
let subscriptions: [String: [ArticleHash]]
|
||||
|
||||
struct ArticleHash: Hashable, Codable {
|
||||
var hash: String
|
||||
var timestamp: Date
|
||||
}
|
||||
}
|
||||
|
||||
extension NewsBlurUnreadArticleHashesResponse {
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case feeds = "unread_feed_story_hashes"
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
// Parse subscriptions
|
||||
var subscriptions: [String: [ArticleHash]] = [:]
|
||||
let subscriptionContainer = try container.nestedContainer(keyedBy: NewsBlurGenericCodingKeys.self, forKey: .feeds)
|
||||
try subscriptionContainer.allKeys.forEach { key in
|
||||
subscriptions[key.stringValue] = []
|
||||
var hashArrayContainer = try subscriptionContainer.nestedUnkeyedContainer(forKey: key)
|
||||
while !hashArrayContainer.isAtEnd {
|
||||
var hashContainer = try hashArrayContainer.nestedUnkeyedContainer()
|
||||
let hash = try hashContainer.decode(String.self)
|
||||
let timestamp = try hashContainer.decode(Date.self)
|
||||
let articleHash = ArticleHash(hash: hash, timestamp: timestamp)
|
||||
|
||||
subscriptions[key.stringValue]?.append(articleHash)
|
||||
}
|
||||
}
|
||||
|
||||
self.subscriptions = subscriptions
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// NewsBlurGenericCodingKeys.swift
|
||||
// Account
|
||||
//
|
||||
// Created by Anh Quang Do on 2020-03-10.
|
||||
// Copyright (c) 2020 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct NewsBlurGenericCodingKeys: CodingKey {
|
||||
var stringValue: String
|
||||
|
||||
init?(stringValue: String) {
|
||||
self.stringValue = stringValue
|
||||
}
|
||||
|
||||
var intValue: Int? {
|
||||
return nil
|
||||
}
|
||||
|
||||
init?(intValue: Int) {
|
||||
return nil
|
||||
}
|
||||
}
|
|
@ -42,7 +42,7 @@ extension NewsBlurFeedsResponse {
|
|||
|
||||
// Parse subscriptions
|
||||
var subscriptions: [Subscription] = []
|
||||
let subscriptionContainer = try container.nestedContainer(keyedBy: GenericCodingKeys.self, forKey: .feeds)
|
||||
let subscriptionContainer = try container.nestedContainer(keyedBy: NewsBlurGenericCodingKeys.self, forKey: .feeds)
|
||||
try subscriptionContainer.allKeys.forEach { key in
|
||||
let subscription = try subscriptionContainer.decode(Subscription.self, forKey: key)
|
||||
subscriptions.append(subscription)
|
||||
|
@ -50,7 +50,7 @@ extension NewsBlurFeedsResponse {
|
|||
|
||||
// Parse folders
|
||||
var folders: [Folder] = []
|
||||
let folderContainer = try container.nestedContainer(keyedBy: GenericCodingKeys.self, forKey: .folders)
|
||||
let folderContainer = try container.nestedContainer(keyedBy: NewsBlurGenericCodingKeys.self, forKey: .folders)
|
||||
try folderContainer.allKeys.forEach { key in
|
||||
let subscriptionIds = try folderContainer.decode([Int].self, forKey: key)
|
||||
let folder = Folder(name: key.stringValue, subscriptionIds: subscriptionIds)
|
||||
|
@ -72,19 +72,3 @@ extension NewsBlurFeedsResponse.Subscription {
|
|||
case favicon = "favicon_url"
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate struct GenericCodingKeys: CodingKey {
|
||||
var stringValue: String
|
||||
|
||||
init?(stringValue: String) {
|
||||
self.stringValue = stringValue
|
||||
}
|
||||
|
||||
var intValue: Int? {
|
||||
return nil
|
||||
}
|
||||
|
||||
init?(intValue: Int) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ final class NewsBlurAPICaller: NSObject {
|
|||
}
|
||||
}
|
||||
|
||||
func retrieveSubscriptions(completion: @escaping (Result<[NewsBlurSubscription]?, Error>) -> Void) {
|
||||
func retrieveSubscriptions(completion: @escaping (Result<[NewsBlurSubscription], Error>) -> Void) {
|
||||
let url = baseURL
|
||||
.appendingPathComponent("reader/feeds")
|
||||
.appendingQueryItems([
|
||||
|
@ -100,8 +100,35 @@ final class NewsBlurAPICaller: NSObject {
|
|||
let request = URLRequest(url: callURL, credentials: credentials)
|
||||
transport.send(request: request, resultType: NewsBlurFeedsResponse.self) { result in
|
||||
switch result {
|
||||
case .success(let (response, payload)):
|
||||
print(payload)
|
||||
case .success((_, let payload)):
|
||||
completion(.success(payload?.subscriptions ?? []))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func retrieveUnreadArticleHashes(completion: @escaping (Result<[NewsBlurArticleHash], Error>) -> Void) {
|
||||
let url = baseURL
|
||||
.appendingPathComponent("reader/unread_story_hashes")
|
||||
.appendingQueryItems([
|
||||
URLQueryItem(name: "include_timestamps", value: "true"),
|
||||
])
|
||||
|
||||
guard let callURL = url else {
|
||||
completion(.failure(TransportError.noURL))
|
||||
return
|
||||
}
|
||||
|
||||
let request = URLRequest(url: callURL, credentials: credentials)
|
||||
transport.send(request: request, resultType: NewsBlurUnreadArticleHashesResponse.self, dateDecoding: .secondsSince1970) { result in
|
||||
switch result {
|
||||
case .success((_, let payload)):
|
||||
guard let subscriptions = payload?.subscriptions else {
|
||||
completion(.success([]))
|
||||
return
|
||||
}
|
||||
completion(.success(subscriptions.values.flatMap { $0 }))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
|
|
@ -58,11 +58,57 @@ final class NewsBlurAccountDelegate: AccountDelegate {
|
|||
}
|
||||
|
||||
func refreshAll(for account: Account, completion: @escaping (Result<Void, Error>) -> ()) {
|
||||
self.refreshProgress.addToNumberOfTasks(1)
|
||||
self.refreshProgress.addToNumberOfTasksAndRemaining(5)
|
||||
|
||||
refreshSubscriptions(for: account) { result in
|
||||
self.refreshProgress.completeTask()
|
||||
|
||||
switch result {
|
||||
case .success:
|
||||
completion(.success(()))
|
||||
self.sendArticleStatus(for: account) { result in
|
||||
self.refreshProgress.completeTask()
|
||||
|
||||
switch result {
|
||||
case .success:
|
||||
self.refreshArticleStatus(for: account) { result in
|
||||
self.refreshProgress.completeTask()
|
||||
|
||||
switch result {
|
||||
case .success:
|
||||
self.refreshArticles(for: account) { result in
|
||||
self.refreshProgress.completeTask()
|
||||
|
||||
switch result {
|
||||
case .success:
|
||||
self.refreshMissingArticles(for: account) { result in
|
||||
self.refreshProgress.completeTask()
|
||||
|
||||
switch result {
|
||||
case .success:
|
||||
DispatchQueue.main.async {
|
||||
completion(.success(()))
|
||||
}
|
||||
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
@ -77,6 +123,23 @@ final class NewsBlurAccountDelegate: AccountDelegate {
|
|||
completion(.success(()))
|
||||
}
|
||||
|
||||
func refreshArticles(for account: Account, completion: @escaping (Result<[NewsBlurArticleHash], Error>) -> Void) {
|
||||
os_log(.debug, log: log, "Refreshing articles...")
|
||||
|
||||
caller.retrieveUnreadArticleHashes { result in
|
||||
switch result {
|
||||
case .success(let articleHashes):
|
||||
print(articleHashes)
|
||||
case .failure(let error):
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func refreshMissingArticles(for account: Account, completion: @escaping (Result<Void, Error>)-> Void) {
|
||||
completion(.success(()))
|
||||
}
|
||||
|
||||
func importOPML(for account: Account, opmlFile: URL, completion: @escaping (Result<Void, Error>) -> ()) {
|
||||
completion(.success(()))
|
||||
}
|
||||
|
@ -156,11 +219,11 @@ final class NewsBlurAccountDelegate: AccountDelegate {
|
|||
extension NewsBlurAccountDelegate {
|
||||
private func refreshSubscriptions(for account: Account, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
os_log(.debug, log: log, "Refreshing subscriptions...")
|
||||
|
||||
caller.retrieveSubscriptions { result in
|
||||
switch result {
|
||||
case .success(let subscriptions):
|
||||
print(subscriptions)
|
||||
self.refreshProgress.completeTask()
|
||||
completion(.success(()))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
|
|
Loading…
Reference in New Issue