Add view options and filter to timeline. Issue #1937
This commit is contained in:
parent
c41c551093
commit
36b53766d6
|
@ -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)!)
|
||||||
}()
|
}()
|
||||||
|
|
|
@ -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"/>
|
||||||
|
|
|
@ -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 doesn’t.")
|
assertionFailure("Expected timelineViewController to match either regular or search timelineViewController, but it doesn’t.")
|
||||||
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "filterActive.pdf"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "filterInactive.pdf"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
Loading…
Reference in New Issue