Add read filter toggle for articles. Issue #130.

This commit is contained in:
Maurice Parker 2019-11-22 11:47:03 -06:00
parent 7667dbf60e
commit 5ac14fb910
4 changed files with 66 additions and 5 deletions

View File

@ -336,6 +336,12 @@
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="View" id="HyV-fh-RgO"> <menu key="submenu" title="View" id="HyV-fh-RgO">
<items> <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"> <menuItem title="Sort Articles By" id="nLP-fa-KUi">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Sort Articles By" id="OlJ-93-6OP"> <menu key="submenu" title="Sort Articles By" id="OlJ-93-6OP">
@ -365,7 +371,7 @@
<menuItem title="Hide Read Feeds" id="E9K-zV-nLv"> <menuItem title="Hide Read Feeds" id="E9K-zV-nLv">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<connections> <connections>
<action selector="toggleReadFilter:" target="Ady-hI-5gd" id="5pI-YT-xai"/> <action selector="toggleReadFeedsFilter:" target="Ady-hI-5gd" id="5pI-YT-xai"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem title="Show Sidebar" keyEquivalent="s" id="kIP-vf-haE"> <menuItem title="Show Sidebar" keyEquivalent="s" id="kIP-vf-haE">

View File

@ -237,10 +237,19 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations {
return currentSearchField != nil return currentSearchField != nil
} }
if item.action == #selector(toggleReadFilter(_:)) { if item.action == #selector(toggleReadFeedsFilter(_:)) {
(item as! NSMenuItem).state = sidebarViewController?.isReadFiltered ?? false ? .on : .off (item as! NSMenuItem).state = sidebarViewController?.isReadFiltered ?? false ? .on : .off
} }
if item.action == #selector(toggleReadArticlesFilter(_:)) {
if let timelineContainer = timelineContainerViewController {
(item as! NSMenuItem).isEnabled = true
(item as! NSMenuItem).state = timelineContainer.isReadFiltered ? .on : .off
} else {
(item as! NSMenuItem).isEnabled = false
}
}
if item.action == #selector(toggleSidebar(_:)) { if item.action == #selector(toggleSidebar(_:)) {
guard let splitViewItem = sidebarSplitViewItem else { guard let splitViewItem = sidebarSplitViewItem else {
return false return false
@ -443,9 +452,14 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations {
window?.makeFirstResponder(searchField) window?.makeFirstResponder(searchField)
} }
@IBAction func toggleReadFilter(_ sender: Any?) { @IBAction func toggleReadFeedsFilter(_ sender: Any?) {
sidebarViewController?.toggleReadFilter() sidebarViewController?.toggleReadFilter()
} }
@IBAction func toggleReadArticlesFilter(_ sender: Any?) {
timelineContainerViewController?.toggleReadFilter()
}
} }
// MARK: - SidebarDelegate // MARK: - SidebarDelegate

View File

@ -30,6 +30,10 @@ final class TimelineContainerViewController: NSViewController {
weak var delegate: TimelineContainerViewControllerDelegate? weak var delegate: TimelineContainerViewControllerDelegate?
var isReadFiltered: Bool {
return regularTimelineViewController.isReadFiltered
}
lazy var regularTimelineViewController = { lazy var regularTimelineViewController = {
return TimelineViewController(delegate: self) return TimelineViewController(delegate: self)
}() }()
@ -79,6 +83,11 @@ final class TimelineContainerViewController: NSViewController {
} }
return true return true
} }
func toggleReadFilter() {
regularTimelineViewController.toggleReadFilter()
}
} }
extension TimelineContainerViewController: TimelineDelegate { extension TimelineContainerViewController: TimelineDelegate {

View File

@ -20,6 +20,11 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr
@IBOutlet var tableView: TimelineTableView! @IBOutlet var tableView: TimelineTableView!
private var articleReadFilterType: ReadFilterType?
var isReadFiltered: Bool {
return articleReadFilterType ?? .read != .none
}
var representedObjects: [AnyObject]? { var representedObjects: [AnyObject]? {
didSet { didSet {
if !representedObjectArraysAreEqual(oldValue, representedObjects) { if !representedObjectArraysAreEqual(oldValue, representedObjects) {
@ -36,6 +41,7 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr
showFeedNames = false showFeedNames = false
} }
determineReadFilterType()
selectionDidChange(nil) selectionDidChange(nil)
if showsSearchResults { if showsSearchResults {
fetchAndReplaceArticlesAsync() fetchAndReplaceArticlesAsync()
@ -213,6 +219,19 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr
return representedObjects.first! === object 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 // MARK: - Actions
@objc func openArticleInBrowser(_ sender: Any?) { @objc func openArticleInBrowser(_ sender: Any?) {
@ -945,6 +964,14 @@ private extension TimelineViewController {
// MARK: - Fetching Articles // MARK: - Fetching Articles
func determineReadFilterType() {
if representedObjects?.count ?? 0 == 1, let feed = representedObjects?.first as? Feed {
articleReadFilterType = feed.defaultReadFilterType
} else {
articleReadFilterType = .read
}
}
func fetchAndReplaceArticlesSync() { func fetchAndReplaceArticlesSync() {
// To be called when the user has made a change of selection in the sidebar. // To be called when the user has made a change of selection in the sidebar.
// It blocks the main thread, so that theres no async delay, // It blocks the main thread, so that theres no async delay,
@ -990,7 +1017,11 @@ private extension TimelineViewController {
var fetchedArticles = Set<Article>() var fetchedArticles = Set<Article>()
for articleFetcher in articleFetchers { for articleFetcher in articleFetchers {
fetchedArticles.formUnion(articleFetcher.fetchArticles()) if articleReadFilterType != ReadFilterType.none {
fetchedArticles.formUnion(articleFetcher.fetchUnreadArticles())
} else {
fetchedArticles.formUnion(articleFetcher.fetchArticles())
}
} }
return fetchedArticles return fetchedArticles
} }
@ -1000,7 +1031,8 @@ private extension TimelineViewController {
// if its been superseded by a newer fetch, or the timeline was emptied, etc., it wont get called. // if its been superseded by a newer fetch, or the timeline was emptied, etc., it wont get called.
precondition(Thread.isMainThread) precondition(Thread.isMainThread)
cancelPendingAsyncFetches() cancelPendingAsyncFetches()
let fetchOperation = FetchRequestOperation(id: fetchSerialNumber, readFilter: false, 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) precondition(Thread.isMainThread)
guard !operation.isCanceled, let strongSelf = self, operation.id == strongSelf.fetchSerialNumber else { guard !operation.isCanceled, let strongSelf = self, operation.id == strongSelf.fetchSerialNumber else {
return return