Add support for basic Reddit articles

This commit is contained in:
Maurice Parker 2020-05-04 14:42:34 -05:00
parent d763ca93f6
commit e8a2b54184
5 changed files with 166 additions and 75 deletions

View File

@ -44,7 +44,8 @@
513323082281070D00C30F19 /* AccountFeedbinSyncTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 513323072281070C00C30F19 /* AccountFeedbinSyncTest.swift */; };
5133230A2281082F00C30F19 /* subscriptions_initial.json in Resources */ = {isa = PBXBuildFile; fileRef = 513323092281082F00C30F19 /* subscriptions_initial.json */; };
5133230C2281088A00C30F19 /* subscriptions_add.json in Resources */ = {isa = PBXBuildFile; fileRef = 5133230B2281088A00C30F19 /* subscriptions_add.json */; };
5133BB47245FD8140001E3D0 /* RedditListing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5133BB46245FD8140001E3D0 /* RedditListing.swift */; };
5133BB47245FD8140001E3D0 /* RedditLinkListing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5133BB46245FD8140001E3D0 /* RedditLinkListing.swift */; };
5133BB49246078910001E3D0 /* RedditLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5133BB48246078910001E3D0 /* RedditLink.swift */; };
5139A6382459822D004D960C /* CloudKitArticleStatusUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5139A6372459822D004D960C /* CloudKitArticleStatusUpdate.swift */; };
5144EA49227B497600D19003 /* FeedbinAPICaller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5144EA48227B497600D19003 /* FeedbinAPICaller.swift */; };
5144EA4E227B829A00D19003 /* FeedbinAccountDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5144EA4D227B829A00D19003 /* FeedbinAccountDelegate.swift */; };
@ -301,7 +302,8 @@
513323072281070C00C30F19 /* AccountFeedbinSyncTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountFeedbinSyncTest.swift; sourceTree = "<group>"; };
513323092281082F00C30F19 /* subscriptions_initial.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = subscriptions_initial.json; sourceTree = "<group>"; };
5133230B2281088A00C30F19 /* subscriptions_add.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = subscriptions_add.json; sourceTree = "<group>"; };
5133BB46245FD8140001E3D0 /* RedditListing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedditListing.swift; sourceTree = "<group>"; };
5133BB46245FD8140001E3D0 /* RedditLinkListing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedditLinkListing.swift; sourceTree = "<group>"; };
5133BB48246078910001E3D0 /* RedditLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedditLink.swift; sourceTree = "<group>"; };
5139A6372459822D004D960C /* CloudKitArticleStatusUpdate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudKitArticleStatusUpdate.swift; sourceTree = "<group>"; };
5144EA48227B497600D19003 /* FeedbinAPICaller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinAPICaller.swift; sourceTree = "<group>"; };
5144EA4D227B829A00D19003 /* FeedbinAccountDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinAccountDelegate.swift; sourceTree = "<group>"; };
@ -635,7 +637,8 @@
isa = PBXGroup;
children = (
5193CD53245E3F7A0092735E /* RedditFeedProvider.swift */,
5133BB46245FD8140001E3D0 /* RedditListing.swift */,
5133BB48246078910001E3D0 /* RedditLink.swift */,
5133BB46245FD8140001E3D0 /* RedditLinkListing.swift */,
5193CD80245F295E0092735E /* RedditUser.swift */,
);
path = Reddit;
@ -1207,7 +1210,7 @@
51F6C593245DBA8E001E41CA /* CloudKitRemoteNotificationOperation.swift in Sources */,
846E77541F6F00E300A165E2 /* AccountManager.swift in Sources */,
51E490362288C37100C791F0 /* FeedbinDate.swift in Sources */,
5133BB47245FD8140001E3D0 /* RedditListing.swift in Sources */,
5133BB47245FD8140001E3D0 /* RedditLinkListing.swift in Sources */,
9EEAE06E235D002D00E3FEE4 /* FeedlyGetCollectionsService.swift in Sources */,
5165D72922835F7A00D9D53D /* FeedSpecifier.swift in Sources */,
9E85C8ED2367020700D0F1F7 /* FeedlyGetEntriesService.swift in Sources */,
@ -1277,6 +1280,7 @@
9E964EB823754AC400A7AF2E /* OAuthAuthorizationClient+Feedly.swift in Sources */,
9EF1B10923590E93000A486A /* FeedlyStreamIds.swift in Sources */,
84D09623217418DC00D77525 /* FeedbinTagging.swift in Sources */,
5133BB49246078910001E3D0 /* RedditLink.swift in Sources */,
84CAD7161FDF2E22000F0755 /* FeedbinEntry.swift in Sources */,
5165D72A22835F7D00D9D53D /* HTMLFeedFinder.swift in Sources */,
841974011F6DD1EC006346C4 /* Folder.swift in Sources */,

