Stub out Reddit Feed Provider / Extension Point

This commit is contained in:
Maurice Parker 2020-05-02 19:38:57 -05:00
parent da8482a53a
commit e681fd6402
14 changed files with 278 additions and 5 deletions

View File

@ -61,6 +61,7 @@
5165D73122837F3400D9D53D /* InitialFeedDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5165D73022837F3400D9D53D /* InitialFeedDownloader.swift */; };
516896352448EBEA00185AC5 /* FeedProviderManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 516896342448EBEA00185AC5 /* FeedProviderManager.swift */; };
5170743C232AEDB500A461A3 /* OPMLFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5170743B232AEDB500A461A3 /* OPMLFile.swift */; };
5193CD54245E3F7A0092735E /* RedditFeedProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5193CD53245E3F7A0092735E /* RedditFeedProvider.swift */; };
519E84A62433D49000D238B0 /* OPMLNormalizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519E84A52433D49000D238B0 /* OPMLNormalizer.swift */; };
519E84A82434C5EF00D238B0 /* CloudKitArticlesZone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519E84A72434C5EF00D238B0 /* CloudKitArticlesZone.swift */; };
519E84AC2435019100D238B0 /* CloudKitArticlesZoneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519E84AB2435019100D238B0 /* CloudKitArticlesZoneDelegate.swift */; };
@ -315,6 +316,7 @@
5165D73022837F3400D9D53D /* InitialFeedDownloader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InitialFeedDownloader.swift; sourceTree = "<group>"; };
516896342448EBEA00185AC5 /* FeedProviderManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedProviderManager.swift; sourceTree = "<group>"; };
5170743B232AEDB500A461A3 /* OPMLFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OPMLFile.swift; sourceTree = "<group>"; };
5193CD53245E3F7A0092735E /* RedditFeedProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedditFeedProvider.swift; sourceTree = "<group>"; };
519E84A52433D49000D238B0 /* OPMLNormalizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OPMLNormalizer.swift; sourceTree = "<group>"; };
519E84A72434C5EF00D238B0 /* CloudKitArticlesZone.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudKitArticlesZone.swift; sourceTree = "<group>"; };
519E84AB2435019100D238B0 /* CloudKitArticlesZoneDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CloudKitArticlesZoneDelegate.swift; sourceTree = "<group>"; };
@ -590,6 +592,7 @@
5132AAC12448BAD90077840A /* FeedProvider.swift */,
516896342448EBEA00185AC5 /* FeedProviderManager.swift */,
5132AAC22448BAD90077840A /* Twitter */,
5193CD52245E3F520092735E /* Reddit */,
);
path = FeedProvider;
sourceTree = "<group>";
@ -624,6 +627,14 @@
path = FeedFinder;
sourceTree = "<group>";
};
5193CD52245E3F520092735E /* Reddit */ = {
isa = PBXGroup;
children = (
5193CD53245E3F7A0092735E /* RedditFeedProvider.swift */,
);
path = Reddit;
sourceTree = "<group>";
};
51D58756227F62E300900287 /* JSON */ = {
isa = PBXGroup;
children = (
@ -1180,6 +1191,7 @@
841974251F6DDCE4006346C4 /* AccountDelegate.swift in Sources */,
510BD113232C3E9D002692E4 /* WebFeedMetadataFile.swift in Sources */,
51B36309244B62A5000DEF2A /* TwitterURL.swift in Sources */,
5193CD54245E3F7A0092735E /* RedditFeedProvider.swift in Sources */,
5103A9D92422546800410853 /* CloudKitAccountDelegate.swift in Sources */,
5165D73122837F3400D9D53D /* InitialFeedDownloader.swift in Sources */,
9E784EBE237E890600099B1B /* FeedlyLogoutOperation.swift in Sources */,

View File

@ -0,0 +1,138 @@
//
// RedditFeedProvider.swift
// Account
//
// Created by Maurice Parker on 5/2/20.
// Copyright © 2020 Ranchero Software, LLC. All rights reserved.
//
import Foundation
import OAuthSwift
import Secrets
import RSParser
public enum RedditFeedProviderError: LocalizedError {
case unknown
public var localizedDescription: String {
switch self {
case .unknown:
return NSLocalizedString("An Reddit Twitter Feed Provider error has occurred.", comment: "Unknown error")
}
}
}
public struct RedditFeedProvider: FeedProvider {
private static let server = "api.twitter.com"
private static let apiBase = "https://api.twitter.com/1.1/"
private static let dateFormat = "EEE MMM dd HH:mm:ss Z yyyy"
private static let userPaths = ["/home", "/notifications"]
private static let reservedPaths = ["/search", "/explore", "/messages", "/i", "/compose"]
public var username: String
private var oauthToken: String
private var oauthTokenSecret: String
private var client: OAuthSwiftClient
public init?(tokenSuccess: OAuthSwift.TokenSuccess) {
guard let username = tokenSuccess.parameters["screen_name"] as? String else {
return nil
}
self.username = username
self.oauthToken = tokenSuccess.credential.oauthToken
self.oauthTokenSecret = tokenSuccess.credential.oauthTokenSecret
let tokenCredentials = Credentials(type: .oauthAccessToken, username: username, secret: oauthToken)
try? CredentialsManager.storeCredentials(tokenCredentials, server: Self.server)
let tokenSecretCredentials = Credentials(type: .oauthAccessTokenSecret, username: username, secret: oauthTokenSecret)
try? CredentialsManager.storeCredentials(tokenSecretCredentials, server: Self.server)
client = OAuthSwiftClient(consumerKey: Secrets.twitterConsumerKey,
consumerSecret: Secrets.twitterConsumerSecret,
oauthToken: oauthToken,
oauthTokenSecret: oauthTokenSecret,
version: .oauth1)
}
public init?(username: String) {
self.username = username
guard let tokenCredentials = try? CredentialsManager.retrieveCredentials(type: .oauthAccessToken, server: Self.server, username: username),
let tokenSecretCredentials = try? CredentialsManager.retrieveCredentials(type: .oauthAccessTokenSecret, server: Self.server, username: username) else {
return nil
}
self.oauthToken = tokenCredentials.secret
self.oauthTokenSecret = tokenSecretCredentials.secret
client = OAuthSwiftClient(consumerKey: Secrets.twitterConsumerKey,
consumerSecret: Secrets.twitterConsumerSecret,
oauthToken: oauthToken,
oauthTokenSecret: oauthTokenSecret,
version: .oauth1)
}
public func ability(_ urlComponents: URLComponents) -> FeedProviderAbility {
guard urlComponents.host?.hasSuffix("reddit.com") ?? false else {
return .none
}
if let username = urlComponents.user {
if username == username {
return .owner
} else {
return .none
}
}
return .available
}
public func iconURL(_ urlComponents: URLComponents, completion: @escaping (Result<String, Error>) -> Void) {
completion(.failure(TwitterFeedProviderError.screenNameNotFound))
}
public func assignName(_ urlComponents: URLComponents, completion: @escaping (Result<String, Error>) -> Void) {
let path = urlComponents.path
switch path {
case "", "/":
let name = NSLocalizedString("Reddit Timeline", comment: "Reddit Timeline")
completion(.success(name))
case "/r", "/u":
let path = String(path.suffix(from: path.index(path.startIndex, offsetBy: 2)))
completion(.success(path))
case "/user":
let path = String(path.suffix(from: path.index(path.startIndex, offsetBy: 5)))
completion(.success(path))
default:
completion(.failure(TwitterFeedProviderError.unknown))
}
}
public func refresh(_ webFeed: WebFeed, completion: @escaping (Result<Set<ParsedItem>, Error>) -> Void) {
// guard let urlComponents = URLComponents(string: webFeed.url) else {
// completion(.failure(TwitterFeedProviderError.unknown))
// return
// }
completion(.success(Set<ParsedItem>()))
}
}
// MARK: OAuth1SwiftProvider
extension RedditFeedProvider: OAuth2SwiftProvider {
public static var oauth2Swift: OAuth2Swift {
return OAuth2Swift(consumerKey: "", consumerSecret: "", authorizeUrl: "", accessTokenUrl: "", responseType: "")
}
}

View File

@ -0,0 +1,17 @@
//
// OAuth2SwiftProvider.swift
// Secrets
//
// Created by Maurice Parker on 5/2/20.
// Copyright © 2020 Ranchero Software, LLC. All rights reserved.
//
import Foundation
import OAuthSwift
public protocol OAuth2SwiftProvider {
static var oauth2Swift: OAuth2Swift { get }
}

View File

@ -15,6 +15,7 @@
514BB43B243FFBFF0023B621 /* CredentialsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 514BB439243FFBFF0023B621 /* CredentialsManager.swift */; };
514BB43C243FFBFF0023B621 /* Credentials.swift in Sources */ = {isa = PBXBuildFile; fileRef = 514BB43A243FFBFF0023B621 /* Credentials.swift */; };
5152BEF2244633FA00138380 /* OAuth1SwiftProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5152BEF1244633FA00138380 /* OAuth1SwiftProvider.swift */; };
5193CD56245E40B70092735E /* OAuth2SwiftProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5193CD55245E40B70092735E /* OAuth2SwiftProvider.swift */; };
51C99ABD2447DD730027D5F6 /* OAuthSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 51C99ABC2447DD730027D5F6 /* OAuthSwift.framework */; };
/* End PBXBuildFile section */
@ -29,6 +30,7 @@
514BB439243FFBFF0023B621 /* CredentialsManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CredentialsManager.swift; sourceTree = "<group>"; };
514BB43A243FFBFF0023B621 /* Credentials.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Credentials.swift; sourceTree = "<group>"; };
5152BEF1244633FA00138380 /* OAuth1SwiftProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuth1SwiftProvider.swift; sourceTree = "<group>"; };
5193CD55245E40B70092735E /* OAuth2SwiftProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuth2SwiftProvider.swift; sourceTree = "<group>"; };
51C99ABC2447DD730027D5F6 /* OAuthSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = OAuthSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
@ -50,6 +52,7 @@
514BB43A243FFBFF0023B621 /* Credentials.swift */,
514BB439243FFBFF0023B621 /* CredentialsManager.swift */,
5152BEF1244633FA00138380 /* OAuth1SwiftProvider.swift */,
5193CD55245E40B70092735E /* OAuth2SwiftProvider.swift */,
514BB41E243FFA640023B621 /* Info.plist */,
514BB41B243FFA640023B621 /* Products */,
514446EC2440030900EE752D /* Secrets.swift */,
@ -192,6 +195,7 @@
514BB43C243FFBFF0023B621 /* Credentials.swift in Sources */,
514446ED2440030900EE752D /* Secrets.swift in Sources */,
5152BEF2244633FA00138380 /* OAuth1SwiftProvider.swift in Sources */,
5193CD56245E40B70092735E /* OAuth2SwiftProvider.swift in Sources */,
514BB43B243FFBFF0023B621 /* CredentialsManager.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@ -85,6 +85,10 @@ struct AppAssets {
return RSImage(named: "extensionPointMicroblog")!
}()
static var extensionPointReddit: RSImage = {
return RSImage(named: "extensionPointReddit")!
}()
static var extensionPointTwitter: RSImage = {
return RSImage(named: "extensionPointTwitter")!
}()

View File

@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "reddit-logo.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}

View File

@ -191,6 +191,9 @@
51934CCE2310792F006127BE /* ActivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51934CCD2310792F006127BE /* ActivityManager.swift */; };
51938DF2231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51938DF1231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift */; };
51938DF3231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51938DF1231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift */; };
5193CD58245E44A90092735E /* RedditFeedProvider-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5193CD57245E44A90092735E /* RedditFeedProvider-Extensions.swift */; };
5193CD59245E44A90092735E /* RedditFeedProvider-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5193CD57245E44A90092735E /* RedditFeedProvider-Extensions.swift */; };
5193CD5A245E44A90092735E /* RedditFeedProvider-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5193CD57245E44A90092735E /* RedditFeedProvider-Extensions.swift */; };
519B8D332143397200FA689C /* SharingServiceDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519B8D322143397200FA689C /* SharingServiceDelegate.swift */; };
519E743D22C663F900A78E47 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519E743422C663F900A78E47 /* SceneDelegate.swift */; };
519ED456244828C3007F8E94 /* AddExtensionPointViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519ED455244828C3007F8E94 /* AddExtensionPointViewController.swift */; };
@ -1502,6 +1505,7 @@
51934CC1230F5963006127BE /* InteractiveNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InteractiveNavigationController.swift; sourceTree = "<group>"; };
51934CCD2310792F006127BE /* ActivityManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityManager.swift; sourceTree = "<group>"; };
51938DF1231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTimelineFeedDelegate.swift; sourceTree = "<group>"; };
5193CD57245E44A90092735E /* RedditFeedProvider-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RedditFeedProvider-Extensions.swift"; sourceTree = "<group>"; };
519B8D322143397200FA689C /* SharingServiceDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharingServiceDelegate.swift; sourceTree = "<group>"; };
519E743422C663F900A78E47 /* SceneDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
519ED455244828C3007F8E94 /* AddExtensionPointViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddExtensionPointViewController.swift; sourceTree = "<group>"; };
@ -1968,6 +1972,7 @@
84A1500420048DDF0046AD9A /* SendToMarsEditCommand.swift */,
84A14FF220048CA70046AD9A /* SendToMicroBlogCommand.swift */,
515A5106243D0CCD0089E588 /* TwitterFeedProvider-Extensions.swift */,
5193CD57245E44A90092735E /* RedditFeedProvider-Extensions.swift */,
);
path = ExtensionPoints;
sourceTree = "<group>";
@ -4141,6 +4146,7 @@
65ED3FE3235DEF6C0081F399 /* ArticleUtilities.swift in Sources */,
65ED3FE4235DEF6C0081F399 /* NNW3OpenPanelAccessoryViewController.swift in Sources */,
65ED3FE5235DEF6C0081F399 /* DefaultFeedsImporter.swift in Sources */,
5193CD59245E44A90092735E /* RedditFeedProvider-Extensions.swift in Sources */,
65ED3FE6235DEF6C0081F399 /* RenameWindowController.swift in Sources */,
65ED3FE7235DEF6C0081F399 /* SendToMicroBlogCommand.swift in Sources */,
65ED3FE8235DEF6C0081F399 /* ArticleStyle.swift in Sources */,
@ -4308,6 +4314,7 @@
51C4526B226508F600C03939 /* MasterFeedViewController.swift in Sources */,
5126EE97226CB48A00C22AFC /* SceneCoordinator.swift in Sources */,
84CAFCB022BC8C35007694F0 /* FetchRequestOperation.swift in Sources */,
5193CD5A245E44A90092735E /* RedditFeedProvider-Extensions.swift in Sources */,
51EF0F77227716200050506E /* FaviconGenerator.swift in Sources */,
51938DF3231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift in Sources */,
51C4525A226508D600C03939 /* UIStoryboard-Extensions.swift in Sources */,
@ -4527,6 +4534,7 @@
849A97531ED9EAC0007D329B /* AddFeedController.swift in Sources */,
5183CCE8226F68D90010922C /* AccountRefreshTimer.swift in Sources */,
849A97831ED9EC63007D329B /* SidebarStatusBarView.swift in Sources */,
5193CD58245E44A90092735E /* RedditFeedProvider-Extensions.swift in Sources */,
51938DF2231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift in Sources */,
84F2D5381FC22FCC00998D64 /* TodayFeedDelegate.swift in Sources */,
841ABA5E20145E9200980E11 /* FolderInspectorViewController.swift in Sources */,

