Add support for basic Reddit articles
This commit is contained in:
parent
d763ca93f6
commit
e8a2b54184
|
@ -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 */,
|
||||
|
|
|
@ -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,21 +221,15 @@ 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))
|
||||
// }
|
||||
do {
|
||||
let listing = try decoder.decode(RedditLinkListing.self, from: response.data)
|
||||
completion(.success(listing))
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
||||
let listing = RedditListing(name: "")
|
||||
completion(.success(listing))
|
||||
|
||||
case .failure(let oathError):
|
||||
self.handleFailure(error: oathError) { error in
|
||||
if let error = error {
|
||||
|
@ -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 {
|
||||
|
|
|
@ -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>"
|
||||
}
|
||||
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue