// // AddFeedViewController.swift // NetNewsWire // // Created by Maurice Parker on 4/16/19. // Copyright © 2019 Ranchero Software, LLC. All rights reserved. // import UIKit import Account import RSCore import RSTree 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! private var pickerData: AddFeedFolderPickerData! private var feedFinder: FeedFinder? private var userEnteredURL: URL? private var userEnteredFolder: Folder? private var userEnteredTitle: String? private var userEnteredAccount: Account? private var foundFeedURLString: String? private var bestFeedSpecifier: FeedSpecifier? private var titleFromFeed: String? private var userCancelled = false weak var delegate: AddContainerViewControllerChildDelegate? var initialFeed: String? var initialFeedName: String? override func viewDidLoad() { super.viewDidLoad() urlTextField.autocorrectionType = .no urlTextField.autocapitalizationType = .none urlTextField.text = initialFeed if initialFeed != nil { delegate?.readyToAdd(state: true) } nameTextField.text = initialFeedName 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) NotificationCenter.default.addObserver(self, selector: #selector(textDidChange(_:)), name: UITextField.textDidChangeNotification, object: urlTextField) } func cancel() { userCancelled = true delegate?.processingDidCancel() } func add() { let urlString = urlTextField.text ?? "" let normalizedURLString = (urlString as NSString).rs_normalizedURL() guard !normalizedURLString.isEmpty, let url = URL(string: normalizedURLString) else { delegate?.processingDidCancel() return } userEnteredURL = url userEnteredTitle = nameTextField.text let container = pickerData.containers[folderPickerView.selectedRow(inComponent: 0)] if let account = container as? Account { userEnteredAccount = account } if let folder = container as? Folder, let account = folder.account { userEnteredAccount = account userEnteredFolder = folder } guard let userEnteredAccount = userEnteredAccount else { assertionFailure() return } if userEnteredAccount.hasFeed(withURL: url.absoluteString) { showAlreadySubscribedError() return } delegate?.processingDidBegin() feedFinder = FeedFinder(url: url, delegate: self) } @objc func textDidChange(_ note: Notification) { delegate?.readyToAdd(state: urlTextField.text?.rs_stringMayBeURL() ?? false) } } extension AddFeedViewController: UIPickerViewDataSource, UIPickerViewDelegate { func numberOfComponents(in pickerView: UIPickerView) ->Int { return 1 } func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { return pickerData.containerNames.count } func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { return pickerData.containerNames[row] } func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { folderLabel.text = pickerData.containerNames[row] } } extension AddFeedViewController: FeedFinderDelegate { public func feedFinder(_ feedFinder: FeedFinder, didFindFeeds feedSpecifiers: Set) { if userCancelled { return } if let error = feedFinder.initialDownloadError { if feedFinder.initialDownloadStatusCode == 404 { showNoFeedsErrorMessage() delegate?.processingDidCancel() } else { showInitialDownloadError(error) delegate?.processingDidCancel() } return } guard let bestFeedSpecifier = FeedSpecifier.bestFeed(in: feedSpecifiers) else { showNoFeedsErrorMessage() delegate?.processingDidCancel() return } self.bestFeedSpecifier = bestFeedSpecifier self.foundFeedURLString = bestFeedSpecifier.urlString if let url = URL(string: bestFeedSpecifier.urlString) { InitialFeedDownloader.download(url) { (parsedFeed) in self.titleFromFeed = parsedFeed?.title self.addFeedIfPossible(parsedFeed) } } else { // Shouldn't happen. showNoFeedsErrorMessage() delegate?.processingDidCancel() } } } private extension AddFeedViewController { private func showAlreadySubscribedError() { let title = NSLocalizedString("Already subscribed", comment: "Feed finder") let message = NSLocalizedString("Can’t add this feed because you’ve already subscribed to it.", comment: "Feed finder") presentError(title: title, message: message) } private func showNoFeedsErrorMessage() { let title = NSLocalizedString("Feed not found", comment: "Feed finder") let message = NSLocalizedString("Can’t add a feed because no feed was found.", comment: "Feed finder") presentError(title: title, message: message) } private func showInitialDownloadError(_ error: Error) { let title = NSLocalizedString("Download Error", comment: "Feed finder") let formatString = NSLocalizedString("Can’t add this feed because of a download error: “%@”", comment: "Feed finder") let message = NSString.localizedStringWithFormat(formatString as NSString, error.localizedDescription) presentError(title: title, message: message as String) } func addFeedIfPossible(_ parsedFeed: ParsedFeed?) { if userCancelled { return } guard let account = userEnteredAccount else { assertionFailure("Expected account.") delegate?.processingDidCancel() return } guard let feedURLString = foundFeedURLString else { assertionFailure("Expected feedURLString.") delegate?.processingDidCancel() return } if account.hasFeed(withURL: feedURLString) { showAlreadySubscribedError() delegate?.processingDidCancel() return } guard let feed = account.createFeed(with: titleFromFeed, editedName: userEnteredTitle, url: feedURLString) else { delegate?.processingDidEnd() return } if let parsedFeed = parsedFeed { account.update(feed, with: parsedFeed, {}) } account.addFeed(feed, to: userEnteredFolder) NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed]) delegate?.processingDidEnd() } }