2019-04-15 22:03:05 +02:00
|
|
|
//
|
|
|
|
// DetailViewController.swift
|
|
|
|
// NetNewsWire
|
|
|
|
//
|
|
|
|
// Created by Maurice Parker on 4/8/19.
|
|
|
|
// Copyright © 2019 Ranchero Software. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
import UIKit
|
|
|
|
import WebKit
|
|
|
|
import Account
|
|
|
|
import Articles
|
2019-04-21 13:41:59 +02:00
|
|
|
import SafariServices
|
2019-04-15 22:03:05 +02:00
|
|
|
|
|
|
|
class DetailViewController: UIViewController {
|
|
|
|
|
2019-04-22 00:42:26 +02:00
|
|
|
@IBOutlet weak var nextUnreadBarButtonItem: UIBarButtonItem!
|
|
|
|
@IBOutlet weak var prevArticleBarButtonItem: UIBarButtonItem!
|
|
|
|
@IBOutlet weak var nextArticleBarButtonItem: UIBarButtonItem!
|
2019-04-15 22:03:05 +02:00
|
|
|
@IBOutlet weak var readBarButtonItem: UIBarButtonItem!
|
|
|
|
@IBOutlet weak var starBarButtonItem: UIBarButtonItem!
|
|
|
|
@IBOutlet weak var actionBarButtonItem: UIBarButtonItem!
|
|
|
|
@IBOutlet weak var browserBarButtonItem: UIBarButtonItem!
|
|
|
|
@IBOutlet weak var webView: WKWebView!
|
|
|
|
|
2019-07-06 00:45:39 +02:00
|
|
|
weak var coordinator: AppCoordinator!
|
2019-04-15 22:03:05 +02:00
|
|
|
|
|
|
|
override func viewDidLoad() {
|
2019-04-23 11:35:48 +02:00
|
|
|
|
2019-04-15 22:03:05 +02:00
|
|
|
super.viewDidLoad()
|
2019-08-22 19:10:50 +02:00
|
|
|
webView.isHidden = true
|
2019-04-15 22:03:05 +02:00
|
|
|
webView.navigationDelegate = self
|
2019-04-23 11:35:48 +02:00
|
|
|
|
2019-04-22 14:08:54 +02:00
|
|
|
markAsRead()
|
2019-04-23 11:35:48 +02:00
|
|
|
updateUI()
|
2019-04-15 22:03:05 +02:00
|
|
|
reloadHTML()
|
2019-04-23 11:35:48 +02:00
|
|
|
|
|
|
|
NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: nil)
|
2019-04-15 22:03:05 +02:00
|
|
|
NotificationCenter.default.addObserver(self, selector: #selector(statusesDidChange(_:)), name: .StatusesDidChange, object: nil)
|
2019-06-29 20:35:12 +02:00
|
|
|
NotificationCenter.default.addObserver(self, selector: #selector(articleSelectionDidChange(_:)), name: .ArticleSelectionDidChange, object: coordinator)
|
2019-04-23 14:26:35 +02:00
|
|
|
NotificationCenter.default.addObserver(self, selector: #selector(progressDidChange(_:)), name: .AccountRefreshProgressDidChange, object: nil)
|
2019-04-27 17:49:54 +02:00
|
|
|
NotificationCenter.default.addObserver(self, selector: #selector(contentSizeCategoryDidChange(_:)), name: UIContentSizeCategory.didChangeNotification, object: nil)
|
2019-04-15 22:03:05 +02:00
|
|
|
}
|
|
|
|
|
2019-04-23 14:26:35 +02:00
|
|
|
override func viewDidAppear(_ animated: Bool) {
|
|
|
|
super.viewDidAppear(animated)
|
|
|
|
updateProgressIndicatorIfNeeded()
|
|
|
|
}
|
|
|
|
|
2019-04-22 14:08:54 +02:00
|
|
|
func markAsRead() {
|
2019-07-06 00:45:39 +02:00
|
|
|
if let article = coordinator.currentArticle {
|
2019-04-22 14:08:54 +02:00
|
|
|
markArticles(Set([article]), statusKey: .read, flag: true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-23 11:35:48 +02:00
|
|
|
func updateUI() {
|
2019-04-15 22:03:05 +02:00
|
|
|
|
2019-07-06 00:45:39 +02:00
|
|
|
guard let article = coordinator.currentArticle else {
|
2019-04-22 00:42:26 +02:00
|
|
|
nextUnreadBarButtonItem.isEnabled = false
|
|
|
|
prevArticleBarButtonItem.isEnabled = false
|
|
|
|
nextArticleBarButtonItem.isEnabled = false
|
2019-04-15 22:03:05 +02:00
|
|
|
readBarButtonItem.isEnabled = false
|
|
|
|
starBarButtonItem.isEnabled = false
|
|
|
|
browserBarButtonItem.isEnabled = false
|
|
|
|
actionBarButtonItem.isEnabled = false
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-07-06 00:45:39 +02:00
|
|
|
nextUnreadBarButtonItem.isEnabled = coordinator.isAnyUnreadAvailable
|
|
|
|
prevArticleBarButtonItem.isEnabled = coordinator.isPrevArticleAvailable
|
|
|
|
nextArticleBarButtonItem.isEnabled = coordinator.isNextArticleAvailable
|
2019-04-22 00:42:26 +02:00
|
|
|
|
2019-04-15 22:03:05 +02:00
|
|
|
readBarButtonItem.isEnabled = true
|
|
|
|
starBarButtonItem.isEnabled = true
|
|
|
|
browserBarButtonItem.isEnabled = true
|
|
|
|
actionBarButtonItem.isEnabled = true
|
|
|
|
|
|
|
|
let readImage = article.status.read ? AppAssets.circleOpenImage : AppAssets.circleClosedImage
|
|
|
|
readBarButtonItem.image = readImage
|
|
|
|
|
|
|
|
let starImage = article.status.starred ? AppAssets.starClosedImage : AppAssets.starOpenImage
|
|
|
|
starBarButtonItem.image = starImage
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func reloadHTML() {
|
2019-04-22 00:42:26 +02:00
|
|
|
|
2019-07-06 00:45:39 +02:00
|
|
|
guard let article = coordinator.currentArticle, let webView = webView else {
|
2019-04-15 22:03:05 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
let style = ArticleStylesManager.shared.currentStyle
|
|
|
|
let html = ArticleRenderer.articleHTML(article: article, style: style)
|
2019-04-19 14:37:47 +02:00
|
|
|
webView.loadHTMLString(html, baseURL: nil)
|
2019-04-22 00:42:26 +02:00
|
|
|
|
2019-04-15 22:03:05 +02:00
|
|
|
}
|
|
|
|
|
2019-04-23 11:35:48 +02:00
|
|
|
// MARK: Notifications
|
|
|
|
|
|
|
|
@objc dynamic func unreadCountDidChange(_ notification: Notification) {
|
|
|
|
updateUI()
|
|
|
|
}
|
|
|
|
|
2019-04-15 22:03:05 +02:00
|
|
|
@objc func statusesDidChange(_ note: Notification) {
|
|
|
|
guard let articles = note.userInfo?[Account.UserInfoKey.articles] as? Set<Article> else {
|
|
|
|
return
|
|
|
|
}
|
2019-07-06 00:45:39 +02:00
|
|
|
if articles.count == 1 && articles.first?.articleID == coordinator.currentArticle?.articleID {
|
2019-04-23 11:35:48 +02:00
|
|
|
updateUI()
|
2019-04-15 22:03:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-22 23:25:16 +02:00
|
|
|
@objc func articleSelectionDidChange(_ note: Notification) {
|
2019-04-22 14:08:54 +02:00
|
|
|
markAsRead()
|
2019-04-23 11:35:48 +02:00
|
|
|
updateUI()
|
2019-04-22 00:42:26 +02:00
|
|
|
reloadHTML()
|
|
|
|
}
|
2019-04-23 14:26:35 +02:00
|
|
|
|
|
|
|
@objc func progressDidChange(_ note: Notification) {
|
|
|
|
updateProgressIndicatorIfNeeded()
|
|
|
|
}
|
2019-04-22 00:42:26 +02:00
|
|
|
|
2019-04-27 17:49:54 +02:00
|
|
|
@objc func contentSizeCategoryDidChange(_ note: Notification) {
|
|
|
|
reloadHTML()
|
|
|
|
}
|
|
|
|
|
2019-04-22 00:42:26 +02:00
|
|
|
// MARK: Actions
|
|
|
|
|
|
|
|
@IBAction func nextUnread(_ sender: Any) {
|
2019-07-06 00:45:39 +02:00
|
|
|
coordinator.selectNextUnread()
|
2019-04-22 00:42:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@IBAction func prevArticle(_ sender: Any) {
|
2019-07-06 18:32:19 +02:00
|
|
|
coordinator.selectPrevArticle()
|
2019-04-22 00:42:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@IBAction func nextArticle(_ sender: Any) {
|
2019-07-06 18:32:19 +02:00
|
|
|
coordinator.selectNextArticle()
|
2019-04-22 00:42:26 +02:00
|
|
|
}
|
|
|
|
|
2019-04-15 22:03:05 +02:00
|
|
|
@IBAction func toggleRead(_ sender: Any) {
|
2019-07-06 18:49:53 +02:00
|
|
|
coordinator.toggleReadForCurrentArticle()
|
2019-04-15 22:03:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@IBAction func toggleStar(_ sender: Any) {
|
2019-07-06 18:49:53 +02:00
|
|
|
coordinator.toggleStarForCurrentArticle()
|
2019-04-15 22:03:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@IBAction func openBrowser(_ sender: Any) {
|
2019-07-06 18:49:53 +02:00
|
|
|
coordinator.showBrowserForCurrentArticle()
|
2019-04-15 22:03:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@IBAction func showActivityDialog(_ sender: Any) {
|
2019-08-20 01:13:21 +02:00
|
|
|
guard let currentArticle = coordinator.currentArticle, let preferredLink = currentArticle.preferredLink, let url = URL(string: preferredLink) else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
let itemSource = ArticleActivityItemSource(url: url, subject: currentArticle.title)
|
|
|
|
let activityViewController = UIActivityViewController(activityItems: [itemSource], applicationActivities: nil)
|
|
|
|
activityViewController.popoverPresentationController?.barButtonItem = actionBarButtonItem
|
|
|
|
present(activityViewController, animated: true)
|
2019-04-15 22:03:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class ArticleActivityItemSource: NSObject, UIActivityItemSource {
|
|
|
|
|
|
|
|
private let url: URL
|
|
|
|
private let subject: String?
|
|
|
|
|
|
|
|
init(url: URL, subject: String?) {
|
|
|
|
self.url = url
|
|
|
|
self.subject = subject
|
|
|
|
}
|
|
|
|
|
|
|
|
func activityViewControllerPlaceholderItem(_ : UIActivityViewController) -> Any {
|
|
|
|
return url
|
|
|
|
}
|
|
|
|
|
|
|
|
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
|
|
|
|
return url
|
|
|
|
}
|
|
|
|
|
|
|
|
func activityViewController(_ activityViewController: UIActivityViewController, subjectForActivityType activityType: UIActivity.ActivityType?) -> String {
|
|
|
|
return subject ?? ""
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
extension DetailViewController: WKNavigationDelegate {
|
|
|
|
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
|
|
|
|
|
|
|
|
if navigationAction.navigationType == .linkActivated {
|
|
|
|
|
|
|
|
guard let url = navigationAction.request.url else {
|
|
|
|
decisionHandler(.allow)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
|
|
|
|
if components?.scheme == "http" || components?.scheme == "https" {
|
2019-04-21 13:41:59 +02:00
|
|
|
let vc = SFSafariViewController(url: url)
|
|
|
|
present(vc, animated: true)
|
2019-04-15 22:03:05 +02:00
|
|
|
decisionHandler(.cancel)
|
|
|
|
} else {
|
|
|
|
decisionHandler(.allow)
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
decisionHandler(.allow)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-08-22 19:10:50 +02:00
|
|
|
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
|
|
|
// We initially hide the webview and only show it after it has loaded to avoid the
|
|
|
|
// white flashing that WKWebView does when it loads. This is especially noticable
|
|
|
|
// in dark mode.
|
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
|
|
|
|
webView.isHidden = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-15 22:03:05 +02:00
|
|
|
}
|
2019-04-23 14:26:35 +02:00
|
|
|
|
|
|
|
private extension DetailViewController {
|
|
|
|
|
|
|
|
func updateProgressIndicatorIfNeeded() {
|
|
|
|
if !(UIDevice.current.userInterfaceIdiom == .pad) {
|
|
|
|
navigationController?.updateAccountRefreshProgressIndicator()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|