View File

@ -16,7 +16,8 @@ enum ExtensionPointIdentifer: Hashable {
case microblog
#endif
case twitter(String)
case reddit(String)
var extensionPointType: ExtensionPoint.Type {
switch self {
#if os(macOS)
@ -27,6 +28,8 @@ enum ExtensionPointIdentifer: Hashable {
#endif
case .twitter:
return TwitterFeedProvider.self
case .reddit:
return RedditFeedProvider.self
}
}
@ -47,6 +50,11 @@ enum ExtensionPointIdentifer: Hashable {
"type": "twitter",
"screenName": screenName
]
case .reddit(let username):
return [
"type": "reddit",
"screenName": username
]
}
}
@ -63,6 +71,9 @@ enum ExtensionPointIdentifer: Hashable {
case "twitter":
guard let screenName = userInfo["screenName"] as? String else { return nil }
self = ExtensionPointIdentifer.twitter(screenName)
case "reddit":
guard let username = userInfo["username"] as? String else { return nil }
self = ExtensionPointIdentifer.twitter(username)
default:
return nil
}
@ -79,6 +90,9 @@ enum ExtensionPointIdentifer: Hashable {
case .twitter(let screenName):
hasher.combine("twitter")
hasher.combine(screenName)
case .reddit(let username):
hasher.combine("reddit")
hasher.combine(username)
}
}

