2019-03-18 03:01:28 +01:00
|
|
|
|
//
|
|
|
|
|
// AccountsPreferencesViewController.swift
|
|
|
|
|
// NetNewsWire
|
|
|
|
|
//
|
|
|
|
|
// Created by Brent Simmons on 3/17/19.
|
|
|
|
|
// Copyright © 2019 Ranchero Software. All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
|
2019-03-20 06:41:09 +01:00
|
|
|
|
import AppKit
|
2019-03-24 20:33:43 +01:00
|
|
|
|
import Account
|
2020-10-28 16:19:42 +01:00
|
|
|
|
import SwiftUI
|
|
|
|
|
import RSCore
|
|
|
|
|
|
2020-10-30 02:30:11 +01:00
|
|
|
|
// MARK: - AccountsPreferencesAddAccountDelegate
|
|
|
|
|
protocol AccountsPreferencesAddAccountDelegate {
|
|
|
|
|
func presentSheetForAccount(_ accountType: AccountType)
|
|
|
|
|
}
|
2019-03-20 06:41:09 +01:00
|
|
|
|
|
2020-10-30 02:30:11 +01:00
|
|
|
|
// MARK: - AccountsPreferencesViewController
|
2019-03-20 06:41:09 +01:00
|
|
|
|
final class AccountsPreferencesViewController: NSViewController {
|
|
|
|
|
|
2019-04-30 13:38:18 +02:00
|
|
|
|
@IBOutlet weak var tableView: NSTableView!
|
|
|
|
|
@IBOutlet weak var detailView: NSView!
|
2019-05-02 01:26:23 +02:00
|
|
|
|
@IBOutlet weak var deleteButton: NSButton!
|
2020-10-28 16:19:42 +01:00
|
|
|
|
var addAccountDelegate: AccountsPreferencesAddAccountDelegate?
|
2020-10-30 10:25:33 +01:00
|
|
|
|
var addAccountWindowController: NSWindowController?
|
2019-04-30 13:38:18 +02:00
|
|
|
|
|
2019-03-24 20:33:43 +01:00
|
|
|
|
private var sortedAccounts = [Account]()
|
|
|
|
|
|
2019-04-30 13:38:18 +02:00
|
|
|
|
override func viewDidLoad() {
|
|
|
|
|
super.viewDidLoad()
|
2019-05-01 17:28:13 +02:00
|
|
|
|
|
|
|
|
|
updateSortedAccounts()
|
|
|
|
|
tableView.delegate = self
|
|
|
|
|
tableView.dataSource = self
|
2020-10-28 16:19:42 +01:00
|
|
|
|
addAccountDelegate = self
|
2019-05-01 17:28:13 +02:00
|
|
|
|
|
2019-04-30 13:38:18 +02:00
|
|
|
|
NotificationCenter.default.addObserver(self, selector: #selector(displayNameDidChange(_:)), name: .DisplayNameDidChange, object: nil)
|
2019-09-08 16:43:51 +02:00
|
|
|
|
NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange(_:)), name: .UserDidAddAccount, object: nil)
|
|
|
|
|
NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange(_:)), name: .UserDidDeleteAccount, object: nil)
|
|
|
|
|
|
2019-05-01 17:28:13 +02:00
|
|
|
|
|
2019-05-27 06:38:13 +02:00
|
|
|
|
// Fix tableView frame — for some reason IB wants it 1pt wider than the clip view. This leads to unwanted horizontal scrolling.
|
|
|
|
|
var rTable = tableView.frame
|
|
|
|
|
rTable.size.width = tableView.superview!.frame.size.width
|
|
|
|
|
tableView.frame = rTable
|
2020-10-30 02:30:11 +01:00
|
|
|
|
|
2020-11-04 03:35:53 +01:00
|
|
|
|
hideController()
|
2019-04-30 13:38:18 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-01 17:28:13 +02:00
|
|
|
|
@IBAction func addAccount(_ sender: Any) {
|
2020-10-28 16:19:42 +01:00
|
|
|
|
let controller = NSHostingController(rootView: AddAccountsView(delegate: self))
|
|
|
|
|
controller.rootView.parent = controller
|
|
|
|
|
presentAsSheet(controller)
|
2019-04-30 13:38:18 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-01 17:28:13 +02:00
|
|
|
|
@IBAction func removeAccount(_ sender: Any) {
|
2019-05-02 13:57:45 +02:00
|
|
|
|
|
2019-05-02 01:26:23 +02:00
|
|
|
|
guard tableView.selectedRow != -1 else {
|
|
|
|
|
return
|
|
|
|
|
}
|
2019-05-02 13:57:45 +02:00
|
|
|
|
|
2019-05-10 15:42:44 +02:00
|
|
|
|
let acctName = sortedAccounts[tableView.selectedRow].nameForDisplay
|
|
|
|
|
|
|
|
|
|
let alert = NSAlert()
|
|
|
|
|
alert.alertStyle = .warning
|
|
|
|
|
let deletePrompt = NSLocalizedString("Delete", comment: "Delete")
|
2019-10-22 12:58:05 +02:00
|
|
|
|
alert.messageText = "\(deletePrompt) “\(acctName)”?"
|
2019-12-08 03:10:32 +01:00
|
|
|
|
alert.informativeText = NSLocalizedString("Are you sure you want to delete the account “\(acctName)”? This cannot be undone.", comment: "Delete text")
|
2019-05-10 15:42:44 +02:00
|
|
|
|
|
|
|
|
|
alert.addButton(withTitle: NSLocalizedString("Delete", comment: "Delete Account"))
|
|
|
|
|
alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "Cancel Delete Account"))
|
|
|
|
|
|
|
|
|
|
alert.beginSheetModal(for: view.window!) { [weak self] result in
|
|
|
|
|
if result == NSApplication.ModalResponse.alertFirstButtonReturn {
|
|
|
|
|
guard let self = self else { return }
|
|
|
|
|
AccountManager.shared.deleteAccount(self.sortedAccounts[self.tableView.selectedRow])
|
2020-12-09 21:50:25 +01:00
|
|
|
|
self.hideController()
|
2019-05-02 13:57:45 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-30 13:38:18 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@objc func displayNameDidChange(_ note: Notification) {
|
2019-05-01 17:28:13 +02:00
|
|
|
|
updateSortedAccounts()
|
2019-04-30 13:38:18 +02:00
|
|
|
|
tableView.reloadData()
|
2019-03-24 20:33:43 +01:00
|
|
|
|
}
|
2019-04-30 13:38:18 +02:00
|
|
|
|
|
2019-09-08 16:43:51 +02:00
|
|
|
|
@objc func accountsDidChange(_ note: Notification) {
|
2019-05-01 19:37:13 +02:00
|
|
|
|
updateSortedAccounts()
|
|
|
|
|
tableView.reloadData()
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-24 20:33:43 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MARK: - NSTableViewDataSource
|
|
|
|
|
|
|
|
|
|
extension AccountsPreferencesViewController: NSTableViewDataSource {
|
|
|
|
|
|
|
|
|
|
func numberOfRows(in tableView: NSTableView) -> Int {
|
|
|
|
|
return sortedAccounts.count
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
|
|
|
|
|
return sortedAccounts[row]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MARK: - NSTableViewDelegate
|
|
|
|
|
|
|
|
|
|
extension AccountsPreferencesViewController: NSTableViewDelegate {
|
|
|
|
|
|
|
|
|
|
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
|
2020-11-20 00:21:29 +01:00
|
|
|
|
if let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "Cell"), owner: nil) as? AccountCell {
|
|
|
|
|
|
2019-05-01 13:49:50 +02:00
|
|
|
|
let account = sortedAccounts[row]
|
|
|
|
|
cell.textField?.stringValue = account.nameForDisplay
|
2019-11-21 18:28:08 +01:00
|
|
|
|
cell.imageView?.image = account.smallIcon?.image
|
2020-11-20 00:21:29 +01:00
|
|
|
|
|
|
|
|
|
if account.type == .feedbin {
|
|
|
|
|
cell.isImageTemplateCapable = false
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-24 20:33:43 +01:00
|
|
|
|
return cell
|
|
|
|
|
}
|
2019-04-30 13:38:18 +02:00
|
|
|
|
return nil
|
2019-03-24 20:33:43 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func tableViewSelectionDidChange(_ notification: Notification) {
|
2019-04-30 13:38:18 +02:00
|
|
|
|
|
|
|
|
|
let selectedRow = tableView.selectedRow
|
2019-05-02 01:26:23 +02:00
|
|
|
|
if tableView.selectedRow == -1 {
|
|
|
|
|
deleteButton.isEnabled = false
|
2020-10-30 02:30:11 +01:00
|
|
|
|
hideController()
|
2019-04-30 13:38:18 +02:00
|
|
|
|
return
|
2019-05-02 01:26:23 +02:00
|
|
|
|
} else {
|
|
|
|
|
deleteButton.isEnabled = true
|
2019-04-30 13:38:18 +02:00
|
|
|
|
}
|
2019-05-02 01:26:23 +02:00
|
|
|
|
|
2019-04-30 13:38:18 +02:00
|
|
|
|
let account = sortedAccounts[selectedRow]
|
2019-05-02 01:26:23 +02:00
|
|
|
|
if AccountManager.shared.defaultAccount == account {
|
|
|
|
|
deleteButton.isEnabled = false
|
|
|
|
|
}
|
2019-04-30 13:38:18 +02:00
|
|
|
|
|
2019-05-01 18:05:55 +02:00
|
|
|
|
let controller = AccountsDetailViewController(account: account)
|
2019-05-01 17:28:13 +02:00
|
|
|
|
showController(controller)
|
|
|
|
|
|
2019-03-24 20:33:43 +01:00
|
|
|
|
}
|
2019-04-30 13:38:18 +02:00
|
|
|
|
|
2019-03-24 20:33:43 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-10-28 16:19:42 +01:00
|
|
|
|
extension AccountsPreferencesViewController: AccountsPreferencesAddAccountDelegate {
|
|
|
|
|
func presentSheetForAccount(_ accountType: AccountType) {
|
|
|
|
|
switch accountType {
|
|
|
|
|
case .onMyMac:
|
|
|
|
|
let accountsAddLocalWindowController = AccountsAddLocalWindowController()
|
|
|
|
|
accountsAddLocalWindowController.runSheetOnWindow(self.view.window!)
|
2020-10-30 10:25:33 +01:00
|
|
|
|
addAccountWindowController = accountsAddLocalWindowController
|
2020-10-28 16:19:42 +01:00
|
|
|
|
case .cloudKit:
|
|
|
|
|
let accountsAddCloudKitWindowController = AccountsAddCloudKitWindowController()
|
|
|
|
|
accountsAddCloudKitWindowController.runSheetOnWindow(self.view.window!) { response in
|
|
|
|
|
if response == NSApplication.ModalResponse.OK {
|
2020-10-29 23:16:28 +01:00
|
|
|
|
self.tableView.reloadData()
|
2020-10-28 16:19:42 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-10-30 10:25:33 +01:00
|
|
|
|
addAccountWindowController = accountsAddCloudKitWindowController
|
2020-10-28 16:19:42 +01:00
|
|
|
|
case .feedbin:
|
|
|
|
|
let accountsFeedbinWindowController = AccountsFeedbinWindowController()
|
|
|
|
|
accountsFeedbinWindowController.runSheetOnWindow(self.view.window!)
|
2020-10-30 10:25:33 +01:00
|
|
|
|
addAccountWindowController = accountsFeedbinWindowController
|
2020-10-28 16:19:42 +01:00
|
|
|
|
case .feedWrangler:
|
|
|
|
|
let accountsFeedWranglerWindowController = AccountsFeedWranglerWindowController()
|
|
|
|
|
accountsFeedWranglerWindowController.runSheetOnWindow(self.view.window!)
|
2020-10-30 10:25:33 +01:00
|
|
|
|
addAccountWindowController = accountsFeedWranglerWindowController
|
|
|
|
|
case .freshRSS, .inoreader, .bazQux, .theOldReader:
|
2020-10-28 16:19:42 +01:00
|
|
|
|
let accountsReaderAPIWindowController = AccountsReaderAPIWindowController()
|
2020-10-30 10:25:33 +01:00
|
|
|
|
accountsReaderAPIWindowController.accountType = accountType
|
2020-10-28 16:19:42 +01:00
|
|
|
|
accountsReaderAPIWindowController.runSheetOnWindow(self.view.window!)
|
2020-10-30 10:25:33 +01:00
|
|
|
|
addAccountWindowController = accountsReaderAPIWindowController
|
2020-10-28 16:19:42 +01:00
|
|
|
|
case .feedly:
|
|
|
|
|
let addAccount = OAuthAccountAuthorizationOperation(accountType: .feedly)
|
2020-10-29 23:16:28 +01:00
|
|
|
|
addAccount.delegate = self
|
2020-10-28 16:19:42 +01:00
|
|
|
|
addAccount.presentationAnchor = self.view.window!
|
|
|
|
|
runAwaitingFeedlyLoginAlertModal(forLifetimeOf: addAccount)
|
|
|
|
|
MainThreadOperationQueue.shared.add(addAccount)
|
|
|
|
|
case .newsBlur:
|
|
|
|
|
let accountsNewsBlurWindowController = AccountsNewsBlurWindowController()
|
|
|
|
|
accountsNewsBlurWindowController.runSheetOnWindow(self.view.window!)
|
2020-10-30 10:25:33 +01:00
|
|
|
|
addAccountWindowController = accountsNewsBlurWindowController
|
2020-10-28 16:19:42 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func runAwaitingFeedlyLoginAlertModal(forLifetimeOf operation: OAuthAccountAuthorizationOperation) {
|
|
|
|
|
let alert = NSAlert()
|
|
|
|
|
alert.alertStyle = .informational
|
|
|
|
|
alert.messageText = NSLocalizedString("Waiting for access to Feedly",
|
|
|
|
|
comment: "Alert title when adding a Feedly account and waiting for authorization from the user.")
|
|
|
|
|
|
2020-12-19 03:43:13 +01:00
|
|
|
|
alert.informativeText = NSLocalizedString("A web browser will open the Feedly login for you to authorize access.",
|
2020-10-28 16:19:42 +01:00
|
|
|
|
comment: "Alert informative text when adding a Feedly account and waiting for authorization from the user.")
|
|
|
|
|
|
|
|
|
|
alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "Cancel"))
|
|
|
|
|
|
|
|
|
|
let attachedWindow = self.view.window!
|
|
|
|
|
|
|
|
|
|
alert.beginSheetModal(for: attachedWindow) { response in
|
|
|
|
|
if response == .alertFirstButtonReturn {
|
|
|
|
|
operation.cancel()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
operation.completionBlock = { _ in
|
|
|
|
|
guard alert.window.isVisible else {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
attachedWindow.endSheet(alert.window)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-24 20:33:43 +01:00
|
|
|
|
// MARK: - Private
|
|
|
|
|
|
|
|
|
|
private extension AccountsPreferencesViewController {
|
|
|
|
|
|
|
|
|
|
func updateSortedAccounts() {
|
|
|
|
|
sortedAccounts = AccountManager.shared.sortedAccounts
|
2019-03-20 06:41:09 +01:00
|
|
|
|
}
|
2019-04-30 13:38:18 +02:00
|
|
|
|
|
2019-05-01 17:28:13 +02:00
|
|
|
|
func showController(_ controller: NSViewController) {
|
2020-10-30 17:21:49 +01:00
|
|
|
|
hideController()
|
2020-11-02 04:08:54 +01:00
|
|
|
|
|
2019-05-01 17:28:13 +02:00
|
|
|
|
addChild(controller)
|
|
|
|
|
controller.view.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
detailView.addSubview(controller.view)
|
2020-01-09 06:40:21 +01:00
|
|
|
|
detailView.addFullSizeConstraints(forSubview: controller.view)
|
2019-04-30 13:38:18 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-10-30 02:30:11 +01:00
|
|
|
|
func hideController() {
|
|
|
|
|
if let controller = children.first {
|
|
|
|
|
children.removeAll()
|
|
|
|
|
controller.view.removeFromSuperview()
|
|
|
|
|
}
|
2020-11-02 04:08:54 +01:00
|
|
|
|
|
|
|
|
|
if tableView.selectedRow == -1 {
|
|
|
|
|
var helpText = ""
|
|
|
|
|
if sortedAccounts.count == 0 {
|
|
|
|
|
helpText = NSLocalizedString("Add an account by clicking the + button.", comment: "Add Account Explainer")
|
|
|
|
|
} else {
|
|
|
|
|
helpText = NSLocalizedString("Select an account or add a new account by clicking the + button.", comment: "Add Account Explainer")
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-04 03:35:53 +01:00
|
|
|
|
let textHostingController = NSHostingController(rootView:
|
|
|
|
|
AddAccountHelpView(delegate: addAccountDelegate, helpText: helpText))
|
2020-11-02 04:08:54 +01:00
|
|
|
|
addChild(textHostingController)
|
|
|
|
|
textHostingController.view.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
detailView.addSubview(textHostingController.view)
|
2020-11-03 02:41:34 +01:00
|
|
|
|
detailView.addConstraints([
|
|
|
|
|
NSLayoutConstraint(item: textHostingController.view, attribute: .top, relatedBy: .equal, toItem: detailView, attribute: .top, multiplier: 1, constant: 1),
|
|
|
|
|
NSLayoutConstraint(item: textHostingController.view, attribute: .bottom, relatedBy: .equal, toItem: detailView, attribute: .bottom, multiplier: 1, constant: -deleteButton.frame.height),
|
|
|
|
|
NSLayoutConstraint(item: textHostingController.view, attribute: .width, relatedBy: .equal, toItem: detailView, attribute: .width, multiplier: 1, constant: 1)
|
|
|
|
|
])
|
|
|
|
|
|
2020-11-02 04:08:54 +01:00
|
|
|
|
}
|
2020-10-30 02:30:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-20 06:41:09 +01:00
|
|
|
|
}
|
2020-10-28 16:19:42 +01:00
|
|
|
|
|
2020-10-29 23:16:28 +01:00
|
|
|
|
extension AccountsPreferencesViewController: OAuthAccountAuthorizationOperationDelegate {
|
|
|
|
|
|
|
|
|
|
func oauthAccountAuthorizationOperation(_ operation: OAuthAccountAuthorizationOperation, didCreate account: Account) {
|
|
|
|
|
// `OAuthAccountAuthorizationOperation` is using `ASWebAuthenticationSession` which bounces the user
|
|
|
|
|
// to their browser on macOS for authorizing NetNewsWire to access the user's Feedly account.
|
|
|
|
|
// When this authorization is granted, the browser remains the foreground app which is unfortunate
|
|
|
|
|
// because the user probably wants to see the result of authorizing NetNewsWire to act on their behalf.
|
|
|
|
|
NSApp.activate(ignoringOtherApps: true)
|
|
|
|
|
|
|
|
|
|
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) {
|
|
|
|
|
// `OAuthAccountAuthorizationOperation` is using `ASWebAuthenticationSession` which bounces the user
|
|
|
|
|
// to their browser on macOS for authorizing NetNewsWire to access the user's Feedly account.
|
|
|
|
|
NSApp.activate(ignoringOtherApps: true)
|
|
|
|
|
|
|
|
|
|
view.window?.presentError(error)
|
|
|
|
|
}
|
|
|
|
|
}
|