NetNewsWire/Mac/MainWindow/Timeline/TimelineContainerViewContro...

220 lines
7.0 KiB
Swift
Raw Normal View History

//
2019-02-15 06:58:45 +01:00
// TimelineContainerViewController.swift
// NetNewsWire
//
// Created by Brent Simmons on 2/14/19.
// Copyright © 2019 Ranchero Software. All rights reserved.
//
import AppKit
import Account
import Articles
protocol TimelineContainerViewControllerDelegate: AnyObject {
func timelineSelectionDidChange(_: TimelineContainerViewController, articles: [Article]?, mode: TimelineSourceMode)
func timelineRequestedWebFeedSelection(_: TimelineContainerViewController, webFeed: WebFeed)
func timelineInvalidatedRestorationState(_: TimelineContainerViewController)
}
final class TimelineContainerViewController: NSViewController {
@IBOutlet weak var viewOptionsPopUpButton: NSPopUpButton!
@IBOutlet weak var newestToOldestMenuItem: NSMenuItem!
@IBOutlet weak var oldestToNewestMenuItem: NSMenuItem!
@IBOutlet weak var groupByFeedMenuItem: NSMenuItem!
@IBOutlet weak var readFilteredButton: NSButton!
@IBOutlet var containerView: TimelineContainerView!
var currentTimelineViewController: TimelineViewController? {
didSet {
let view = currentTimelineViewController?.view
if containerView.contentView === view {
return
}
containerView.contentView = view
view?.window?.recalculateKeyViewLoop()
}
}
weak var delegate: TimelineContainerViewControllerDelegate?
var isReadFiltered: Bool? {
guard let currentTimelineViewController = currentTimelineViewController, mode(for: currentTimelineViewController) == .regular else { return nil }
return regularTimelineViewController.isReadFiltered
}
var isCleanUpAvailable: Bool {
guard let currentTimelineViewController = currentTimelineViewController, mode(for: currentTimelineViewController) == .regular else { return false }
return regularTimelineViewController.isCleanUpAvailable
}
lazy var regularTimelineViewController = {
return TimelineViewController(delegate: self)
}()
private lazy var searchTimelineViewController: TimelineViewController = {
let viewController = TimelineViewController(delegate: self)
viewController.showsSearchResults = true
return viewController
}()
override func viewDidLoad() {
super.viewDidLoad()
2019-02-18 04:38:17 +01:00
setRepresentedObjects(nil, mode: .regular)
showTimeline(for: .regular)
makeMenuItemTitleLarger(newestToOldestMenuItem)
makeMenuItemTitleLarger(oldestToNewestMenuItem)
makeMenuItemTitleLarger(groupByFeedMenuItem)
updateViewOptionsPopUpButton()
NotificationCenter.default.addObserver(self, selector: #selector(userDefaultsDidChange(_:)), name: UserDefaults.didChangeNotification, object: nil)
}
// MARK: - Notifications
@objc func userDefaultsDidChange(_ note: Notification) {
updateViewOptionsPopUpButton()
}
// MARK: - API
2019-02-18 04:38:17 +01:00
func setRepresentedObjects(_ objects: [AnyObject]?, mode: TimelineSourceMode) {
timelineViewController(for: mode).representedObjects = objects
updateReadFilterButton()
}
func showTimeline(for mode: TimelineSourceMode) {
currentTimelineViewController = timelineViewController(for: mode)
}
func regularTimelineViewControllerHasRepresentedObjects(_ representedObjects: [AnyObject]?) -> Bool {
// Use this to find out if the regular timeline view already has the specified representedObjects.
// This is used in determining whether a search should end.
// The sidebar may think that the selection has changed, and therefore search should end
// but it could be that the regular timeline already has these representedObjects,
// and therefore the selection hasnt actually changed,
// and therefore search shouldnt end.
// https://github.com/brentsimmons/NetNewsWire/issues/791
if representedObjects == nil && regularTimelineViewController.representedObjects == nil {
return true
}
guard let currentObjects = regularTimelineViewController.representedObjects, let representedObjects = representedObjects else {
return false
}
if currentObjects.count != representedObjects.count {
return false
}
for object in representedObjects {
guard let _ = currentObjects.firstIndex(where: { $0 === object } ) else {
return false
}
}
return true
}
2020-03-16 00:02:55 +01:00
func cleanUp() {
regularTimelineViewController.cleanUp()
}
func toggleReadFilter() {
regularTimelineViewController.toggleReadFilter()
updateReadFilterButton()
}
// MARK: State Restoration
func saveState(to state: inout [AnyHashable : Any]) {
regularTimelineViewController.saveState(to: &state)
}
func restoreState(from state: [AnyHashable : Any]) {
regularTimelineViewController.restoreState(from: state)
updateReadFilterButton()
}
}
extension TimelineContainerViewController: TimelineDelegate {
func timelineSelectionDidChange(_ timelineViewController: TimelineViewController, selectedArticles: [Article]?) {
delegate?.timelineSelectionDidChange(self, articles: selectedArticles, mode: mode(for: timelineViewController))
}
func timelineRequestedWebFeedSelection(_: TimelineViewController, webFeed: WebFeed) {
delegate?.timelineRequestedWebFeedSelection(self, webFeed: webFeed)
}
func timelineInvalidatedRestorationState(_: TimelineViewController) {
delegate?.timelineInvalidatedRestorationState(self)
}
}
private extension TimelineContainerViewController {
func makeMenuItemTitleLarger(_ menuItem: NSMenuItem) {
menuItem.attributedTitle = NSAttributedString(string: menuItem.title,
attributes: [NSAttributedString.Key.font: NSFont.controlContentFont(ofSize: NSFont.systemFontSize)])
}
func timelineViewController(for mode: TimelineSourceMode) -> TimelineViewController {
switch mode {
case .regular:
return regularTimelineViewController
case .search:
return searchTimelineViewController
}
}
func mode(for timelineViewController: TimelineViewController) -> TimelineSourceMode {
if timelineViewController === regularTimelineViewController {
return .regular
}
else if timelineViewController === searchTimelineViewController {
return .search
}
assertionFailure("Expected timelineViewController to match either regular or search timelineViewController, but it doesnt.")
return .regular // Should never get here.
}
func updateViewOptionsPopUpButton() {
if AppDefaults.shared.timelineSortDirection == .orderedAscending {
newestToOldestMenuItem.state = .off
oldestToNewestMenuItem.state = .on
viewOptionsPopUpButton.setTitle(oldestToNewestMenuItem.title)
} else {
newestToOldestMenuItem.state = .on
oldestToNewestMenuItem.state = .off
viewOptionsPopUpButton.setTitle(newestToOldestMenuItem.title)
}
if AppDefaults.shared.timelineGroupByFeed == true {
groupByFeedMenuItem.state = .on
} else {
groupByFeedMenuItem.state = .off
}
}
func updateReadFilterButton() {
2020-05-02 20:25:48 +02:00
guard currentTimelineViewController == regularTimelineViewController else {
readFilteredButton.isHidden = true
return
}
guard let isReadFiltered = regularTimelineViewController.isReadFiltered else {
readFilteredButton.isHidden = true
return
}
readFilteredButton.isHidden = false
if isReadFiltered {
readFilteredButton.image = AppAssets.filterActive
} else {
readFilteredButton.image = AppAssets.filterInactive
}
}
}