mirror of
https://github.com/Ranchero-Software/NetNewsWire
synced 2025-08-12 06:26:36 +00:00
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:
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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>
|
||||
@@ -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.")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user