Merge branch 'master' into feature/feed-wrangler

# Conflicts:
#	Frameworks/Account/Account.swift
#	Frameworks/Account/Account.xcodeproj/project.pbxproj
#	NetNewsWire.xcodeproj/project.pbxproj
#	submodules/RSCore
This commit is contained in:
Jonathan Bennett
2019-11-20 14:01:04 -05:00
355 changed files with 89315 additions and 5653 deletions

View File

@@ -108,9 +108,10 @@ extension AccountsAddViewController: NSTableViewDelegate {
accountsReaderAPIWindowController.runSheetOnWindow(self.view.window!)
accountsAddWindowController = accountsReaderAPIWindowController
case .feedly:
let accountsFeedlyWindowController = AccountsFeedlyWebWindowController()
accountsFeedlyWindowController.runSheetOnWindow(self.view.window!)
accountsAddWindowController = accountsFeedlyWindowController
let addAccount = OAuthAccountAuthorizationOperation(accountType: .feedly)
addAccount.delegate = self
addAccount.presentationAnchor = self.view.window!
OperationQueue.main.addOperation(addAccount)
default:
break
}
@@ -120,3 +121,23 @@ extension AccountsAddViewController: NSTableViewDelegate {
}
}
// MARK: OAuthAccountAuthorizationOperationDelegate
extension AccountsAddViewController: OAuthAccountAuthorizationOperationDelegate {
func oauthAccountAuthorizationOperation(_ operation: OAuthAccountAuthorizationOperation, didCreate account: Account) {
account.refreshAll { [weak self] result in
switch result {
case .success:
break
case .failure(let error):
self?.presentError(error)
}
}
}
func oauthAccountAuthorizationOperation(_ operation: OAuthAccountAuthorizationOperation, didFailWith error: Error) {
view.window?.presentError(error)
}
}

View File

