NetNewsWire/iOS/Add/AddFeedViewController.swift

231 lines
6.4 KiB
Swift
Raw Normal View History

//
// 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
var delegate: AddContainerViewControllerChildDelegate?
override func viewDidLoad() {
super.viewDidLoad()
urlTextField.autocorrectionType = .no
urlTextField.autocapitalizationType = .none
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<FeedSpecifier>) {
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("Cant add this feed because youve 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("Cant 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("Cant 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()
}
}