// // DetailViewController.swift // NetNewsWire // // Created by Brent Simmons on 7/26/15. // Copyright © 2015 Ranchero Software, LLC. All rights reserved. // import Foundation import WebKit import RSCore import Articles import RSWeb enum DetailState: Equatable { case noSelection case multipleSelection case loading case article(Article, CGFloat?) case extracted(Article, ExtractedArticle, CGFloat?) } final class DetailViewController: NSViewController, WKUIDelegate { @IBOutlet var containerView: DetailContainerView! @IBOutlet var statusBarView: DetailStatusBarView! private lazy var regularWebViewController = createWebViewController() private var searchWebViewController: DetailWebViewController? private var currentWebViewController: DetailWebViewController! { didSet { let webview = currentWebViewController.view if containerView.contentView === webview { return } statusBarView.mouseoverLink = nil containerView.contentView = webview } } private var currentSourceMode: TimelineSourceMode = .regular { didSet { currentWebViewController = webViewController(for: currentSourceMode) } } private var detailStateForRegular: DetailState = .noSelection { didSet { webViewController(for: .regular).state = detailStateForRegular } } private var detailStateForSearch: DetailState = .noSelection { didSet { webViewController(for: .search).state = detailStateForSearch } } private var isArticleContentJavascriptEnabled = AppDefaults.shared.isArticleContentJavascriptEnabled override func viewDidLoad() { currentWebViewController = regularWebViewController NotificationCenter.default.addObserver(self, selector: #selector(userDefaultsDidChange(_:)), name: UserDefaults.didChangeNotification, object: nil) } // MARK: - API func setState(_ state: DetailState, mode: TimelineSourceMode) { switch mode { case .regular: detailStateForRegular = state case .search: detailStateForSearch = state } } func showDetail(for mode: TimelineSourceMode) { currentSourceMode = mode } func stopMediaPlayback() { currentWebViewController.stopMediaPlayback() } func canScrollDown(_ callback: @escaping (Bool) -> Void) { currentWebViewController.canScrollDown(callback) } func canScrollUp(_ callback: @escaping (Bool) -> Void) { currentWebViewController.canScrollUp(callback) } override func scrollPageDown(_ sender: Any?) { currentWebViewController.scrollPageDown(sender) } override func scrollPageUp(_ sender: Any?) { currentWebViewController.scrollPageUp(sender) } // MARK: - Navigation func focus() { guard let window = currentWebViewController.webView.window else { return } window.makeFirstResponderUnlessDescendantIsFirstResponder(currentWebViewController.webView) } // MARK: State Restoration func saveState(to state: inout [AnyHashable : Any]) { currentWebViewController.saveState(to: &state) } } // MARK: - DetailWebViewControllerDelegate extension DetailViewController: DetailWebViewControllerDelegate { func mouseDidEnter(_ detailWebViewController: DetailWebViewController, link: String) { guard !link.isEmpty, detailWebViewController === currentWebViewController else { return } statusBarView.mouseoverLink = link } func mouseDidExit(_ detailWebViewController: DetailWebViewController) { guard detailWebViewController === currentWebViewController else { return } statusBarView.mouseoverLink = nil } } // MARK: - Private private extension DetailViewController { func createWebViewController() -> DetailWebViewController { let controller = DetailWebViewController() controller.delegate = self controller.state = .noSelection return controller } func webViewController(for mode: TimelineSourceMode) -> DetailWebViewController { switch mode { case .regular: return regularWebViewController case .search: if searchWebViewController == nil { searchWebViewController = createWebViewController() } return searchWebViewController! } } @objc func userDefaultsDidChange(_ : Notification) { if AppDefaults.shared.isArticleContentJavascriptEnabled != isArticleContentJavascriptEnabled { isArticleContentJavascriptEnabled = AppDefaults.shared.isArticleContentJavascriptEnabled createNewWebViewsAndRestoreState() } } func createNewWebViewsAndRestoreState() { regularWebViewController = createWebViewController() currentWebViewController = regularWebViewController regularWebViewController.state = detailStateForRegular searchWebViewController = nil if currentSourceMode == .search { searchWebViewController = createWebViewController() currentWebViewController = searchWebViewController searchWebViewController!.state = detailStateForSearch } } }