diff --git a/Multiplatform/Shared/Sidebar/SidebarView.swift b/Multiplatform/Shared/Sidebar/SidebarView.swift index 4c66c9ab6..69d533210 100644 --- a/Multiplatform/Shared/Sidebar/SidebarView.swift +++ b/Multiplatform/Shared/Sidebar/SidebarView.swift @@ -33,7 +33,7 @@ struct SidebarView: View { AppAssets.filterInactiveImage } }) - .padding(.top).padding(.trailing) + .padding(.top, 8).padding(.trailing) .buttonStyle(PlainButtonStyle()) } ZStack { diff --git a/Multiplatform/Shared/Timeline/TimelineContainerView.swift b/Multiplatform/Shared/Timeline/TimelineContainerView.swift index a1ed6ced9..f2b370482 100644 --- a/Multiplatform/Shared/Timeline/TimelineContainerView.swift +++ b/Multiplatform/Shared/Timeline/TimelineContainerView.swift @@ -23,7 +23,7 @@ struct TimelineContainerView: View { .environmentObject(sceneModel.timelineModel) .onAppear { sceneModel.timelineModel.undoManager = undoManager - sceneModel.timelineModel.rebuildTimelineItems(feeds: feeds) + sceneModel.timelineModel.fetchArticles(feeds: feeds) } } else { EmptyView() diff --git a/Multiplatform/Shared/Timeline/TimelineModel.swift b/Multiplatform/Shared/Timeline/TimelineModel.swift index ffa50b0f5..b05879ae6 100644 --- a/Multiplatform/Shared/Timeline/TimelineModel.swift +++ b/Multiplatform/Shared/Timeline/TimelineModel.swift @@ -22,8 +22,8 @@ class TimelineModel: ObservableObject, UndoableCommandRunner { @Published var nameForDisplay = "" @Published var timelineItems = [TimelineItem]() - @Published var selectedArticleIDs = Set() - @Published var selectedArticleID: String? = .none + @Published var selectedArticleIDs = Set() // Don't use directly. Use selectedArticles + @Published var selectedArticleID: String? = .none // Don't use directly. Use selectedArticles @Published var selectedArticles = [Article]() @Published var isReadFiltered = false @@ -33,6 +33,7 @@ class TimelineModel: ObservableObject, UndoableCommandRunner { private var selectedArticleIDsCancellable: AnyCancellable? private var selectedArticleIDCancellable: AnyCancellable? private var selectedArticlesCancellable: AnyCancellable? + private var selectedReadFilteredCancellable: AnyCancellable? private var fetchSerialNumber = 0 private let fetchRequestQueue = FetchRequestQueue() @@ -95,11 +96,16 @@ class TimelineModel: ObservableObject, UndoableCommandRunner { } } } + + selectedReadFilteredCancellable = $isReadFiltered.sink { [weak self] filter in + guard let self = self else { return } + self.rebuildTimelineItems(isReadFiltered: filter) + } } // MARK: API - func rebuildTimelineItems(feeds: [Feed]) { + func fetchArticles(feeds: [Feed]) { if feeds.count == 1 { nameForDisplay = feeds.first!.nameForDisplay } else { @@ -259,8 +265,20 @@ private extension TimelineModel { func replaceArticles(with unsortedArticles: Set
) { articles = Array(unsortedArticles).sortedByDate(sortDirection ? .orderedDescending : .orderedAscending, groupByFeed: groupByFeed) - timelineItems = articles.map { TimelineItem(article: $0) } + rebuildTimelineItems(isReadFiltered: isReadFiltered) // TODO: Update unread counts and other item done in didSet on AppKit } + func rebuildTimelineItems(isReadFiltered: Bool) { + let selectedArticleIDs = selectedArticles.map { $0.articleID } + + timelineItems = articles.compactMap { article in + if isReadFiltered && article.status.read && !selectedArticleIDs.contains(article.articleID) { + return nil + } else { + return TimelineItem(article: article) + } + } + } + } diff --git a/Multiplatform/Shared/Timeline/TimelineView.swift b/Multiplatform/Shared/Timeline/TimelineView.swift index f5b4bb89e..22abe3b71 100644 --- a/Multiplatform/Shared/Timeline/TimelineView.swift +++ b/Multiplatform/Shared/Timeline/TimelineView.swift @@ -15,16 +15,34 @@ struct TimelineView: View { @ViewBuilder var body: some View { #if os(macOS) - ZStack { - NavigationLink(destination: ArticleContainerView(articles: timelineModel.selectedArticles), isActive: $navigate) { - EmptyView() - }.hidden() - List(timelineModel.timelineItems, selection: $timelineModel.selectedArticleIDs) { timelineItem in - TimelineItemView(timelineItem: timelineItem) + VStack { + HStack { + Spacer() + Button (action: { + withAnimation { + timelineModel.isReadFiltered.toggle() + } + }, label: { + if timelineModel.isReadFiltered { + AppAssets.filterActiveImage + } else { + AppAssets.filterInactiveImage + } + }) + .padding(.top, 8).padding(.trailing) + .buttonStyle(PlainButtonStyle()) + } + ZStack { + NavigationLink(destination: ArticleContainerView(articles: timelineModel.selectedArticles), isActive: $navigate) { + EmptyView() + }.hidden() + List(timelineModel.timelineItems, selection: $timelineModel.selectedArticleIDs) { timelineItem in + TimelineItemView(timelineItem: timelineItem) + } + } + .onChange(of: timelineModel.selectedArticleIDs) { value in + navigate = !timelineModel.selectedArticleIDs.isEmpty } - } - .onChange(of: timelineModel.selectedArticleIDs) { value in - navigate = !timelineModel.selectedArticleIDs.isEmpty } #else List(timelineModel.timelineItems) { timelineItem in