diff --git a/Frameworks/Account/Account.swift b/Frameworks/Account/Account.swift index f5bc4f199..ed6365030 100644 --- a/Frameworks/Account/Account.swift +++ b/Frameworks/Account/Account.swift @@ -67,6 +67,21 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, public static let feeds = "feeds" // AccountDidDownloadArticles, StatusesDidChange } + public static let defaultLocalAccountName: String = { + let defaultName: String + #if os(macOS) + defaultName = NSLocalizedString("On My Mac", comment: "Account name") + #else + if UIDevice.current.userInterfaceIdiom == .pad { + defaultName = NSLocalizedString("On My iPad", comment: "Account name") + } else { + defaultName = NSLocalizedString("On My iPhone", comment: "Account name") + } + #endif + + return defaultName + }() + public let accountID: String public let type: AccountType public var nameForDisplay: String { @@ -220,15 +235,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, switch type { case .onMyMac: - #if os(macOS) - defaultName = NSLocalizedString("On My Mac", comment: "Account name") - #else - if UIDevice.current.userInterfaceIdiom == .pad { - defaultName = NSLocalizedString("On My iPad", comment: "Account name") - } else { - defaultName = NSLocalizedString("On My iPhone", comment: "Account name") - } - #endif + defaultName = Account.defaultLocalAccountName case .feedly: defaultName = "Feedly" case .feedbin: diff --git a/Mac/Preferences/Accounts/AccountsAddLocal.xib b/Mac/Preferences/Accounts/AccountsAddLocal.xib index ea2ffd4b8..c035af932 100644 --- a/Mac/Preferences/Accounts/AccountsAddLocal.xib +++ b/Mac/Preferences/Accounts/AccountsAddLocal.xib @@ -7,6 +7,7 @@ + @@ -17,13 +18,13 @@ - + - + @@ -34,7 +35,7 @@ - + diff --git a/Mac/Preferences/Accounts/AccountsAddLocalWindowController.swift b/Mac/Preferences/Accounts/AccountsAddLocalWindowController.swift index f89ee0e4a..753fc4e75 100644 --- a/Mac/Preferences/Accounts/AccountsAddLocalWindowController.swift +++ b/Mac/Preferences/Accounts/AccountsAddLocalWindowController.swift @@ -11,14 +11,21 @@ import Account class AccountsAddLocalWindowController: NSWindowController { - @IBOutlet weak var nameTextField: NSTextField! - + @IBOutlet private weak var nameTextField: NSTextField! + @IBOutlet private weak var localAccountNameTextField: NSTextField! + private weak var hostWindow: NSWindow? convenience init() { self.init(windowNibName: NSNib.Name("AccountsAddLocal")) } + override func windowDidLoad() { + super.windowDidLoad() + + localAccountNameTextField.stringValue = Account.defaultLocalAccountName + } + // MARK: API func runSheetOnWindow(_ hostWindow: NSWindow) { diff --git a/Mac/Preferences/Accounts/AccountsAddViewController.swift b/Mac/Preferences/Accounts/AccountsAddViewController.swift index 92c9d6ddf..a77a4bdd3 100644 --- a/Mac/Preferences/Accounts/AccountsAddViewController.swift +++ b/Mac/Preferences/Accounts/AccountsAddViewController.swift @@ -7,6 +7,7 @@ // import AppKit +import Account class AccountsAddViewController: NSViewController { @@ -57,7 +58,7 @@ extension AccountsAddViewController: NSTableViewDelegate { if let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "Cell"), owner: nil) as? AccountsAddTableCellView { switch row { case 0: - cell.accountNameLabel?.stringValue = NSLocalizedString("On My Mac", comment: "Local") + cell.accountNameLabel?.stringValue = Account.defaultLocalAccountName cell.accountImageView?.image = AppImages.accountLocal case 1: cell.accountNameLabel?.stringValue = NSLocalizedString("Feedbin", comment: "Feedbin") diff --git a/iOS/Add/Add.storyboard b/iOS/Add/Add.storyboard index 95c8fb579..12745974f 100644 --- a/iOS/Add/Add.storyboard +++ b/iOS/Add/Add.storyboard @@ -14,7 +14,7 @@ - + @@ -28,10 +28,10 @@ - + - + @@ -50,10 +50,10 @@ - + - + @@ -76,39 +76,38 @@ - - - - - - - - + + + + + + - - + + - + - + @@ -242,7 +241,7 @@ - + @@ -256,10 +255,10 @@ - + - + @@ -282,38 +281,38 @@ - - - + + - + - - + + - + - + diff --git a/iOS/Add/AddContainerViewController.swift b/iOS/Add/AddContainerViewController.swift index 28ed46514..16d92719c 100644 --- a/iOS/Add/AddContainerViewController.swift +++ b/iOS/Add/AddContainerViewController.swift @@ -23,6 +23,8 @@ protocol AddContainerViewControllerChildDelegate: UIViewController { class AddContainerViewController: UIViewController { + static let preferredContentSizeForFormSheetDisplay = CGSize(width: 460.0, height: 400.0) + @IBOutlet weak var cancelButton: UIBarButtonItem! @IBOutlet weak var activityIndicatorView: UIActivityIndicatorView! @IBOutlet weak var addButton: UIBarButtonItem! diff --git a/iOS/Add/AddFeedViewController.swift b/iOS/Add/AddFeedViewController.swift index 13dc65c26..b2b84cee0 100644 --- a/iOS/Add/AddFeedViewController.swift +++ b/iOS/Add/AddFeedViewController.swift @@ -14,12 +14,15 @@ import RSParser class AddFeedViewController: UITableViewController, AddContainerViewControllerChild { - @IBOutlet weak var urlTextField: UITextField! - @IBOutlet weak var nameTextField: UITextField! - @IBOutlet weak var folderPickerView: UIPickerView! - @IBOutlet weak var folderLabel: UILabel! + @IBOutlet private weak var urlTextField: UITextField! + @IBOutlet private weak var nameTextField: UITextField! + @IBOutlet private weak var folderPickerView: UIPickerView! + @IBOutlet private weak var folderLabel: UILabel! - private var pickerData: AddFeedFolderPickerData! + private lazy var pickerData: AddFeedFolderPickerData = AddFeedFolderPickerData() + private var shouldDisplayPicker: Bool { + return pickerData.containerNames.count > 1 + } private var userCancelled = false @@ -34,19 +37,24 @@ class AddFeedViewController: UITableViewController, AddContainerViewControllerCh urlTextField.autocorrectionType = .no urlTextField.autocapitalizationType = .none urlTextField.text = initialFeed + urlTextField.delegate = self if initialFeed != nil { delegate?.readyToAdd(state: true) } nameTextField.text = initialFeedName + nameTextField.delegate = self + folderLabel.text = pickerData.containerNames.first + + if shouldDisplayPicker { + folderPickerView.dataSource = self + folderPickerView.delegate = self + folderPickerView.showsSelectionIndicator = true + } else { + folderPickerView.isHidden = true + } - pickerData = AddFeedFolderPickerData() - folderPickerView.dataSource = self - folderPickerView.delegate = self - folderPickerView.showsSelectionIndicator = true - folderLabel.text = pickerData.containerNames[0] - // I couldn't figure out the gap at the top of the UITableView, so I took a hammer to it. tableView.contentInset = UIEdgeInsets(top: -28, left: 0, bottom: 0, right: 0) @@ -117,6 +125,16 @@ class AddFeedViewController: UITableViewController, AddContainerViewControllerCh delegate?.readyToAdd(state: urlTextField.text?.rs_stringMayBeURL() ?? false) } + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + let defaultNumberOfRows = super.tableView(tableView, numberOfRowsInSection: section) + if section == 1 && !shouldDisplayPicker { + return defaultNumberOfRows - 1 + } + + return defaultNumberOfRows + } + + } extension AddFeedViewController: UIPickerViewDataSource, UIPickerViewDelegate { @@ -200,3 +218,12 @@ private extension AddFeedViewController { } } + +extension AddFeedViewController: UITextFieldDelegate { + + func textFieldShouldReturn(_ textField: UITextField) -> Bool { + textField.resignFirstResponder() + return true + } + +} diff --git a/iOS/Add/AddFolderViewController.swift b/iOS/Add/AddFolderViewController.swift index 7bca799e3..3e7849937 100644 --- a/iOS/Add/AddFolderViewController.swift +++ b/iOS/Add/AddFolderViewController.swift @@ -12,9 +12,13 @@ import RSCore class AddFolderViewController: UITableViewController, AddContainerViewControllerChild { - @IBOutlet weak var nameTextField: UITextField! - @IBOutlet weak var accountLabel: UILabel! - @IBOutlet weak var accountPickerView: UIPickerView! + @IBOutlet private weak var nameTextField: UITextField! + @IBOutlet private weak var accountLabel: UILabel! + @IBOutlet private weak var accountPickerView: UIPickerView! + + private var shouldDisplayPicker: Bool { + return accounts.count > 1 + } private var accounts: [Account]! @@ -25,10 +29,16 @@ class AddFolderViewController: UITableViewController, AddContainerViewController super.viewDidLoad() accounts = AccountManager.shared.sortedActiveAccounts + + nameTextField.delegate = self accountLabel.text = (accounts[0] as DisplayNameProvider).nameForDisplay - accountPickerView.dataSource = self - accountPickerView.delegate = self + if shouldDisplayPicker { + accountPickerView.dataSource = self + accountPickerView.delegate = self + } else { + accountPickerView.isHidden = true + } // I couldn't figure out the gap at the top of the UITableView, so I took a hammer to it. tableView.contentInset = UIEdgeInsets(top: -28, left: 0, bottom: 0, right: 0) @@ -53,6 +63,14 @@ class AddFolderViewController: UITableViewController, AddContainerViewController delegate?.readyToAdd(state: !(nameTextField.text?.isEmpty ?? false)) } + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + let defaultNumberOfRows = super.tableView(tableView, numberOfRowsInSection: section) + if section == 1 && !shouldDisplayPicker { + return defaultNumberOfRows - 1 + } + + return defaultNumberOfRows + } } extension AddFolderViewController: UIPickerViewDataSource, UIPickerViewDelegate { @@ -74,3 +92,12 @@ extension AddFolderViewController: UIPickerViewDataSource, UIPickerViewDelegate } } + +extension AddFolderViewController: UITextFieldDelegate { + + func textFieldShouldReturn(_ textField: UITextField) -> Bool { + textField.resignFirstResponder() + return true + } + +} diff --git a/iOS/Base.lproj/Main.storyboard b/iOS/Base.lproj/Main.storyboard index a72b987e5..3911beb62 100644 --- a/iOS/Base.lproj/Main.storyboard +++ b/iOS/Base.lproj/Main.storyboard @@ -259,6 +259,7 @@ + diff --git a/iOS/MasterFeed/MasterFeedViewController.swift b/iOS/MasterFeed/MasterFeedViewController.swift index 3a2e69d3a..cfb6fb1c2 100644 --- a/iOS/MasterFeed/MasterFeedViewController.swift +++ b/iOS/MasterFeed/MasterFeedViewController.swift @@ -14,7 +14,8 @@ import RSTree class MasterFeedViewController: ProgressTableViewController, UndoableCommandRunner { - @IBOutlet weak var markAllAsReadButton: UIBarButtonItem! + @IBOutlet private weak var markAllAsReadButton: UIBarButtonItem! + @IBOutlet private weak var addNewItemButton: UIBarButtonItem! var undoableCommands = [UndoableCommand]() @@ -35,7 +36,9 @@ class MasterFeedViewController: ProgressTableViewController, UndoableCommandRunn NotificationCenter.default.addObserver(self, selector: #selector(faviconDidBecomeAvailable(_:)), name: .FaviconDidBecomeAvailable, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(feedSettingDidChange(_:)), name: .FeedSettingDidChange, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(userDidAddFeed(_:)), name: .UserDidAddFeed, object: nil) - + NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange(_:)), name: .AccountsDidChange, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(accountStateDidChange(_:)), name: .AccountStateDidChange, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(backingStoresDidRebuild(_:)), name: .BackingStoresDidRebuild, object: navState) NotificationCenter.default.addObserver(self, selector: #selector(masterSelectionDidChange(_:)), name: .MasterSelectionDidChange, object: navState) NotificationCenter.default.addObserver(self, selector: #selector(contentSizeCategoryDidChange), name: UIContentSizeCategory.didChangeNotification, object: nil) @@ -48,7 +51,7 @@ class MasterFeedViewController: ProgressTableViewController, UndoableCommandRunn } override func viewWillAppear(_ animated: Bool) { - clearsSelectionOnViewWillAppear = splitViewController!.isCollapsed + clearsSelectionOnViewWillAppear = true navigationController?.title = NSLocalizedString("Feeds", comment: "Feeds") super.viewWillAppear(animated) } @@ -140,7 +143,15 @@ class MasterFeedViewController: ProgressTableViewController, UndoableCommandRunn } } - + + @objc func accountsDidChange(_ notification: Notification) { + updateUI() + } + + @objc func accountStateDidChange(_ notification: Notification) { + updateUI() + } + @objc func masterSelectionDidChange(_ note: Notification) { if let indexPath = navState.currentMasterIndexPath { if tableView.indexPathForSelectedRow != indexPath { @@ -433,8 +444,10 @@ class MasterFeedViewController: ProgressTableViewController, UndoableCommandRunn @IBAction func add(_ sender: UIBarButtonItem) { let addViewController = UIStoryboard.add.instantiateInitialViewController()! - addViewController.modalPresentationStyle = .popover + addViewController.modalPresentationStyle = .formSheet + addViewController.preferredContentSize = AddContainerViewController.preferredContentSizeForFormSheetDisplay addViewController.popoverPresentationController?.barButtonItem = sender + self.present(addViewController, animated: true) } @@ -596,6 +609,7 @@ private extension MasterFeedViewController { func updateUI() { markAllAsReadButton.isEnabled = navState.isAnyUnreadAvailable + addNewItemButton.isEnabled = !AccountManager.shared.activeAccounts.isEmpty } func configureCellsForRepresentedObject(_ representedObject: AnyObject) { diff --git a/iOS/Settings/AddAccountViewController.swift b/iOS/Settings/AddAccountViewController.swift index d05a15f37..c4e7a6285 100644 --- a/iOS/Settings/AddAccountViewController.swift +++ b/iOS/Settings/AddAccountViewController.swift @@ -6,6 +6,7 @@ // Copyright © 2019 Ranchero Software. All rights reserved. // +import Account import UIKit protocol AddAccountDismissDelegate: UIViewController { @@ -14,6 +15,14 @@ protocol AddAccountDismissDelegate: UIViewController { class AddAccountViewController: UITableViewController, AddAccountDismissDelegate { + @IBOutlet private weak var localAccountNameLabel: UILabel! + + override func viewDidLoad() { + super.viewDidLoad() + + localAccountNameLabel.text = Account.defaultLocalAccountName + } + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { switch indexPath.row { case 0: diff --git a/iOS/Settings/AddLocalAccountViewController.swift b/iOS/Settings/AddLocalAccountViewController.swift index 3c09f0b03..6973a48d8 100644 --- a/iOS/Settings/AddLocalAccountViewController.swift +++ b/iOS/Settings/AddLocalAccountViewController.swift @@ -11,9 +11,18 @@ import Account class AddLocalAccountViewController: UIViewController { + @IBOutlet private weak var localAccountNameLabel: UILabel! @IBOutlet weak var nameTextField: UITextField! - weak var delegate: AddAccountDismissDelegate? + weak var delegate: AddAccountDismissDelegate? + + override func viewDidLoad() { + super.viewDidLoad() + + localAccountNameLabel.text = Account.defaultLocalAccountName + nameTextField.delegate = self + } + @IBAction func cancel(_ sender: Any) { dismiss(animated: true) } @@ -26,3 +35,12 @@ class AddLocalAccountViewController: UIViewController { } } + +extension AddLocalAccountViewController: UITextFieldDelegate { + + func textFieldShouldReturn(_ textField: UITextField) -> Bool { + textField.resignFirstResponder() + return true + } + +} diff --git a/iOS/Settings/DetailAccountViewController.swift b/iOS/Settings/DetailAccountViewController.swift index d1edb4bf0..9ab945120 100644 --- a/iOS/Settings/DetailAccountViewController.swift +++ b/iOS/Settings/DetailAccountViewController.swift @@ -21,6 +21,7 @@ class DetailAccountViewController: UITableViewController { guard let account = account else { return } nameTextField.text = account.name + nameTextField.delegate = self activeSwitch.isOn = account.isActive } @@ -28,6 +29,10 @@ class DetailAccountViewController: UITableViewController { account?.name = nameTextField.text account?.isActive = activeSwitch.isOn } + +} + +extension DetailAccountViewController { override func numberOfSections(in tableView: UITableView) -> Int { if account == AccountManager.shared.defaultAccount { @@ -46,6 +51,14 @@ class DetailAccountViewController: UITableViewController { return cell } + override func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool { + if indexPath.section == 1 { + return true + } + + return false + } + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if indexPath.section == 1 { deleteAccount() @@ -79,3 +92,12 @@ private extension DetailAccountViewController { } } + +extension DetailAccountViewController: UITextFieldDelegate { + + func textFieldShouldReturn(_ textField: UITextField) -> Bool { + textField.resignFirstResponder() + return true + } + +} diff --git a/iOS/Settings/Settings.storyboard b/iOS/Settings/Settings.storyboard index 77f35406c..7a67ec398 100644 --- a/iOS/Settings/Settings.storyboard +++ b/iOS/Settings/Settings.storyboard @@ -127,12 +127,29 @@ + + + + + + + + + + + - + @@ -153,7 +170,7 @@ - + @@ -183,7 +200,7 @@ - + @@ -211,7 +228,7 @@ - + @@ -234,23 +251,6 @@ - - - - - - - - - - - @@ -337,7 +337,7 @@ - + @@ -508,6 +508,9 @@ + + + @@ -572,6 +575,7 @@ + diff --git a/iOS/Settings/SettingsViewController.swift b/iOS/Settings/SettingsViewController.swift index 2e1664f1a..f395d62d1 100644 --- a/iOS/Settings/SettingsViewController.swift +++ b/iOS/Settings/SettingsViewController.swift @@ -58,6 +58,13 @@ class SettingsViewController: UITableViewController { switch section { case 0: return AccountManager.shared.accounts.count + 1 + case 1: + let defaultNumberOfRows = super.tableView(tableView, numberOfRowsInSection: section) + if AccountManager.shared.activeAccounts.isEmpty { + // Hide the add NetNewsWire feed row if they don't have any active accounts + return defaultNumberOfRows - 1 + } + return defaultNumberOfRows default: return super.tableView(tableView, numberOfRowsInSection: section) } @@ -116,8 +123,12 @@ class SettingsViewController: UITableViewController { UIApplication.shared.open(URL(string: "https://github.com/brentsimmons/NetNewsWire")!, options: [:]) case 3: UIApplication.shared.open(URL(string: "https://github.com/brentsimmons/NetNewsWire/issues")!, options: [:]) - default: + case 4: UIApplication.shared.open(URL(string: "https://github.com/brentsimmons/NetNewsWire/tree/master/Technotes")!, options: [:]) + case 5: + addFeed() + default: + UIApplication.shared.open(URL(string: "https://ranchero.com/netnewswire/")!, options: [:]) } case 2: UIApplication.shared.open(URL(string: "https://appcamp4girls.com/contribute/")!, options: [:]) @@ -132,10 +143,8 @@ class SettingsViewController: UITableViewController { let timeline = UIStoryboard.settings.instantiateController(ofType: RefreshIntervalViewController.self) self.navigationController?.pushViewController(timeline, animated: true) case 1: - addFeed() - case 2: importOPML() - case 3: + case 2: exportOPML() default: print("export") @@ -227,6 +236,7 @@ private extension SettingsViewController { let addNavViewController = UIStoryboard.add.instantiateInitialViewController() as! UINavigationController let addViewController = addNavViewController.topViewController as! AddContainerViewController addNavViewController.modalPresentationStyle = .formSheet + addNavViewController.preferredContentSize = AddContainerViewController.preferredContentSizeForFormSheetDisplay addViewController.initialFeed = appNewsURLString addViewController.initialFeedName = "NetNewsWire News"