View File

@ -53,18 +53,22 @@ final class ExtensionPointManager: FeedProviderManagerDelegate {
return activeExtensionPoints.values.contains(where: { $0 is TwitterFeedProvider })
}
var isRedditEnabled: Bool {
return activeExtensionPoints.values.contains(where: { $0 is RedditFeedProvider })
}
init() {
#if os(macOS)
#if DEBUG
possibleExtensionPointTypes = [SendToMarsEditCommand.self, SendToMicroBlogCommand.self, TwitterFeedProvider.self]
possibleExtensionPointTypes = [SendToMarsEditCommand.self, SendToMicroBlogCommand.self, TwitterFeedProvider.self, RedditFeedProvider.self]
#else
possibleExtensionPointTypes = [SendToMarsEditCommand.self, SendToMicroBlogCommand.self, TwitterFeedProvider.self]
possibleExtensionPointTypes = [SendToMarsEditCommand.self, SendToMicroBlogCommand.self, TwitterFeedProvider.self, RedditFeedProvider.self]
#endif
#else
#if DEBUG
possibleExtensionPointTypes = [TwitterFeedProvider.self]
possibleExtensionPointTypes = [TwitterFeedProvider.self, RedditFeedProvider.self]
#else
possibleExtensionPointTypes = [TwitterFeedProvider.self]
possibleExtensionPointTypes = [TwitterFeedProvider.self, RedditFeedProvider.self]
#endif
#endif
loadExtensionPoints()
@ -115,6 +119,12 @@ private extension ExtensionPointManager {
} else {
return nil
}
case is RedditFeedProvider.Type:
if let tokenSuccess = tokenSuccess {
return RedditFeedProvider(tokenSuccess: tokenSuccess)
} else {
return nil
}
default:
assertionFailure("Unrecognized Extension Point Type.")
}
@ -131,6 +141,8 @@ private extension ExtensionPointManager {
#endif
case .twitter(let screenName):
return TwitterFeedProvider(screenName: screenName)
case .reddit(let username):
return RedditFeedProvider(username: username)
}
}

