diff --git a/Frameworks/Account/Feedbin/FeedbinAPICaller.swift b/Frameworks/Account/Feedbin/FeedbinAPICaller.swift index e442931cb..e02f5c44d 100644 --- a/Frameworks/Account/Feedbin/FeedbinAPICaller.swift +++ b/Frameworks/Account/Feedbin/FeedbinAPICaller.swift @@ -70,8 +70,9 @@ final class FeedbinAPICaller: NSObject { func importOPML(opmlData: Data, completion: @escaping (Result) -> Void) { let callURL = feedbinBaseURL.appendingPathComponent("imports.json") - let request = URLRequest(url: callURL, credentials: credentials) - + var request = URLRequest(url: callURL, credentials: credentials) + request.addValue("text/xml; charset=utf-8", forHTTPHeaderField: HTTPRequestHeader.contentType) + transport.send(request: request, method: HTTPMethod.post, payload: opmlData) { result in switch result { diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 5919aa5a0..bc971f13e 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -28,6 +28,7 @@ 5144EA52227B8E4500D19003 /* AccountsFeedbin.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5144EA50227B8E4500D19003 /* AccountsFeedbin.xib */; }; 51543685228F6753005E1CDF /* DetailAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51543684228F6753005E1CDF /* DetailAccountViewController.swift */; }; 515436882291D75D005E1CDF /* AddLocalAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 515436872291D75D005E1CDF /* AddLocalAccountViewController.swift */; }; + 5154368A2291FED9005E1CDF /* FeedbinAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 515436892291FED9005E1CDF /* FeedbinAccountViewController.swift */; }; 51554C24228B71910055115A /* SyncDatabase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 51554C01228B6EB50055115A /* SyncDatabase.framework */; }; 51554C25228B71910055115A /* SyncDatabase.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 51554C01228B6EB50055115A /* SyncDatabase.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 51554C30228B71A10055115A /* SyncDatabase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 51554C01228B6EB50055115A /* SyncDatabase.framework */; }; @@ -677,6 +678,7 @@ 5144EA50227B8E4500D19003 /* AccountsFeedbin.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AccountsFeedbin.xib; sourceTree = ""; }; 51543684228F6753005E1CDF /* DetailAccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailAccountViewController.swift; sourceTree = ""; }; 515436872291D75D005E1CDF /* AddLocalAccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddLocalAccountViewController.swift; sourceTree = ""; }; + 515436892291FED9005E1CDF /* FeedbinAccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinAccountViewController.swift; sourceTree = ""; }; 51554BFC228B6EB50055115A /* SyncDatabase.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SyncDatabase.xcodeproj; path = Frameworks/SyncDatabase/SyncDatabase.xcodeproj; sourceTree = SOURCE_ROOT; }; 5183CCCF226E1E880010922C /* NonIntrinsicLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonIntrinsicLabel.swift; sourceTree = ""; }; 5183CCD9226E31A50010922C /* NonIntrinsicImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonIntrinsicImageView.swift; sourceTree = ""; }; @@ -1042,6 +1044,7 @@ 51E595AA228DF94C00FCC42B /* SettingsTableViewCell.xib */, 5183CCEE227125970010922C /* SettingsViewController.swift */, 51E595AC228E1C2100FCC42B /* AddAccountViewController.swift */, + 515436892291FED9005E1CDF /* FeedbinAccountViewController.swift */, 515436872291D75D005E1CDF /* AddLocalAccountViewController.swift */, 51F85BE6227245FC00C787DC /* AboutViewController.swift */, 51543684228F6753005E1CDF /* DetailAccountViewController.swift */, @@ -2340,6 +2343,7 @@ 512E09352268B25900BDCFDD /* UISplitViewController-Extensions.swift in Sources */, 51C452A022650A1900C03939 /* FeedIconDownloader.swift in Sources */, 51F85BE7227245FC00C787DC /* AboutViewController.swift in Sources */, + 5154368A2291FED9005E1CDF /* FeedbinAccountViewController.swift in Sources */, 51C4529E22650A1900C03939 /* ImageDownloader.swift in Sources */, 51C45292226509C800C03939 /* TodayFeedDelegate.swift in Sources */, 51C452A222650A1900C03939 /* RSHTMLMetadata+Extension.swift in Sources */, diff --git a/iOS/NavigationStateController.swift b/iOS/NavigationStateController.swift index 79c939149..954d4c2f5 100644 --- a/iOS/NavigationStateController.swift +++ b/iOS/NavigationStateController.swift @@ -178,7 +178,7 @@ class NavigationStateController { NotificationCenter.default.addObserver(self, selector: #selector(batchUpdateDidPerform(_:)), name: .BatchUpdateDidPerform, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(displayNameDidChange(_:)), name: .DisplayNameDidChange, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(accountStateDidChange(_:)), name: .AccountStateDidChange, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange(_:)), name: .AccountsDidChangeNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange(_:)), name: .AccountsDidChange, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(userDefaultsDidChange(_:)), name: UserDefaults.didChangeNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(accountDidDownloadArticles(_:)), name: .AccountDidDownloadArticles, object: nil) diff --git a/iOS/Settings/AddAccountViewController.swift b/iOS/Settings/AddAccountViewController.swift index a109fe2ed..c4e7a6285 100644 --- a/iOS/Settings/AddAccountViewController.swift +++ b/iOS/Settings/AddAccountViewController.swift @@ -9,7 +9,11 @@ import Account import UIKit -class AddAccountViewController: UITableViewController { +protocol AddAccountDismissDelegate: UIViewController { + func dismiss() +} + +class AddAccountViewController: UITableViewController, AddAccountDismissDelegate { @IBOutlet private weak var localAccountNameLabel: UILabel! @@ -22,11 +26,22 @@ class AddAccountViewController: UITableViewController { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { switch indexPath.row { case 0: - let viewController = UIStoryboard.settings.instantiateViewController(withIdentifier: "AddLocalAccountNavigationViewController") - present(viewController, animated: true) + let navController = UIStoryboard.settings.instantiateViewController(withIdentifier: "AddLocalAccountNavigationViewController") as! UINavigationController + let addViewController = navController.topViewController as! AddLocalAccountViewController + addViewController.delegate = self + present(navController, animated: true) + case 1: + let navController = UIStoryboard.settings.instantiateViewController(withIdentifier: "FeedbinAccountNavigationViewController") as! UINavigationController + let addViewController = navController.topViewController as! FeedbinAccountViewController + addViewController.delegate = self + present(navController, animated: true) default: break } } + func dismiss() { + navigationController?.popViewController(animated: false) + } + } diff --git a/iOS/Settings/AddLocalAccountViewController.swift b/iOS/Settings/AddLocalAccountViewController.swift index b2c789627..6973a48d8 100644 --- a/iOS/Settings/AddLocalAccountViewController.swift +++ b/iOS/Settings/AddLocalAccountViewController.swift @@ -14,13 +14,15 @@ class AddLocalAccountViewController: UIViewController { @IBOutlet private weak var localAccountNameLabel: UILabel! @IBOutlet weak var nameTextField: UITextField! + weak var delegate: AddAccountDismissDelegate? + override func viewDidLoad() { super.viewDidLoad() localAccountNameLabel.text = Account.defaultLocalAccountName nameTextField.delegate = self } - + @IBAction func cancel(_ sender: Any) { dismiss(animated: true) } @@ -29,6 +31,7 @@ class AddLocalAccountViewController: UIViewController { let account = AccountManager.shared.createAccount(type: .onMyMac) account.name = nameTextField.text dismiss(animated: true) + delegate?.dismiss() } } diff --git a/iOS/Settings/DetailAccountViewController.swift b/iOS/Settings/DetailAccountViewController.swift index eb6d597ac..4640671ab 100644 --- a/iOS/Settings/DetailAccountViewController.swift +++ b/iOS/Settings/DetailAccountViewController.swift @@ -23,7 +23,6 @@ class DetailAccountViewController: UITableViewController { nameTextField.text = account.name nameTextField.delegate = self activeSwitch.isOn = account.isActive - } override func viewWillDisappear(_ animated: Bool) { @@ -33,11 +32,64 @@ class DetailAccountViewController: UITableViewController { } -extension DetailAccountViewController: UITextFieldDelegate { +extension DetailAccountViewController { + + override func numberOfSections(in tableView: UITableView) -> Int { + if account == AccountManager.shared.defaultAccount { + return 1 + } else { + return super.numberOfSections(in: tableView) + } + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = super.tableView(tableView, cellForRowAt: indexPath) + let bgView = UIView() + bgView.backgroundColor = AppAssets.selectionBackgroundColor + cell.selectedBackgroundView = bgView + return cell + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + if indexPath.section == 1 { + deleteAccount() + } + + tableView.selectRow(at: nil, animated: true, scrollPosition: .none) + } + +} + +private extension DetailAccountViewController { + + func deleteAccount() { + let title = NSLocalizedString("Delete Account", comment: "Delete Account") + let message = NSLocalizedString("Are you sure you want to delete this account? This can not be undone.", comment: "Delete Account") + let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert) + + let cancelTitle = NSLocalizedString("Cancel", comment: "Cancel") + let cancelAction = UIAlertAction(title: cancelTitle, style: .cancel) + alertController.addAction(cancelAction) + + let markTitle = NSLocalizedString("Delete", comment: "Delete") + let markAction = UIAlertAction(title: markTitle, style: .default) { [weak self] (action) in + guard let account = self?.account else { return } + AccountManager.shared.deleteAccount(account) + self?.navigationController?.popViewController(animated: true) + } + alertController.addAction(markAction) + + present(alertController, animated: true) + } + +} + +extension DetailAccountViewController: UITextFieldDelegate { + func textFieldShouldReturn(_ textField: UITextField) -> Bool { textField.resignFirstResponder() return true } - + } diff --git a/iOS/Settings/FeedbinAccountViewController.swift b/iOS/Settings/FeedbinAccountViewController.swift new file mode 100644 index 000000000..b88c5172a --- /dev/null +++ b/iOS/Settings/FeedbinAccountViewController.swift @@ -0,0 +1,103 @@ +// +// AddFeedbinAccountViewController.swift +// NetNewsWire-iOS +// +// Created by Maurice Parker on 5/19/19. +// Copyright © 2019 Ranchero Software. All rights reserved. +// + +import UIKit +import Account +import RSWeb + +class FeedbinAccountViewController: UIViewController { + + @IBOutlet weak var cancelBarButtonItem: UIBarButtonItem! + @IBOutlet weak var activityIndicator: UIActivityIndicatorView! + @IBOutlet weak var doneBarButtonItem: UIBarButtonItem! + @IBOutlet weak var emailTextField: UITextField! + @IBOutlet weak var passwordTextField: UITextField! + @IBOutlet weak var errorMessageLabel: UILabel! + + weak var account: Account? + weak var delegate: AddAccountDismissDelegate? + + override func viewDidLoad() { + super.viewDidLoad() + + activityIndicator.isHidden = true + + if let account = account, let credentials = try? account.retrieveBasicCredentials() { + if case .basic(let username, let password) = credentials { + emailTextField.text = username + passwordTextField.text = password + } + } + } + + @IBAction func cancel(_ sender: Any) { + dismiss(animated: true) + } + + @IBAction func done(_ sender: Any) { + + self.errorMessageLabel.text = nil + + guard emailTextField.text != nil && passwordTextField.text != nil else { + self.errorMessageLabel.text = NSLocalizedString("Username & password required.", comment: "Credentials Error") + return + } + + cancelBarButtonItem.isEnabled = false + doneBarButtonItem.isEnabled = false + activityIndicator.isHidden = false + activityIndicator.startAnimating() + + let credentials = Credentials.basic(username: emailTextField.text ?? "", password: passwordTextField.text ?? "") + Account.validateCredentials(type: .feedbin, credentials: credentials) { [weak self] result in + + guard let self = self else { return } + + self.cancelBarButtonItem.isEnabled = true + self.doneBarButtonItem.isEnabled = true + self.activityIndicator.isHidden = true + self.activityIndicator.stopAnimating() + + switch result { + case .success(let authenticated): + + if authenticated { + + var newAccount = false + if self.account == nil { + self.account = AccountManager.shared.createAccount(type: .feedbin) + newAccount = true + } + + do { + try self.account?.removeBasicCredentials() + try self.account?.storeCredentials(credentials) + if newAccount { + self.account?.refreshAll() + } + self.dismiss(animated: true) + self.delegate?.dismiss() + } catch { + self.errorMessageLabel.text = NSLocalizedString("Keychain error while storing credentials.", comment: "Credentials Error") + } + + } else { + self.errorMessageLabel.text = NSLocalizedString("Invalid email/password combination.", comment: "Credentials Error") + } + + case .failure: + + self.errorMessageLabel.text = NSLocalizedString("Network error. Try again later.", comment: "Credentials Error") + + } + + } + + } + +} diff --git a/iOS/Settings/Settings.storyboard b/iOS/Settings/Settings.storyboard index 2636c141e..435e042e1 100644 --- a/iOS/Settings/Settings.storyboard +++ b/iOS/Settings/Settings.storyboard @@ -380,6 +380,30 @@ + + + + + + + + + + + + + + + + + + + @@ -417,7 +441,7 @@ - + @@ -504,14 +528,14 @@ - + - - + - + @@ -560,6 +584,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +