Add unread story hashes query

This commit is contained in:
Anh Do 2020-03-10 21:08:56 -04:00
parent 9727219b09
commit d37f70d2dd
No known key found for this signature in database
GPG Key ID: 451E3092F917B62D
6 changed files with 181 additions and 24 deletions

View File

@ -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;
};

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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))
}

View File

@ -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))