View File

@ -80,6 +80,7 @@ public final class RedditFeedProvider: FeedProvider {
}
public func iconURL(_ urlComponents: URLComponents, completion: @escaping (Result<String, Error>) -> Void) {
// TODO: call https://www.reddit.com/user/<USERNAME>/about.json to get the key "icon_img"
completion(.failure(RedditFeedProviderError.unknown))
}
@ -107,7 +108,13 @@ public final class RedditFeedProvider: FeedProvider {
}
let api = urlComponents.path
retrieveListing(api: api, parameters: [:]) { result in
completion(.success(Set<ParsedItem>()))
switch result {
case .success(let linkListing):
let parsedItems = self.makeParsedItems(webFeed.url, linkListing)
completion(.success(parsedItems))
case .failure(let error):
completion(.failure(error))
}
}
}
@ -194,7 +201,7 @@ private extension RedditFeedProvider {
}
}
func retrieveListing(api: String, parameters: [String: Any], completion: @escaping (Result<RedditListing, Error>) -> Void) {
func retrieveListing(api: String, parameters: [String: Any], completion: @escaping (Result<RedditLinkListing, Error>) -> Void) {
guard let client = client else {
completion(.failure(RedditFeedProviderError.unknown))
return
@ -214,20 +221,14 @@ private extension RedditFeedProvider {
print("******** writing to: \(url.path)")
try? jsonString?.write(toFile: url.path, atomically: true, encoding: .utf8)
// let decoder = JSONDecoder()
// let dateFormatter = DateFormatter()
// dateFormatter.dateFormat = Self.dateFormat
// decoder.dateDecodingStrategy = .formatted(dateFormatter)
let decoder = JSONDecoder()
// do {
// let listing = try decoder.decode(RedditListing.self, from: response.data)
// completion(.success(listing))
// } catch {
// completion(.failure(error))
// }
let listing = RedditListing(name: "")
do {
let listing = try decoder.decode(RedditLinkListing.self, from: response.data)
completion(.success(listing))
} catch {
completion(.failure(error))
}
case .failure(let oathError):
self.handleFailure(error: oathError) { error in
@ -241,43 +242,46 @@ private extension RedditFeedProvider {
}
}
// func makeParsedItems(_ webFeedURL: String, _ statuses: [TwitterStatus]) -> Set<ParsedItem> {
// var parsedItems = Set<ParsedItem>()
//
// for status in statuses {
// guard let idStr = status.idStr, let statusURL = status.url else { continue }
//
// let parsedItem = ParsedItem(syncServiceID: nil,
// uniqueID: idStr,
// feedURL: webFeedURL,
// url: statusURL,
// externalURL: nil,
// title: nil,
// language: nil,
// contentHTML: status.renderAsHTML(),
// contentText: status.renderAsText(),
// summary: nil,
// imageURL: nil,
// bannerImageURL: nil,
// datePublished: status.createdAt,
// dateModified: nil,
// authors: makeParsedAuthors(status.user),
// tags: nil,
// attachments: nil)
// parsedItems.insert(parsedItem)
// }
//
// return parsedItems
// }
//
// func makeUserURL(_ screenName: String) -> String {
// return "https://twitter.com/\(screenName)"
// }
//
// func makeParsedAuthors(_ user: TwitterUser?) -> Set<ParsedAuthor>? {
// guard let user = user else { return nil }
// return Set([ParsedAuthor(name: user.name, url: user.url, avatarURL: user.avatarURL, emailAddress: nil)])
// }
func makeParsedItems(_ webFeedURL: String, _ linkListing: RedditLinkListing) -> Set<ParsedItem> {
var parsedItems = Set<ParsedItem>()
guard let linkDatas = linkListing.data?.children?.compactMap({ $0.data }), !linkDatas.isEmpty else {
return parsedItems
}
for linkData in linkDatas {
guard let permalink = linkData.permalink else { continue }
let parsedItem = ParsedItem(syncServiceID: nil,
uniqueID: permalink,
feedURL: webFeedURL,
url: "https://www.reddit.com\(permalink)",
externalURL: linkData.url,
title: linkData.title,
language: nil,
contentHTML: linkData.renderAsHTML(),
contentText: linkData.selfText,
summary: nil,
imageURL: nil,
bannerImageURL: nil,
datePublished: linkData.createdDate,
dateModified: nil,
authors: makeParsedAuthors(linkData.author),
tags: nil,
attachments: nil)
parsedItems.insert(parsedItem)
}
return parsedItems
}
func makeParsedAuthors(_ username: String?) -> Set<ParsedAuthor>? {
guard let username = username else { return nil }
var urlComponents = URLComponents(string: "https://www.reddit.com")
urlComponents?.path = "/u/\(username)"
let userURL = urlComponents?.url?.absoluteString
return Set([ParsedAuthor(name: "u/\(username)", url: userURL, avatarURL: userURL, emailAddress: nil)])
}
func handleFailure(error: OAuthSwiftError, completion: @escaping (Error?) -> Void) {
if case .tokenExpired = error {

View File

@ -0,0 +1,71 @@
//
// RedditLink.swift
// Account
//
// Created by Maurice Parker on 5/4/20.
// Copyright © 2020 Ranchero Software, LLC. All rights reserved.
//
import Foundation
struct RedditLink: Codable {
let kind: String?
let data: RedditLinkData?
enum CodingKeys: String, CodingKey {
case kind = "kind"
case data = "data"
}
}
struct RedditLinkData: Codable {
let title: String?
let permalink: String?
let url: String?
let id: String?
let selfHTML: String?
let selfText: String?
let author: String?
let created: Double?
enum CodingKeys: String, CodingKey {
case title = "title"
case permalink = "permalink"
case url = "url"
case id = "id"
case selfHTML = "selftext_html"
case selfText = "selftext"
case author = "author"
case created = "created_utc"
}
var createdDate: Date? {
guard let created = created else { return nil }
return Date(timeIntervalSince1970: created)
}
func renderAsHTML() -> String? {
var html = String()
if let selfHTML = selfHTML {
html.append(selfHTML)
}
if let urlHTML = renderURLAsHTML() {
html.append(urlHTML)
}
return html
}
func renderURLAsHTML() -> String? {
guard let url = url else { return nil }
guard url.hasSuffix(".jpg") || url.hasSuffix(".jpeg") || url.hasSuffix(".png") || url.hasSuffix(".gif") else {
return nil
}
return "<figure><img src=\"\(url)\"></figure>"
}
}

View File

@ -0,0 +1,31 @@
//
// RedditLinkListing.swift
// Account
//
// Created by Maurice Parker on 5/3/20.
// Copyright © 2020 Ranchero Software, LLC. All rights reserved.
//
import Foundation
struct RedditLinkListing: Codable {
let kind: String?
let data: RedditLinkListingData?
enum CodingKeys: String, CodingKey {
case kind
case data
}
}
struct RedditLinkListingData: Codable {
let children: [RedditLink]?
enum CodingKeys: String, CodingKey {
case children = "children"
}
}

View File

@ -1,19 +0,0 @@
//
// RedditListing.swift
// Account
//
// Created by Maurice Parker on 5/3/20.
// Copyright © 2020 Ranchero Software, LLC. All rights reserved.
//
import Foundation
struct RedditListing: Codable {
let name: String?
enum CodingKeys: String, CodingKey {
case name = "name"
}
}