NetNewsWire/iOS/Add/AddFeedViewController.swift

240 lines
6.6 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// 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<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()
}
}