Add sidebar read filter
This commit is contained in:
parent
9f4a037c8f
commit
22e2c0b0e6
|
@ -23,9 +23,11 @@ class SidebarModel: ObservableObject, UndoableCommandRunner {
|
||||||
@Published var selectedFeedIdentifiers = Set<FeedIdentifier>()
|
@Published var selectedFeedIdentifiers = Set<FeedIdentifier>()
|
||||||
@Published var selectedFeedIdentifier: FeedIdentifier? = .none
|
@Published var selectedFeedIdentifier: FeedIdentifier? = .none
|
||||||
@Published var selectedFeeds = [Feed]()
|
@Published var selectedFeeds = [Feed]()
|
||||||
|
@Published var isReadFiltered = false
|
||||||
|
|
||||||
private var selectedFeedIdentifiersCancellable: AnyCancellable?
|
private var selectedFeedIdentifiersCancellable: AnyCancellable?
|
||||||
private var selectedFeedIdentifierCancellable: AnyCancellable?
|
private var selectedFeedIdentifierCancellable: AnyCancellable?
|
||||||
|
private var selectedReadFilteredCancellable: AnyCancellable?
|
||||||
|
|
||||||
private let rebuildSidebarItemsQueue = CoalescingQueue(name: "Rebuild The Sidebar Items", interval: 0.5)
|
private let rebuildSidebarItemsQueue = CoalescingQueue(name: "Rebuild The Sidebar Items", interval: 0.5)
|
||||||
|
|
||||||
|
@ -55,41 +57,20 @@ class SidebarModel: ObservableObject, UndoableCommandRunner {
|
||||||
self.selectedFeeds = [feed]
|
self.selectedFeeds = [feed]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
selectedReadFilteredCancellable = $isReadFiltered.sink { [weak self] filter in
|
||||||
|
guard let self = self else { return }
|
||||||
|
self.rebuildSidebarItems(isReadFiltered: filter)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: API
|
// MARK: API
|
||||||
|
|
||||||
@objc func rebuildSidebarItems() {
|
/// Rebuilds the sidebar items to cause the sidebar to rebuild itself
|
||||||
guard let delegate = delegate else { return }
|
func rebuildSidebarItems() {
|
||||||
var items = [SidebarItem]()
|
rebuildSidebarItemsWithCurrentValues()
|
||||||
|
|
||||||
var smartFeedControllerItem = SidebarItem(SmartFeedsController.shared)
|
|
||||||
for feed in SmartFeedsController.shared.smartFeeds {
|
|
||||||
smartFeedControllerItem.addChild(SidebarItem(feed, unreadCount: delegate.unreadCount(for: feed)))
|
|
||||||
}
|
|
||||||
items.append(smartFeedControllerItem)
|
|
||||||
|
|
||||||
for account in AccountManager.shared.sortedActiveAccounts {
|
|
||||||
var accountItem = SidebarItem(account)
|
|
||||||
|
|
||||||
for webFeed in sort(account.topLevelWebFeeds) {
|
|
||||||
accountItem.addChild(SidebarItem(webFeed, unreadCount: delegate.unreadCount(for: webFeed)))
|
|
||||||
}
|
|
||||||
|
|
||||||
for folder in sort(account.folders ?? Set<Folder>()) {
|
|
||||||
var folderItem = SidebarItem(folder, unreadCount: delegate.unreadCount(for: folder))
|
|
||||||
for webFeed in sort(folder.topLevelWebFeeds) {
|
|
||||||
folderItem.addChild(SidebarItem(webFeed, unreadCount: delegate.unreadCount(for: webFeed)))
|
|
||||||
}
|
|
||||||
accountItem.addChild(folderItem)
|
|
||||||
}
|
|
||||||
|
|
||||||
items.append(accountItem)
|
|
||||||
}
|
|
||||||
|
|
||||||
sidebarItems = items
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
@ -114,16 +95,61 @@ private extension SidebarModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
func queueRebuildSidebarItems() {
|
func queueRebuildSidebarItems() {
|
||||||
rebuildSidebarItemsQueue.add(self, #selector(rebuildSidebarItems))
|
rebuildSidebarItemsQueue.add(self, #selector(rebuildSidebarItemsWithCurrentValues))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc func rebuildSidebarItemsWithCurrentValues() {
|
||||||
|
rebuildSidebarItems(isReadFiltered: isReadFiltered)
|
||||||
|
}
|
||||||
|
|
||||||
|
func rebuildSidebarItems(isReadFiltered: Bool) {
|
||||||
|
guard let delegate = delegate else { return }
|
||||||
|
var items = [SidebarItem]()
|
||||||
|
|
||||||
|
var smartFeedControllerItem = SidebarItem(SmartFeedsController.shared)
|
||||||
|
for feed in SmartFeedsController.shared.smartFeeds {
|
||||||
|
// It looks like SwiftUI loses its mind when the last element in a section is removed. Don't filter
|
||||||
|
// the smartfeeds yet or we crash about everytime because Starred is almost always filtered
|
||||||
|
// if !isReadFiltered || feed.unreadCount > 0 {
|
||||||
|
smartFeedControllerItem.addChild(SidebarItem(feed, unreadCount: delegate.unreadCount(for: feed)))
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
items.append(smartFeedControllerItem)
|
||||||
|
|
||||||
|
for account in AccountManager.shared.sortedActiveAccounts {
|
||||||
|
var accountItem = SidebarItem(account)
|
||||||
|
|
||||||
|
for webFeed in sort(account.topLevelWebFeeds) {
|
||||||
|
if !isReadFiltered || webFeed.unreadCount > 0 {
|
||||||
|
accountItem.addChild(SidebarItem(webFeed, unreadCount: delegate.unreadCount(for: webFeed)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for folder in sort(account.folders ?? Set<Folder>()) {
|
||||||
|
if !isReadFiltered || folder.unreadCount > 0 {
|
||||||
|
var folderItem = SidebarItem(folder, unreadCount: delegate.unreadCount(for: folder))
|
||||||
|
for webFeed in sort(folder.topLevelWebFeeds) {
|
||||||
|
if !isReadFiltered || webFeed.unreadCount > 0 {
|
||||||
|
folderItem.addChild(SidebarItem(webFeed, unreadCount: delegate.unreadCount(for: webFeed)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
accountItem.addChild(folderItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
items.append(accountItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
sidebarItems = items
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: Notifications
|
// MARK: Notifications
|
||||||
|
|
||||||
@objc func unreadCountDidInitialize(_ notification: Notification) {
|
@objc func unreadCountDidInitialize(_ notification: Notification) {
|
||||||
guard notification.object is AccountManager else {
|
guard notification.object is AccountManager else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
rebuildSidebarItems()
|
rebuildSidebarItems(isReadFiltered: isReadFiltered)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func unreadCountDidChange(_ note: Notification) {
|
@objc func unreadCountDidChange(_ note: Notification) {
|
||||||
|
@ -135,27 +161,27 @@ private extension SidebarModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func containerChildrenDidChange(_ notification: Notification) {
|
@objc func containerChildrenDidChange(_ notification: Notification) {
|
||||||
rebuildSidebarItems()
|
rebuildSidebarItems(isReadFiltered: isReadFiltered)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func batchUpdateDidPerform(_ notification: Notification) {
|
@objc func batchUpdateDidPerform(_ notification: Notification) {
|
||||||
rebuildSidebarItems()
|
rebuildSidebarItems(isReadFiltered: isReadFiltered)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func displayNameDidChange(_ note: Notification) {
|
@objc func displayNameDidChange(_ note: Notification) {
|
||||||
rebuildSidebarItems()
|
rebuildSidebarItems(isReadFiltered: isReadFiltered)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func accountStateDidChange(_ note: Notification) {
|
@objc func accountStateDidChange(_ note: Notification) {
|
||||||
rebuildSidebarItems()
|
rebuildSidebarItems(isReadFiltered: isReadFiltered)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func userDidAddAccount(_ note: Notification) {
|
@objc func userDidAddAccount(_ note: Notification) {
|
||||||
rebuildSidebarItems()
|
rebuildSidebarItems(isReadFiltered: isReadFiltered)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func userDidDeleteAccount(_ note: Notification) {
|
@objc func userDidDeleteAccount(_ note: Notification) {
|
||||||
rebuildSidebarItems()
|
rebuildSidebarItems(isReadFiltered: isReadFiltered)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import SwiftUI
|
||||||
|
|
||||||
struct SidebarToolbarModifier: ViewModifier {
|
struct SidebarToolbarModifier: ViewModifier {
|
||||||
|
|
||||||
|
@EnvironmentObject private var sidebarModel: SidebarModel
|
||||||
@EnvironmentObject private var defaults: AppDefaults
|
@EnvironmentObject private var defaults: AppDefaults
|
||||||
@StateObject private var viewModel = SidebarToolbarModel()
|
@StateObject private var viewModel = SidebarToolbarModel()
|
||||||
|
|
||||||
|
@ -19,10 +20,16 @@ struct SidebarToolbarModifier: ViewModifier {
|
||||||
.toolbar {
|
.toolbar {
|
||||||
|
|
||||||
ToolbarItem(placement: .navigation) {
|
ToolbarItem(placement: .navigation) {
|
||||||
Button(action: {
|
Button (action: {
|
||||||
|
withAnimation {
|
||||||
|
sidebarModel.isReadFiltered.toggle()
|
||||||
|
}
|
||||||
}, label: {
|
}, label: {
|
||||||
AppAssets.filterInactiveImage
|
if sidebarModel.isReadFiltered {
|
||||||
.font(.title3)
|
AppAssets.filterActiveImage.font(.title3)
|
||||||
|
} else {
|
||||||
|
AppAssets.filterInactiveImage.font(.title3)
|
||||||
|
}
|
||||||
}).help("Filter Read Feeds")
|
}).help("Filter Read Feeds")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,16 +19,34 @@ struct SidebarView: View {
|
||||||
|
|
||||||
@ViewBuilder var body: some View {
|
@ViewBuilder var body: some View {
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
ZStack {
|
VStack {
|
||||||
NavigationLink(destination: TimelineContainerView(feeds: sidebarModel.selectedFeeds), isActive: $navigate) {
|
HStack {
|
||||||
EmptyView()
|
Spacer()
|
||||||
}.hidden()
|
Button (action: {
|
||||||
List(selection: $sidebarModel.selectedFeedIdentifiers) {
|
withAnimation {
|
||||||
rows
|
sidebarModel.isReadFiltered.toggle()
|
||||||
|
}
|
||||||
|
}, label: {
|
||||||
|
if sidebarModel.isReadFiltered {
|
||||||
|
AppAssets.filterActiveImage
|
||||||
|
} else {
|
||||||
|
AppAssets.filterInactiveImage
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.padding(.top).padding(.trailing)
|
||||||
|
.buttonStyle(PlainButtonStyle())
|
||||||
|
}
|
||||||
|
ZStack {
|
||||||
|
NavigationLink(destination: TimelineContainerView(feeds: sidebarModel.selectedFeeds), isActive: $navigate) {
|
||||||
|
EmptyView()
|
||||||
|
}.hidden()
|
||||||
|
List(selection: $sidebarModel.selectedFeedIdentifiers) {
|
||||||
|
rows
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onChange(of: sidebarModel.selectedFeedIdentifiers) { value in
|
||||||
|
navigate = !sidebarModel.selectedFeedIdentifiers.isEmpty
|
||||||
}
|
}
|
||||||
}
|
|
||||||
.onChange(of: sidebarModel.selectedFeedIdentifiers) { value in
|
|
||||||
navigate = !sidebarModel.selectedFeedIdentifiers.isEmpty
|
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
List {
|
List {
|
||||||
|
|
|
@ -25,7 +25,8 @@ class TimelineModel: ObservableObject, UndoableCommandRunner {
|
||||||
@Published var selectedArticleIDs = Set<String>()
|
@Published var selectedArticleIDs = Set<String>()
|
||||||
@Published var selectedArticleID: String? = .none
|
@Published var selectedArticleID: String? = .none
|
||||||
@Published var selectedArticles = [Article]()
|
@Published var selectedArticles = [Article]()
|
||||||
|
@Published var isReadFiltered = false
|
||||||
|
|
||||||
var undoManager: UndoManager?
|
var undoManager: UndoManager?
|
||||||
var undoableCommands = [UndoableCommand]()
|
var undoableCommands = [UndoableCommand]()
|
||||||
|
|
||||||
|
@ -36,7 +37,6 @@ class TimelineModel: ObservableObject, UndoableCommandRunner {
|
||||||
private var fetchSerialNumber = 0
|
private var fetchSerialNumber = 0
|
||||||
private let fetchRequestQueue = FetchRequestQueue()
|
private let fetchRequestQueue = FetchRequestQueue()
|
||||||
private var exceptionArticleFetcher: ArticleFetcher?
|
private var exceptionArticleFetcher: ArticleFetcher?
|
||||||
private var isReadFiltered = false
|
|
||||||
|
|
||||||
private var articles = [Article]() {
|
private var articles = [Article]() {
|
||||||
didSet {
|
didSet {
|
||||||
|
|
Loading…
Reference in New Issue