// // FeedInspectorViewController.swift // NetNewsWire-iOS // // Created by Maurice Parker on 11/6/19. // Copyright © 2019 Ranchero Software. All rights reserved. // import UIKit import Account import SafariServices import UserNotifications import Images class FeedInspectorViewController: UITableViewController { static let preferredContentSizeForFormSheetDisplay = CGSize(width: 460.0, height: 500.0) var feed: Feed! @IBOutlet weak var nameTextField: UITextField! @IBOutlet weak var notifyAboutNewArticlesSwitch: UISwitch! @IBOutlet weak var alwaysShowReaderViewSwitch: UISwitch! @IBOutlet weak var homePageLabel: InteractiveLabel! @IBOutlet weak var feedURLLabel: InteractiveLabel! private var headerView: InspectorIconHeaderView? private var iconImage: IconImage? { return IconImageCache.shared.imageForFeed(feed) } private let homePageIndexPath = IndexPath(row: 0, section: 1) private var shouldHideHomePageSection: Bool { return feed.homePageURL == nil } private var userNotificationSettings: UNNotificationSettings? override func viewDidLoad() { tableView.register(InspectorIconHeaderView.self, forHeaderFooterViewReuseIdentifier: "SectionHeader") navigationItem.title = feed.nameForDisplay nameTextField.text = feed.nameForDisplay notifyAboutNewArticlesSwitch.setOn(feed.isNotifyAboutNewArticles ?? false, animated: false) alwaysShowReaderViewSwitch.setOn(feed.isArticleExtractorAlwaysOn ?? false, animated: false) homePageLabel.text = feed.homePageURL?.decodedURLString feedURLLabel.text = feed.url.decodedURLString NotificationCenter.default.addObserver(self, selector: #selector(feedIconDidBecomeAvailable(_:)), name: .FeedIconDidBecomeAvailable, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(updateNotificationSettings), name: UIApplication.willEnterForegroundNotification, object: nil) } override func viewDidAppear(_ animated: Bool) { updateNotificationSettings() } override func viewDidDisappear(_ animated: Bool) { if nameTextField.text != feed.nameForDisplay { let nameText = nameTextField.text ?? "" let newName = nameText.isEmpty ? (feed.name ?? NSLocalizedString("Untitled", comment: "Feed name")) : nameText Task { @MainActor in try? await feed.rename(to: newName) } } } // MARK: Notifications @objc func feedIconDidBecomeAvailable(_ notification: Notification) { headerView?.iconView.iconImage = iconImage } @IBAction func notifyAboutNewArticlesChanged(_ sender: Any) { guard let settings = userNotificationSettings else { notifyAboutNewArticlesSwitch.isOn = !notifyAboutNewArticlesSwitch.isOn return } if settings.authorizationStatus == .denied { notifyAboutNewArticlesSwitch.isOn = !notifyAboutNewArticlesSwitch.isOn present(notificationUpdateErrorAlert(), animated: true, completion: nil) } else if settings.authorizationStatus == .authorized { feed.isNotifyAboutNewArticles = notifyAboutNewArticlesSwitch.isOn } else { UNUserNotificationCenter.current().requestAuthorization(options:[.badge, .sound, .alert]) { (granted, error) in self.updateNotificationSettings() if granted { DispatchQueue.main.async { self.feed.isNotifyAboutNewArticles = self.notifyAboutNewArticlesSwitch.isOn UIApplication.shared.registerForRemoteNotifications() } } else { DispatchQueue.main.async { self.notifyAboutNewArticlesSwitch.isOn = !self.notifyAboutNewArticlesSwitch.isOn } } } } } @IBAction func alwaysShowReaderViewChanged(_ sender: Any) { feed.isArticleExtractorAlwaysOn = alwaysShowReaderViewSwitch.isOn } @IBAction func done(_ sender: Any) { dismiss(animated: true) } /// Returns a new indexPath, taking into consideration any /// conditions that may require the tableView to be /// displayed differently than what is setup in the storyboard. private func shift(_ indexPath: IndexPath) -> IndexPath { return IndexPath(row: indexPath.row, section: shift(indexPath.section)) } /// Returns a new section, taking into consideration any /// conditions that may require the tableView to be /// displayed differently than what is setup in the storyboard. private func shift(_ section: Int) -> Int { if section >= homePageIndexPath.section && shouldHideHomePageSection { return section + 1 } return section } } // MARK: Table View extension FeedInspectorViewController { override func numberOfSections(in tableView: UITableView) -> Int { let numberOfSections = super.numberOfSections(in: tableView) return shouldHideHomePageSection ? numberOfSections - 1 : numberOfSections } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return super.tableView(tableView, numberOfRowsInSection: shift(section)) } override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return section == 0 ? ImageHeaderView.rowHeight : super.tableView(tableView, heightForHeaderInSection: shift(section)) } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = super.tableView(tableView, cellForRowAt: shift(indexPath)) if indexPath.section == 0 && indexPath.row == 1 { guard let label = cell.contentView.subviews.filter({ $0.isKind(of: UILabel.self) })[0] as? UILabel else { return cell } label.numberOfLines = 2 label.text = feed.notificationDisplayName.capitalized } return cell } override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { super.tableView(tableView, titleForHeaderInSection: shift(section)) } override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { if shift(section) == 0 { headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "SectionHeader") as? InspectorIconHeaderView headerView?.iconView.iconImage = iconImage return headerView } else { return super.tableView(tableView, viewForHeaderInSection: shift(section)) } } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if shift(indexPath) == homePageIndexPath, let homePageUrlString = feed.homePageURL, let homePageUrl = URL(string: homePageUrlString) { let safari = SFSafariViewController(url: homePageUrl) safari.modalPresentationStyle = .pageSheet present(safari, animated: true) { tableView.deselectRow(at: indexPath, animated: true) } } } } // MARK: UITextFieldDelegate extension FeedInspectorViewController: UITextFieldDelegate { func textFieldShouldReturn(_ textField: UITextField) -> Bool { textField.resignFirstResponder() return true } } // MARK: UNUserNotificationCenter extension FeedInspectorViewController { @objc func updateNotificationSettings() { UNUserNotificationCenter.current().getNotificationSettings { (settings) in DispatchQueue.main.async { self.userNotificationSettings = settings if settings.authorizationStatus == .authorized { UIApplication.shared.registerForRemoteNotifications() } } } } func notificationUpdateErrorAlert() -> UIAlertController { let alert = UIAlertController(title: NSLocalizedString("Enable Notifications", comment: "Notifications"), message: NSLocalizedString("Notifications need to be enabled in the Settings app.", comment: "Notifications need to be enabled in the Settings app."), preferredStyle: .alert) let openSettings = UIAlertAction(title: NSLocalizedString("Open Settings", comment: "Open Settings"), style: .default) { (action) in UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [UIApplication.OpenExternalURLOptionsKey.universalLinksOnly : false], completionHandler: nil) } let dismiss = UIAlertAction(title: NSLocalizedString("Dismiss", comment: "Dismiss"), style: .cancel, handler: nil) alert.addAction(openSettings) alert.addAction(dismiss) alert.preferredAction = openSettings return alert } }