View File

@ -0,0 +1,30 @@
//
// RedditFeedProvider-Extensions.swift
// NetNewsWire
//
// Created by Maurice Parker on 5/2/20.
// Copyright © 2020 Ranchero Software. All rights reserved.
//
import Foundation
import Account
extension RedditFeedProvider: ExtensionPoint {
static var isSinglton = false
static var isDeveloperBuildRestricted = true
static var title = NSLocalizedString("Reddit", comment: "Reddit")
static var templateImage = AppAssets.extensionPointReddit
static var description: NSAttributedString = {
return RedditFeedProvider.makeAttrString("This extension enables you to subscribe to Reddit URL's as if they were RSS feeds. It only works with \(Account.defaultLocalAccountName) or iCloud accounts.")
}()
var extensionPointID: ExtensionPointIdentifer {
return ExtensionPointIdentifer.reddit(username)
}
var title: String {
return "/u/\(username)"
}
}

View File

@ -101,6 +101,10 @@ struct AppAssets {
UIImage(systemName: "square.and.pencil")!
}()
static var extensionPointReddit: RSImage = {
return RSImage(named: "extensionPointReddit")!
}()
static var extensionPointTwitter: UIImage = {
return UIImage(named: "extensionPointTwitter")!
}()

View File

@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "reddit-logo.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}