mirror of
https://github.com/Ranchero-Software/NetNewsWire
synced 2025-08-12 06:26:36 +00:00
Merge branch 'master' into feature/feed-wrangler
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="15504" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="15505" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15504"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15505"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Application-->
|
||||
@@ -336,6 +336,12 @@
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="View" id="HyV-fh-RgO">
|
||||
<items>
|
||||
<menuItem title="Hide Read Articles" id="b10-sA-Yzi">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleReadArticlesFilter:" target="Ady-hI-5gd" id="YhV-0F-jrM"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Sort Articles By" id="nLP-fa-KUi">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Sort Articles By" id="OlJ-93-6OP">
|
||||
@@ -362,6 +368,12 @@
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="dZt-2W-gxf"/>
|
||||
<menuItem title="Hide Read Feeds" id="E9K-zV-nLv">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleReadFeedsFilter:" target="Ady-hI-5gd" id="5pI-YT-xai"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Show Sidebar" keyEquivalent="s" id="kIP-vf-haE">
|
||||
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
|
||||
<connections>
|
||||
|
||||
@@ -221,7 +221,7 @@ private extension DetailWebViewController {
|
||||
var render = "error();"
|
||||
if let data = try? encoder.encode(templateData) {
|
||||
let json = String(data: data, encoding: .utf8)!
|
||||
render = "render(\(json));"
|
||||
render = "render(\(json), 0);"
|
||||
}
|
||||
|
||||
webView.evaluateJavaScript(render)
|
||||
|
||||
@@ -237,6 +237,14 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations {
|
||||
return currentSearchField != nil
|
||||
}
|
||||
|
||||
if item.action == #selector(toggleReadFeedsFilter(_:)) {
|
||||
return validateToggleReadFeeds(item)
|
||||
}
|
||||
|
||||
if item.action == #selector(toggleReadArticlesFilter(_:)) {
|
||||
return validateToggleReadArticles(item)
|
||||
}
|
||||
|
||||
if item.action == #selector(toggleSidebar(_:)) {
|
||||
guard let splitViewItem = sidebarSplitViewItem else {
|
||||
return false
|
||||
@@ -438,6 +446,15 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations {
|
||||
}
|
||||
window?.makeFirstResponder(searchField)
|
||||
}
|
||||
|
||||
@IBAction func toggleReadFeedsFilter(_ sender: Any?) {
|
||||
sidebarViewController?.toggleReadFilter()
|
||||
}
|
||||
|
||||
@IBAction func toggleReadArticlesFilter(_ sender: Any?) {
|
||||
timelineContainerViewController?.toggleReadFilter()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - SidebarDelegate
|
||||
@@ -810,6 +827,30 @@ private extension MainWindowController {
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func validateToggleReadFeeds(_ item: NSValidatedUserInterfaceItem) -> Bool {
|
||||
guard let menuItem = item as? NSMenuItem else { return false }
|
||||
|
||||
let showCommand = NSLocalizedString("Show Read Feeds", comment: "Command")
|
||||
let hideCommand = NSLocalizedString("Hide Read Feeds", comment: "Command")
|
||||
menuItem.title = sidebarViewController?.isReadFiltered ?? false ? showCommand : hideCommand
|
||||
return true
|
||||
}
|
||||
|
||||
func validateToggleReadArticles(_ item: NSValidatedUserInterfaceItem) -> Bool {
|
||||
guard let menuItem = item as? NSMenuItem else { return false }
|
||||
|
||||
let showCommand = NSLocalizedString("Show Read Articles", comment: "Command")
|
||||
let hideCommand = NSLocalizedString("Hide Read Articles", comment: "Command")
|
||||
|
||||
if let isReadFiltered = timelineContainerViewController?.isReadFiltered {
|
||||
menuItem.title = isReadFiltered ? showCommand : hideCommand
|
||||
return true
|
||||
} else {
|
||||
menuItem.title = hideCommand
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Misc.
|
||||
|
||||
|
||||
@@ -30,6 +30,9 @@ protocol SidebarDelegate: class {
|
||||
lazy var dataSource: SidebarOutlineDataSource = {
|
||||
return SidebarOutlineDataSource(treeController: treeController)
|
||||
}()
|
||||
var isReadFiltered: Bool {
|
||||
return treeControllerDelegate.isReadFiltered
|
||||
}
|
||||
|
||||
var undoableCommands = [UndoableCommand]()
|
||||
private var animatingChanges = false
|
||||
@@ -333,7 +336,16 @@ protocol SidebarDelegate: class {
|
||||
}
|
||||
revealAndSelectRepresentedObject(feedNode.representedObject)
|
||||
}
|
||||
|
||||
|
||||
func toggleReadFilter() {
|
||||
if treeControllerDelegate.isReadFiltered {
|
||||
treeControllerDelegate.isReadFiltered = false
|
||||
} else {
|
||||
treeControllerDelegate.isReadFiltered = true
|
||||
}
|
||||
rebuildTreeAndRestoreSelection()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - NSUserInterfaceValidations
|
||||
|
||||
@@ -30,6 +30,11 @@ final class TimelineContainerViewController: NSViewController {
|
||||
|
||||
weak var delegate: TimelineContainerViewControllerDelegate?
|
||||
|
||||
var isReadFiltered: Bool? {
|
||||
guard let currentTimelineViewController = currentTimelineViewController, mode(for: currentTimelineViewController) == .regular else { return nil }
|
||||
return regularTimelineViewController.isReadFiltered
|
||||
}
|
||||
|
||||
lazy var regularTimelineViewController = {
|
||||
return TimelineViewController(delegate: self)
|
||||
}()
|
||||
@@ -79,6 +84,11 @@ final class TimelineContainerViewController: NSViewController {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func toggleReadFilter() {
|
||||
regularTimelineViewController.toggleReadFilter()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension TimelineContainerViewController: TimelineDelegate {
|
||||
|
||||
@@ -20,6 +20,12 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr
|
||||
|
||||
@IBOutlet var tableView: TimelineTableView!
|
||||
|
||||
private var articleReadFilterType: ReadFilterType?
|
||||
var isReadFiltered: Bool? {
|
||||
guard let articleReadFilterType = articleReadFilterType, articleReadFilterType != .alwaysRead else { return nil}
|
||||
return articleReadFilterType != .none
|
||||
}
|
||||
|
||||
var representedObjects: [AnyObject]? {
|
||||
didSet {
|
||||
if !representedObjectArraysAreEqual(oldValue, representedObjects) {
|
||||
@@ -36,6 +42,7 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr
|
||||
showFeedNames = false
|
||||
}
|
||||
|
||||
determineReadFilterType()
|
||||
selectionDidChange(nil)
|
||||
if showsSearchResults {
|
||||
fetchAndReplaceArticlesAsync()
|
||||
@@ -213,6 +220,19 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr
|
||||
return representedObjects.first! === object
|
||||
}
|
||||
|
||||
func toggleReadFilter() {
|
||||
guard let filterType = articleReadFilterType else { return }
|
||||
switch filterType {
|
||||
case .alwaysRead:
|
||||
break
|
||||
case .read:
|
||||
articleReadFilterType = ReadFilterType.none
|
||||
case .none:
|
||||
articleReadFilterType = ReadFilterType.read
|
||||
}
|
||||
fetchAndReplaceArticlesAsync()
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
@objc func openArticleInBrowser(_ sender: Any?) {
|
||||
@@ -944,6 +964,14 @@ private extension TimelineViewController {
|
||||
}
|
||||
|
||||
// MARK: - Fetching Articles
|
||||
|
||||
func determineReadFilterType() {
|
||||
if representedObjects?.count ?? 0 == 1, let feed = representedObjects?.first as? Feed {
|
||||
articleReadFilterType = feed.defaultReadFilterType
|
||||
} else {
|
||||
articleReadFilterType = .read
|
||||
}
|
||||
}
|
||||
|
||||
func fetchAndReplaceArticlesSync() {
|
||||
// To be called when the user has made a change of selection in the sidebar.
|
||||
@@ -990,7 +1018,11 @@ private extension TimelineViewController {
|
||||
|
||||
var fetchedArticles = Set<Article>()
|
||||
for articleFetcher in articleFetchers {
|
||||
fetchedArticles.formUnion(articleFetcher.fetchArticles())
|
||||
if articleReadFilterType != ReadFilterType.none {
|
||||
fetchedArticles.formUnion(articleFetcher.fetchUnreadArticles())
|
||||
} else {
|
||||
fetchedArticles.formUnion(articleFetcher.fetchArticles())
|
||||
}
|
||||
}
|
||||
return fetchedArticles
|
||||
}
|
||||
@@ -1000,7 +1032,8 @@ private extension TimelineViewController {
|
||||
// if it’s been superseded by a newer fetch, or the timeline was emptied, etc., it won’t get called.
|
||||
precondition(Thread.isMainThread)
|
||||
cancelPendingAsyncFetches()
|
||||
let fetchOperation = FetchRequestOperation(id: fetchSerialNumber, representedObjects: representedObjects) { [weak self] (articles, operation) in
|
||||
let readFilter = articleReadFilterType != ReadFilterType.none
|
||||
let fetchOperation = FetchRequestOperation(id: fetchSerialNumber, readFilter: readFilter, representedObjects: representedObjects) { [weak self] (articles, operation) in
|
||||
precondition(Thread.isMainThread)
|
||||
guard !operation.isCanceled, let strongSelf = self, operation.id == strongSelf.fetchSerialNumber else {
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user