Use NewsBlur terminologies in models (Feed, Story)

This commit is contained in:
Anh Do 2020-03-13 18:30:36 -04:00
parent 175cd0e798
commit f4a0c56a54
No known key found for this signature in database
GPG Key ID: 451E3092F917B62D
7 changed files with 123 additions and 123 deletions

View File

@ -7,12 +7,12 @@
objects = {
/* Begin PBXBuildFile section */
179DB02FFBC17AC9798F0EBC /* NewsBlurArticle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179DB7399814F6FB3247825C /* NewsBlurArticle.swift */; };
179DB02FFBC17AC9798F0EBC /* NewsBlurStory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179DB7399814F6FB3247825C /* NewsBlurStory.swift */; };
179DB28CF49F73A945EBF5DB /* NewsBlurLoginResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179DB088236E3236010462E8 /* NewsBlurLoginResponse.swift */; };
179DB49A960F8B78C4924458 /* NewsBlurGenericCodingKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179DB66D933E976C29159DEE /* NewsBlurGenericCodingKeys.swift */; };
179DB61D33CD8DC94C90F7ED /* NewsBlurDate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179DBB17C42E6E434EDC29FA /* NewsBlurDate.swift */; };
179DBED55C9B4D6A413486C1 /* NewsBlurUnreadArticle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179DB818180A51098A9816B2 /* NewsBlurUnreadArticle.swift */; };
179DBF4DE2562D4C532F6008 /* NewsBlurSubscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179DB1B909672E0E807B5E8C /* NewsBlurSubscription.swift */; };
179DBED55C9B4D6A413486C1 /* NewsBlurUnreadStory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179DB818180A51098A9816B2 /* NewsBlurUnreadStory.swift */; };
179DBF4DE2562D4C532F6008 /* NewsBlurFeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179DB1B909672E0E807B5E8C /* NewsBlurFeed.swift */; };
3B3A33E7238D3D6800314204 /* Secrets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B3A33E6238D3D6800314204 /* Secrets.swift */; };
3B826DA72385C81C00FC1ADB /* FeedWranglerAuthorizationResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B826D9E2385C81C00FC1ADB /* FeedWranglerAuthorizationResult.swift */; };
3B826DA82385C81C00FC1ADB /* FeedWranglerFeedItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B826D9F2385C81C00FC1ADB /* FeedWranglerFeedItem.swift */; };
@ -229,10 +229,10 @@
/* 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>"; };
179DB1B909672E0E807B5E8C /* NewsBlurFeed.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewsBlurFeed.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>"; };
179DB818180A51098A9816B2 /* NewsBlurUnreadArticle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewsBlurUnreadArticle.swift; sourceTree = "<group>"; };
179DB7399814F6FB3247825C /* NewsBlurStory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewsBlurStory.swift; sourceTree = "<group>"; };
179DB818180A51098A9816B2 /* NewsBlurUnreadStory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewsBlurUnreadStory.swift; sourceTree = "<group>"; };
179DBB17C42E6E434EDC29FA /* NewsBlurDate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewsBlurDate.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>"; };
@ -455,10 +455,10 @@
isa = PBXGroup;
children = (
179DB088236E3236010462E8 /* NewsBlurLoginResponse.swift */,
179DB1B909672E0E807B5E8C /* NewsBlurSubscription.swift */,
179DB7399814F6FB3247825C /* NewsBlurArticle.swift */,
179DB1B909672E0E807B5E8C /* NewsBlurFeed.swift */,
179DB7399814F6FB3247825C /* NewsBlurStory.swift */,
179DB66D933E976C29159DEE /* NewsBlurGenericCodingKeys.swift */,
179DB818180A51098A9816B2 /* NewsBlurUnreadArticle.swift */,
179DB818180A51098A9816B2 /* NewsBlurUnreadStory.swift */,
179DBB17C42E6E434EDC29FA /* NewsBlurDate.swift */,
);
path = Models;
@ -1150,10 +1150,10 @@
769F295938E5A30D03DFF88F /* NewsBlurAccountDelegate.swift in Sources */,
769F2BA02EF5F329CDE45F5A /* NewsBlurAPICaller.swift in Sources */,
179DB28CF49F73A945EBF5DB /* NewsBlurLoginResponse.swift in Sources */,
179DBF4DE2562D4C532F6008 /* NewsBlurSubscription.swift in Sources */,
179DB02FFBC17AC9798F0EBC /* NewsBlurArticle.swift in Sources */,
179DBF4DE2562D4C532F6008 /* NewsBlurFeed.swift in Sources */,
179DB02FFBC17AC9798F0EBC /* NewsBlurStory.swift in Sources */,
179DB49A960F8B78C4924458 /* NewsBlurGenericCodingKeys.swift in Sources */,
179DBED55C9B4D6A413486C1 /* NewsBlurUnreadArticle.swift in Sources */,
179DBED55C9B4D6A413486C1 /* NewsBlurUnreadStory.swift in Sources */,
179DB61D33CD8DC94C90F7ED /* NewsBlurDate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@ -1,5 +1,5 @@
//
// NewsBlurSubscription.swift
// NewsBlurFeed.swift
// Account
//
// Created by Anh Quang Do on 2020-03-09.
@ -10,13 +10,13 @@ import Foundation
import RSCore
import RSParser
typealias NewsBlurSubscription = NewsBlurFeedsResponse.Subscription
typealias NewsBlurFeed = NewsBlurFeedsResponse.Feed
struct NewsBlurFeedsResponse: Decodable {
let subscriptions: [Subscription]
let feeds: [Feed]
let folders: [Folder]
struct Subscription: Hashable, Codable {
struct Feed: Hashable, Codable {
let title: String
let feedId: Int
let feedURL: String
@ -26,7 +26,7 @@ struct NewsBlurFeedsResponse: Decodable {
struct Folder: Hashable, Codable {
let name: String
let subscriptionIds: [Int]
let feedIds: [Int]
}
}
@ -40,12 +40,12 @@ extension NewsBlurFeedsResponse {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
// Parse subscriptions
var subscriptions: [Subscription] = []
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)
// Parse feeds
var feeds: [Feed] = []
let feedContainer = try container.nestedContainer(keyedBy: NewsBlurGenericCodingKeys.self, forKey: .feeds)
try feedContainer.allKeys.forEach { key in
let subscription = try feedContainer.decode(Feed.self, forKey: key)
feeds.append(subscription)
}
// Parse folders
@ -53,17 +53,17 @@ extension NewsBlurFeedsResponse {
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)
let folder = Folder(name: key.stringValue, feedIds: subscriptionIds)
folders.append(folder)
}
self.subscriptions = subscriptions
self.feeds = feeds
self.folders = folders
}
}
extension NewsBlurFeedsResponse.Subscription {
extension NewsBlurFeedsResponse.Feed {
private enum CodingKeys: String, CodingKey {
case title = "feed_title"
case feedId = "id"

View File

@ -1,5 +1,5 @@
//
// NewsBlurArticle.swift
// NewsBlurStory.swift
// Account
//
// Created by Anh Quang Do on 2020-03-10.
@ -10,13 +10,13 @@ import Foundation
import RSCore
import RSParser
typealias NewsBlurArticle = NewsBlurArticlesResponse.Article
typealias NewsBlurStory = NewsBlurStoriesResponse.Story
struct NewsBlurArticlesResponse: Decodable {
let articles: [Article]
struct NewsBlurStoriesResponse: Decodable {
let stories: [Story]
struct Article: Decodable {
let articleId: String
struct Story: Decodable {
let storyId: String
let feedId: Int
let title: String?
let url: String?
@ -26,15 +26,15 @@ struct NewsBlurArticlesResponse: Decodable {
}
}
extension NewsBlurArticlesResponse {
extension NewsBlurStoriesResponse {
private enum CodingKeys: String, CodingKey {
case articles = "stories"
case stories = "stories"
}
}
extension NewsBlurArticlesResponse.Article {
extension NewsBlurStoriesResponse.Story {
private enum CodingKeys: String, CodingKey {
case articleId = "story_hash"
case storyId = "story_hash"
case feedId = "story_feed_id"
case title = "story_title"
case url = "story_permalink"

View File

@ -1,50 +0,0 @@
//
// NewsBlurUnreadArticle.swift
// Account
//
// Created by Anh Quang Do on 2020-03-13.
// 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,50 @@
//
// NewsBlurUnreadStory.swift
// Account
//
// Created by Anh Quang Do on 2020-03-13.
// Copyright (c) 2020 Ranchero Software, LLC. All rights reserved.
//
import Foundation
import RSCore
import RSParser
typealias NewsBlurStoryHash = NewsBlurUnreadStoryHashesResponse.StoryHash
struct NewsBlurUnreadStoryHashesResponse: Decodable {
let feeds: [String: [StoryHash]]
struct StoryHash: Hashable, Codable {
var hash: String
var timestamp: Date
}
}
extension NewsBlurUnreadStoryHashesResponse {
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 feeds
var feeds: [String: [StoryHash]] = [:]
let feedContainer = try container.nestedContainer(keyedBy: NewsBlurGenericCodingKeys.self, forKey: .feeds)
try feedContainer.allKeys.forEach { key in
feeds[key.stringValue] = []
var hashArrayContainer = try feedContainer.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 storyHash = StoryHash(hash: hash, timestamp: timestamp)
feeds[key.stringValue]?.append(storyHash)
}
}
self.feeds = feeds
}
}

View File

@ -84,7 +84,7 @@ final class NewsBlurAPICaller: NSObject {
}
}
func retrieveSubscriptions(completion: @escaping (Result<[NewsBlurSubscription]?, Error>) -> Void) {
func retrieveFeeds(completion: @escaping (Result<[NewsBlurFeed]?, Error>) -> Void) {
let url = baseURL
.appendingPathComponent("reader/feeds")
.appendingQueryItems([
@ -101,14 +101,14 @@ final class NewsBlurAPICaller: NSObject {
transport.send(request: request, resultType: NewsBlurFeedsResponse.self) { result in
switch result {
case .success((_, let payload)):
completion(.success(payload?.subscriptions))
completion(.success(payload?.feeds))
case .failure(let error):
completion(.failure(error))
}
}
}
func retrieveUnreadArticleHashes(completion: @escaping (Result<[NewsBlurArticleHash]?, Error>) -> Void) {
func retrieveUnreadStoryHashes(completion: @escaping (Result<[NewsBlurStoryHash]?, Error>) -> Void) {
let url = baseURL
.appendingPathComponent("reader/unread_story_hashes")
.appendingQueryItems([
@ -121,17 +121,17 @@ final class NewsBlurAPICaller: NSObject {
}
let request = URLRequest(url: callURL, credentials: credentials)
transport.send(request: request, resultType: NewsBlurUnreadArticleHashesResponse.self, dateDecoding: .secondsSince1970) { result in
transport.send(request: request, resultType: NewsBlurUnreadStoryHashesResponse.self, dateDecoding: .secondsSince1970) { result in
switch result {
case .success((_, let payload)):
completion(.success(payload?.subscriptions.values.flatMap { $0 }))
completion(.success(payload?.feeds.values.flatMap { $0 }))
case .failure(let error):
completion(.failure(error))
}
}
}
func retrieveArticles(hashes: [NewsBlurArticleHash], completion: @escaping (Result<[NewsBlurArticle]?, Error>) -> Void) {
func retrieveStories(hashes: [NewsBlurStoryHash], completion: @escaping (Result<[NewsBlurStory]?, Error>) -> Void) {
let url = baseURL
.appendingPathComponent("reader/river_stories")
.appendingQueryItem(.init(name: "include_hidden", value: "true"))?
@ -143,10 +143,10 @@ final class NewsBlurAPICaller: NSObject {
}
let request = URLRequest(url: callURL, credentials: credentials)
transport.send(request: request, resultType: NewsBlurArticlesResponse.self, dateDecoding: .formatted(NewsBlurDate.yyyyMMddHHmmss)) { result in
transport.send(request: request, resultType: NewsBlurStoriesResponse.self, dateDecoding: .formatted(NewsBlurDate.yyyyMMddHHmmss)) { result in
switch result {
case .success((_, let payload)):
completion(.success(payload?.articles))
completion(.success(payload?.stories))
case .failure(let error):
completion(.failure(error))
}

View File

@ -60,7 +60,7 @@ final class NewsBlurAccountDelegate: AccountDelegate {
func refreshAll(for account: Account, completion: @escaping (Result<Void, Error>) -> ()) {
self.refreshProgress.addToNumberOfTasksAndRemaining(5)
refreshSubscriptions(for: account) { result in
refreshFeeds(for: account) { result in
self.refreshProgress.completeTask()
switch result {
@ -75,12 +75,12 @@ final class NewsBlurAccountDelegate: AccountDelegate {
switch result {
case .success:
self.refreshArticles(for: account) { result in
self.refreshStories(for: account) { result in
self.refreshProgress.completeTask()
switch result {
case .success:
self.refreshMissingArticles(for: account) { result in
self.refreshMissingStories(for: account) { result in
self.refreshProgress.completeTask()
switch result {
@ -123,23 +123,23 @@ final class NewsBlurAccountDelegate: AccountDelegate {
completion(.success(()))
}
func refreshArticles(for account: Account, completion: @escaping (Result<Void, Error>) -> Void) {
os_log(.debug, log: log, "Refreshing articles...")
os_log(.debug, log: log, "Refreshing unread articles...")
func refreshStories(for account: Account, completion: @escaping (Result<Void, Error>) -> Void) {
os_log(.debug, log: log, "Refreshing stories...")
os_log(.debug, log: log, "Refreshing unread stories...")
caller.retrieveUnreadArticleHashes { result in
caller.retrieveUnreadStoryHashes { result in
switch result {
case .success(let articleHashes):
case .success(let storyHashes):
self.refreshProgress.completeTask()
self.refreshUnreadArticles(for: account, hashes: articleHashes, updateFetchDate: nil, completion: completion)
self.refreshUnreadStories(for: account, hashes: storyHashes, updateFetchDate: nil, completion: completion)
case .failure(let error):
completion(.failure(error))
}
}
}
func refreshUnreadArticles(for account: Account, hashes: [NewsBlurArticleHash]?, updateFetchDate: Date?, completion: @escaping (Result<Void, Error>) -> Void) {
func refreshUnreadStories(for account: Account, hashes: [NewsBlurStoryHash]?, updateFetchDate: Date?, completion: @escaping (Result<Void, Error>) -> Void) {
guard let hashes = hashes, !hashes.isEmpty else {
if let lastArticleFetch = updateFetchDate {
self.accountMetadata?.lastArticleFetchStartTime = lastArticleFetch
@ -149,13 +149,13 @@ final class NewsBlurAccountDelegate: AccountDelegate {
return
}
let numberOfArticles = min(hashes.count, 100) // api limit
let hashesToFetch = Array(hashes[..<numberOfArticles])
let numberOfStories = min(hashes.count, 100) // api limit
let hashesToFetch = Array(hashes[..<numberOfStories])
caller.retrieveArticles(hashes: hashesToFetch) { result in
caller.retrieveStories(hashes: hashesToFetch) { result in
switch result {
case .success(let articles):
self.processArticles(account: account, articles: articles) { error in
case .success(let stories):
self.processStories(account: account, stories: stories) { error in
self.refreshProgress.completeTask()
if let error = error {
@ -163,7 +163,7 @@ final class NewsBlurAccountDelegate: AccountDelegate {
return
}
self.refreshUnreadArticles(for: account, hashes: Array(hashes[numberOfArticles...]), updateFetchDate: updateFetchDate, completion: completion)
self.refreshUnreadStories(for: account, hashes: Array(hashes[numberOfStories...]), updateFetchDate: updateFetchDate, completion: completion)
}
case .failure(let error):
completion(.failure(error))
@ -171,24 +171,24 @@ final class NewsBlurAccountDelegate: AccountDelegate {
}
}
func refreshMissingArticles(for account: Account, completion: @escaping (Result<Void, Error>)-> Void) {
func refreshMissingStories(for account: Account, completion: @escaping (Result<Void, Error>)-> Void) {
completion(.success(()))
}
func processArticles(account: Account, articles: [NewsBlurArticle]?, completion: @escaping DatabaseCompletionBlock) {
let parsedItems = mapArticlesToParsedItems(articles: articles)
func processStories(account: Account, stories: [NewsBlurStory]?, completion: @escaping DatabaseCompletionBlock) {
let parsedItems = mapStoriesToParsedItems(stories: stories)
let webFeedIDsAndItems = Dictionary(grouping: parsedItems, by: { item in item.feedURL } ).mapValues { Set($0) }
account.update(webFeedIDsAndItems: webFeedIDsAndItems, defaultRead: true, completion: completion)
}
func mapArticlesToParsedItems(articles: [NewsBlurArticle]?) -> Set<ParsedItem> {
guard let articles = articles else {
func mapStoriesToParsedItems(stories: [NewsBlurStory]?) -> Set<ParsedItem> {
guard let stories = stories else {
return Set<ParsedItem>()
}
let parsedItems: [ParsedItem] = articles.map { article in
let author = Set([ParsedAuthor(name: article.authorName, url: nil, avatarURL: nil, emailAddress: nil)])
return ParsedItem(syncServiceID: article.articleId, uniqueID: String(article.articleId), feedURL: String(article.feedId), url: article.url, externalURL: nil, title: article.title, contentHTML: article.contentHTML, contentText: nil, summary: nil, imageURL: nil, bannerImageURL: nil, datePublished: article.datePublished, dateModified: nil, authors: author, tags: nil, attachments: nil)
let parsedItems: [ParsedItem] = stories.map { story in
let author = Set([ParsedAuthor(name: story.authorName, url: nil, avatarURL: nil, emailAddress: nil)])
return ParsedItem(syncServiceID: story.storyId, uniqueID: String(story.storyId), feedURL: String(story.feedId), url: story.url, externalURL: nil, title: story.title, contentHTML: story.contentHTML, contentText: nil, summary: nil, imageURL: nil, bannerImageURL: nil, datePublished: story.datePublished, dateModified: nil, authors: author, tags: nil, attachments: nil)
}
return Set(parsedItems)
@ -271,13 +271,13 @@ 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...")
private func refreshFeeds(for account: Account, completion: @escaping (Result<Void, Error>) -> Void) {
os_log(.debug, log: log, "Refreshing feeds...")
caller.retrieveSubscriptions { result in
caller.retrieveFeeds { result in
switch result {
case .success(let subscriptions):
print(subscriptions)
case .success(let feeds):
print(feeds)
completion(.success(()))
case .failure(let error):
completion(.failure(error))