@@ -28,6 +28,19 @@ final class AccountsDetailViewController: NSViewController, NSTextFieldDelegate
super.init(coder: coder)
}
private var hidesCredentialsButton: Bool {
guard let account = account else {
return true
}
switch account.type {
case .onMyMac,
.feedly:
return true
default:
return false
}
}
override func viewDidLoad() {
super.viewDidLoad()
@@ -35,7 +48,7 @@ final class AccountsDetailViewController: NSViewController, NSTextFieldDelegate
typeLabel.stringValue = account?.defaultName ?? ""
nameTextField.stringValue = account?.name ?? ""
activeButton.state = account?.isActive ?? false ? .on : .off
credentialsButton.isHidden = account?.type ?? .onMyMac == .onMyMac
credentialsButton.isHidden = hidesCredentialsButton
}
func controlTextDidEndEditing(_ obj: Notification) {
@@ -66,8 +79,6 @@ final class AccountsDetailViewController: NSViewController, NSTextFieldDelegate
accountsFreshRSSWindowController.account = account
accountsFreshRSSWindowController.runSheetOnWindow(self.view.window!)
accountsWindowController = accountsFreshRSSWindowController
case .feedly:
assertionFailure("Implement feedly logout window controller")
break
case .feedWrangler:
let accountsFeedWranglerWindowController = AccountsFeedWranglerWindowController()

View File

@@ -1,66 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14865.1" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14865.1"/>
<plugIn identifier="com.apple.WebKit2IBPlugin" version="14865.1"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="AccountsFeedlyWebWindowController" customModule="NetNewsWire" customModuleProvider="target">
<connections>
<outlet property="webView" destination="W4c-Xp-rpq" id="l11-5B-8yc"/>
<outlet property="window" destination="F0z-JX-Cv5" id="gIp-Ho-8D9"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="F0z-JX-Cv5">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="196" y="240" width="480" height="708"/>
<rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
<view key="contentView" id="se5-gp-TjO">
<rect key="frame" x="0.0" y="0.0" width="480" height="708"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<wkWebView wantsLayer="YES" translatesAutoresizingMaskIntoConstraints="NO" id="W4c-Xp-rpq">
<rect key="frame" x="0.0" y="61" width="480" height="647"/>
<wkWebViewConfiguration key="configuration">
<audiovisualMediaTypes key="mediaTypesRequiringUserActionForPlayback" none="YES"/>
<wkPreferences key="preferences"/>
</wkWebViewConfiguration>
<connections>
<outlet property="navigationDelegate" destination="-2" id="wAp-Oh-5EK"/>
</connections>
</wkWebView>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="PD2-Zk-3yM">
<rect key="frame" x="384" y="13" width="82" height="32"/>
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="IEi-N0-sbw">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
Gw
</string>
</buttonCell>
<connections>
<action selector="cancel:" target="-2" id="5BT-to-e4W"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="W4c-Xp-rpq" secondAttribute="trailing" id="D9j-IU-BZj"/>
<constraint firstAttribute="bottom" secondItem="PD2-Zk-3yM" secondAttribute="bottom" constant="20" symbolic="YES" id="Qdc-tu-9kO"/>
<constraint firstItem="W4c-Xp-rpq" firstAttribute="top" secondItem="se5-gp-TjO" secondAttribute="top" id="V7Q-kM-JDA"/>
<constraint firstAttribute="trailing" secondItem="PD2-Zk-3yM" secondAttribute="trailing" constant="20" symbolic="YES" id="bQS-L4-jbx"/>
<constraint firstItem="W4c-Xp-rpq" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" id="ec6-U0-t8X"/>
<constraint firstItem="PD2-Zk-3yM" firstAttribute="top" secondItem="W4c-Xp-rpq" secondAttribute="bottom" constant="20" symbolic="YES" id="zlA-8I-aKr"/>
</constraints>
</view>
<connections>
<outlet property="delegate" destination="-2" id="0bl-1N-AYu"/>
</connections>
<point key="canvasLocation" x="134" y="556"/>
</window>
</objects>
</document>

View File

@@ -1,119 +0,0 @@
//
// AccountsFeedlyWebWindowController.swift
// NetNewsWire
//
// Created by Kiel Gillard on 30/8/19.
// Copyright © 2019 Ranchero Software. All rights reserved.
//
import Cocoa
import Account
import WebKit
class AccountsFeedlyWebWindowController: NSWindowController, WKNavigationDelegate {
@IBOutlet private weak var webView: WKWebView!
private weak var hostWindow: NSWindow?
convenience init() {
self.init(windowNibName: NSNib.Name("AccountsFeedlyWeb"))
}
// MARK: API
func runSheetOnWindow(_ hostWindow: NSWindow, completionHandler handler: ((NSApplication.ModalResponse) -> Void)? = nil) {
self.hostWindow = hostWindow
hostWindow.beginSheet(window!, completionHandler: handler)
beginAuthorization()
}
// MARK: Requesting an Access Token
private let client = OAuthAuthorizationClient.feedlySandboxClient
private func beginAuthorization() {
let request = Account.oauthAuthorizationCodeGrantRequest(for: .feedly, client: client)
webView.load(request)
}
private func requestAccessToken(for response: OAuthAuthorizationResponse) {
Account.requestOAuthAccessToken(with: response, client: client, accountType: .feedly) { [weak self] result in
switch result {
case .success(let tokenResponse):
self?.saveAccount(for: tokenResponse)
case .failure(let error):
NSApplication.shared.presentError(error)
}
}
}
// MARK: Actions
@IBAction func cancel(_ sender: Any) {
hostWindow!.endSheet(window!, returnCode: NSApplication.ModalResponse.cancel)
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
do {
guard let url = navigationAction.request.url else { return }
let response = try OAuthAuthorizationResponse(url: url, client: client)
requestAccessToken(for: response)
// No point the web view trying to load this.
return decisionHandler(.cancel)
} catch let error as OAuthAuthorizationErrorResponse {
NSApplication.shared.presentError(error)
} catch {
print(error)
}
decisionHandler(.allow)
}
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
print(error)
}
private func saveAccount(for grant: OAuthAuthorizationGrant) {
// TODO: Find an already existing account for this username?
let account = AccountManager.shared.createAccount(type: .feedly)
do {
// Store the refresh token first because it sends this token to the account delegate.
if let token = grant.refreshToken {
try account.storeCredentials(token)
}
// Now store the access token because we want the account delegate to use it.
try account.storeCredentials(grant.accessToken)
self.hostWindow?.endSheet(self.window!, returnCode: NSApplication.ModalResponse.OK)
} catch {
NSApplication.shared.presentError(error)
}
}
}
private extension OAuthAuthorizationClient {
/// Models public sandbox API values found at:
/// https://groups.google.com/forum/#!topic/feedly-cloud/WwQWMgDmOuw
static var feedlySandboxClient: OAuthAuthorizationClient {
return OAuthAuthorizationClient(id: "sandbox",
redirectUri: "http://localhost",
state: nil,
secret: "ReVGXA6WekanCxbf")
}
/// Models private NetNewsWire client secrets.
/// https://developer.feedly.com/v3/auth/#authenticating-a-user-and-obtaining-an-auth-code
static var netNewsWireClient: OAuthAuthorizationClient {
fatalError("This app is not registered as a client with Feedly. Follow the URL in the code comments for this property.")
}
}