NetNewsWire/Mac/MainWindow/Detail/DetailViewController.swift

182 lines
4.7 KiB
Swift

//
// DetailViewController.swift
// NetNewsWire
//
// Created by Brent Simmons on 7/26/15.
// Copyright © 2015 Ranchero Software, LLC. All rights reserved.
//
import Foundation
import WebKit
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
}
}
}