Add view options and filter to timeline. Issue #1937

This commit is contained in:
Maurice Parker 2020-03-22 15:39:37 -05:00
parent c41c551093
commit 36b53766d6
7 changed files with 177 additions and 7 deletions

View File

@ -17,10 +17,6 @@ extension NSImage.Name {
struct AppAssets { struct AppAssets {
static var timelineStar: RSImage! = {
return RSImage(named: .timelineStar)
}()
static var accountCloudKit: RSImage! = { static var accountCloudKit: RSImage! = {
return RSImage(named: "accountCloudKit") return RSImage(named: "accountCloudKit")
}() }()
@ -81,6 +77,14 @@ struct AppAssets {
return RSImage(named: "faviconTemplateImage")! return RSImage(named: "faviconTemplateImage")!
}() }()
static var filterActive: RSImage = {
return RSImage(named: "filterActive")!
}()
static var filterInactive: RSImage = {
return RSImage(named: "filterInactive")!
}()
static var iconLightBackgroundColor: NSColor = { static var iconLightBackgroundColor: NSColor = {
return NSColor(named: NSColor.Name("iconLightBackgroundColor"))! return NSColor(named: NSColor.Name("iconLightBackgroundColor"))!
}() }()
@ -101,6 +105,10 @@ struct AppAssets {
return IconImage(RSImage(named: NSImage.smartBadgeTemplateName)!) return IconImage(RSImage(named: NSImage.smartBadgeTemplateName)!)
}() }()
static var timelineStar: RSImage! = {
return RSImage(named: .timelineStar)
}()
static var todayFeedImage: IconImage = { static var todayFeedImage: IconImage = {
return IconImage(RSImage(named: NSImage.smartBadgeTemplateName)!) return IconImage(RSImage(named: NSImage.smartBadgeTemplateName)!)
}() }()

View File

@ -432,12 +432,87 @@
<scene sceneID="zUD-i8-QYC"> <scene sceneID="zUD-i8-QYC">
<objects> <objects>
<viewController id="36G-bQ-b96" customClass="TimelineContainerViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController"> <viewController id="36G-bQ-b96" customClass="TimelineContainerViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="Dnl-L5-xFP" customClass="TimelineContainerView" customModule="NetNewsWire" customModuleProvider="target"> <view key="view" id="Dnl-L5-xFP">
<rect key="frame" x="0.0" y="0.0" width="450" height="198"/> <rect key="frame" x="0.0" y="0.0" width="450" height="198"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="lSU-OC-sEC">
<rect key="frame" x="8" y="177" width="49" height="17"/>
<constraints>
<constraint firstAttribute="height" constant="16" id="DoO-KI-ena"/>
</constraints>
<popUpButtonCell key="cell" type="recessed" title="Sort" bezelStyle="recessed" alignment="center" lineBreakMode="truncatingTail" borderStyle="border" tag="1" imageScaling="proportionallyDown" inset="2" pullsDown="YES" id="bl0-6I-cH2">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES" changeBackground="YES" changeGray="YES"/>
<font key="font" metaFont="menu" size="11"/>
<menu key="menu" id="dN0-S2-uqU">
<items>
<menuItem title="Sort" tag="1" hidden="YES" id="4BZ-ya-evy">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="Newest To Oldest" state="on" tag="2" id="40c-kt-vhO">
<connections>
<action selector="sortByNewestArticleOnTop:" target="Ebq-4s-EwK" id="vYg-MZ-zve"/>
</connections>
</menuItem>
<menuItem title="Oldest To Newest" tag="3" id="sOF-Ez-vIL">
<connections>
<action selector="sortByOldestArticleOnTop:" target="Ebq-4s-EwK" id="KFG-M7-blB"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="xQP-gm-iO9"/>
<menuItem title="Group by Feed" tag="4" id="YSR-5C-Yjd">
<connections>
<action selector="groupByFeedToggled:" target="Ebq-4s-EwK" id="4y9-5l-ToF"/>
</connections>
</menuItem>
</items>
</menu>
</popUpButtonCell>
</popUpButton>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="iA5-go-AO0">
<rect key="frame" x="428" y="179" width="14" height="14"/>
<constraints>
<constraint firstAttribute="width" constant="14" id="FbQ-4n-ThT"/>
<constraint firstAttribute="height" constant="14" id="dlW-82-T6N"/>
</constraints>
<buttonCell key="cell" type="bevel" bezelStyle="rounded" image="filterInactive" imagePosition="overlaps" alignment="center" imageScaling="proportionallyDown" inset="2" id="j7d-36-DO5">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<color key="contentTintColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<connections>
<action selector="toggleReadArticlesFilter:" target="Ebq-4s-EwK" id="tcC-72-Npk"/>
</connections>
</button>
<box verticalHuggingPriority="750" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="7p6-pA-iw6">
<rect key="frame" x="0.0" y="171" width="450" height="5"/>
</box>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="Zpk-pq-9nW" customClass="TimelineContainerView" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="450" height="173"/>
</customView>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="Zpk-pq-9nW" secondAttribute="trailing" id="67d-pI-I9C"/>
<constraint firstAttribute="trailing" secondItem="iA5-go-AO0" secondAttribute="trailing" constant="8" id="9Dl-n9-vRI"/>
<constraint firstItem="lSU-OC-sEC" firstAttribute="leading" secondItem="Dnl-L5-xFP" secondAttribute="leading" constant="8" id="Ceb-sA-ECJ"/>
<constraint firstItem="Zpk-pq-9nW" firstAttribute="top" secondItem="7p6-pA-iw6" secondAttribute="bottom" id="KCa-8b-a6y"/>
<constraint firstItem="Zpk-pq-9nW" firstAttribute="leading" secondItem="Dnl-L5-xFP" secondAttribute="leading" id="XF2-31-E1x"/>
<constraint firstAttribute="trailing" secondItem="7p6-pA-iw6" secondAttribute="trailing" id="fG3-fe-Stb"/>
<constraint firstAttribute="bottom" secondItem="Zpk-pq-9nW" secondAttribute="bottom" id="fyv-EG-PC8"/>
<constraint firstItem="iA5-go-AO0" firstAttribute="top" secondItem="Dnl-L5-xFP" secondAttribute="top" constant="5" id="nM9-iA-OzY"/>
<constraint firstItem="7p6-pA-iw6" firstAttribute="leading" secondItem="Dnl-L5-xFP" secondAttribute="leading" id="pZU-jW-B1h"/>
<constraint firstItem="7p6-pA-iw6" firstAttribute="top" secondItem="Dnl-L5-xFP" secondAttribute="top" constant="24" id="tUm-nX-Jce"/>
<constraint firstItem="iA5-go-AO0" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="lSU-OC-sEC" secondAttribute="trailing" constant="8" id="yCg-gc-exN"/>
<constraint firstItem="lSU-OC-sEC" firstAttribute="top" secondItem="Dnl-L5-xFP" secondAttribute="top" constant="4" id="zay-ZJ-od3"/>
</constraints>
</view> </view>
<connections> <connections>
<outlet property="containerView" destination="Dnl-L5-xFP" id="kYD-K2-FOj"/> <outlet property="containerView" destination="Zpk-pq-9nW" id="Kye-yX-Wyn"/>
<outlet property="groupByFeedMenuItem" destination="YSR-5C-Yjd" id="1aN-9S-nE1"/>
<outlet property="newestToOldestMenuItem" destination="40c-kt-vhO" id="AGa-fX-EVy"/>
<outlet property="oldestToNewestMenuItem" destination="sOF-Ez-vIL" id="qSg-ST-ww9"/>
<outlet property="readFilteredButton" destination="iA5-go-AO0" id="kQg-2g-zNZ"/>
<outlet property="viewOptionsPopUpButton" destination="lSU-OC-sEC" id="Z8V-rm-n2m"/>
</connections> </connections>
</viewController> </viewController>
<customObject id="Ebq-4s-EwK" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/> <customObject id="Ebq-4s-EwK" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
@ -503,6 +578,7 @@
<image name="NSAddTemplate" width="11" height="11"/> <image name="NSAddTemplate" width="11" height="11"/>
<image name="NSRefreshTemplate" width="11" height="15"/> <image name="NSRefreshTemplate" width="11" height="15"/>
<image name="NSShareTemplate" width="11" height="16"/> <image name="NSShareTemplate" width="11" height="16"/>
<image name="filterInactive" width="100" height="101"/>
<image name="markAllRead" width="22" height="19"/> <image name="markAllRead" width="22" height="19"/>
<image name="markRead" width="19" height="19"/> <image name="markRead" width="19" height="19"/>
<image name="newFolder" width="19" height="19"/> <image name="newFolder" width="19" height="19"/>

View File

@ -19,6 +19,12 @@ protocol TimelineContainerViewControllerDelegate: class {
final class TimelineContainerViewController: NSViewController { 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! @IBOutlet var containerView: TimelineContainerView!
var currentTimelineViewController: TimelineViewController? { var currentTimelineViewController: TimelineViewController? {
@ -52,12 +58,22 @@ final class TimelineContainerViewController: NSViewController {
super.viewDidLoad() super.viewDidLoad()
setRepresentedObjects(nil, mode: .regular) setRepresentedObjects(nil, mode: .regular)
showTimeline(for: .regular) showTimeline(for: .regular)
updateViewOptionsPopUpButton()
NotificationCenter.default.addObserver(self, selector: #selector(userDefaultsDidChange(_:)), name: UserDefaults.didChangeNotification, object: nil)
}
// MARK: - Notifications
@objc func userDefaultsDidChange(_ note: Notification) {
updateViewOptionsPopUpButton()
} }
// MARK: - API // MARK: - API
func setRepresentedObjects(_ objects: [AnyObject]?, mode: TimelineSourceMode) { func setRepresentedObjects(_ objects: [AnyObject]?, mode: TimelineSourceMode) {
timelineViewController(for: mode).representedObjects = objects timelineViewController(for: mode).representedObjects = objects
updateReadFilterButton()
} }
func showTimeline(for mode: TimelineSourceMode) { func showTimeline(for mode: TimelineSourceMode) {
@ -95,6 +111,7 @@ final class TimelineContainerViewController: NSViewController {
func toggleReadFilter() { func toggleReadFilter() {
regularTimelineViewController.toggleReadFilter() regularTimelineViewController.toggleReadFilter()
updateReadFilterButton()
} }
// MARK: State Restoration // MARK: State Restoration
@ -105,6 +122,7 @@ final class TimelineContainerViewController: NSViewController {
func restoreState(from state: [AnyHashable : Any]) { func restoreState(from state: [AnyHashable : Any]) {
regularTimelineViewController.restoreState(from: state) regularTimelineViewController.restoreState(from: state)
updateReadFilterButton()
} }
} }
@ -145,4 +163,42 @@ private extension TimelineContainerViewController {
assertionFailure("Expected timelineViewController to match either regular or search timelineViewController, but it doesnt.") assertionFailure("Expected timelineViewController to match either regular or search timelineViewController, but it doesnt.")
return .regular // Should never get here. return .regular // Should never get here.
} }
func updateViewOptionsPopUpButton() {
let localizedTitle = NSLocalizedString("Sort %@", comment: "Sort")
if AppDefaults.timelineSortDirection == .orderedAscending {
newestToOldestMenuItem.state = .off
oldestToNewestMenuItem.state = .on
let title = NSString.localizedStringWithFormat(localizedTitle as NSString, oldestToNewestMenuItem.title) as String
viewOptionsPopUpButton.setTitle(title)
} else {
newestToOldestMenuItem.state = .on
oldestToNewestMenuItem.state = .off
let title = NSString.localizedStringWithFormat(localizedTitle as NSString, newestToOldestMenuItem.title) as String
viewOptionsPopUpButton.setTitle(title)
}
if AppDefaults.timelineGroupByFeed == true {
groupByFeedMenuItem.state = .on
} else {
groupByFeedMenuItem.state = .off
}
}
func updateReadFilterButton() {
guard let isReadFiltered = regularTimelineViewController.isReadFiltered else {
readFilteredButton.isHidden = true
return
}
readFilteredButton.isHidden = false
if isReadFiltered {
readFilteredButton.image = AppAssets.filterActive
} else {
readFilteredButton.image = AppAssets.filterInactive
}
}
} }

View File

@ -0,0 +1,15 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "filterActive.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template"
}
}

View File

@ -0,0 +1,15 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "filterInactive.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template"
}
}