Reimplement Article Read Filter
This commit is contained in:
parent
8f346af250
commit
59528f48b1
|
@ -28,7 +28,6 @@ class TimelineModel: ObservableObject, UndoableCommandRunner {
|
|||
@Published var nameForDisplay = ""
|
||||
@Published var selectedTimelineItemIDs = Set<String>() // Don't use directly. Use selectedTimelineItemsPublisher
|
||||
@Published var selectedTimelineItemID: String? = nil // Don't use directly. Use selectedTimelineItemsPublisher
|
||||
@Published var isReadFiltered: Bool? = nil
|
||||
|
||||
var selectedArticles = [Article]()
|
||||
|
||||
|
@ -37,11 +36,13 @@ class TimelineModel: ObservableObject, UndoableCommandRunner {
|
|||
var selectedTimelineItemsPublisher: AnyPublisher<[TimelineItem], Never>?
|
||||
var selectedArticlesPublisher: AnyPublisher<[Article], Never>?
|
||||
var articleStatusChangePublisher: AnyPublisher<Set<String>, Never>?
|
||||
var readFilterAndFeedsPublisher: AnyPublisher<([Feed], Bool?), Never>?
|
||||
|
||||
var markAllAsReadSubject = PassthroughSubject<Void, Never>()
|
||||
var toggleReadStatusForSelectedArticlesSubject = PassthroughSubject<Void, Never>()
|
||||
var toggleStarredStatusForSelectedArticlesSubject = PassthroughSubject<Void, Never>()
|
||||
var openSelectedArticlesInBrowserSubject = PassthroughSubject<Void, Never>()
|
||||
var changeReadFilterSubject = PassthroughSubject<Bool, Never>()
|
||||
|
||||
var readFilterEnabledTable = [FeedIdentifier: Bool]()
|
||||
|
||||
|
@ -58,7 +59,7 @@ class TimelineModel: ObservableObject, UndoableCommandRunner {
|
|||
init(delegate: TimelineModelDelegate) {
|
||||
self.delegate = delegate
|
||||
subscribeToUserDefaultsChanges()
|
||||
subscribeToReadFilterChanges()
|
||||
subscribeToReadFilterAndFeedChanges()
|
||||
subscribeToArticleFetchChanges()
|
||||
subscribeToArticleSelectionChanges()
|
||||
subscribeToArticleStatusChanges()
|
||||
|
@ -67,15 +68,6 @@ class TimelineModel: ObservableObject, UndoableCommandRunner {
|
|||
subscribeToOpenInBrowserEvents()
|
||||
}
|
||||
|
||||
// MARK: API
|
||||
|
||||
func toggleReadFilter() {
|
||||
// guard let filter = isReadFiltered, let feedID = feeds.first?.feedID else { return }
|
||||
// readFilterEnabledTable[feedID] = !filter
|
||||
// isReadFiltered = !filter
|
||||
// self.fetchArticles()
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func goToNextUnread() -> Bool {
|
||||
// var startIndex: Int
|
||||
|
@ -142,32 +134,6 @@ private extension TimelineModel {
|
|||
// }.store(in: &cancellables)
|
||||
// }
|
||||
|
||||
// TODO: Don't forget to redo this!!!
|
||||
func subscribeToReadFilterChanges() {
|
||||
guard let selectedFeedsPublisher = delegate?.selectedFeedsPublisher else { return }
|
||||
|
||||
selectedFeedsPublisher.sink { [weak self] feeds in
|
||||
guard let self = self else { return }
|
||||
|
||||
guard feeds.count == 1, let timelineFeed = feeds.first else {
|
||||
self.isReadFiltered = nil
|
||||
return
|
||||
}
|
||||
|
||||
guard timelineFeed.defaultReadFilterType != .alwaysRead else {
|
||||
self.isReadFiltered = nil
|
||||
return
|
||||
}
|
||||
|
||||
if let feedID = timelineFeed.feedID, let readFilterEnabled = self.readFilterEnabledTable[feedID] {
|
||||
self.isReadFiltered = readFilterEnabled
|
||||
} else {
|
||||
self.isReadFiltered = timelineFeed.defaultReadFilterType == .read
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func subscribeToUserDefaultsChanges() {
|
||||
let kickStartNote = Notification(name: Notification.Name("Kick Start"))
|
||||
NotificationCenter.default.publisher(for: UserDefaults.didChangeNotification)
|
||||
|
@ -178,17 +144,60 @@ private extension TimelineModel {
|
|||
}.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func subscribeToArticleFetchChanges() {
|
||||
func subscribeToReadFilterAndFeedChanges() {
|
||||
guard let selectedFeedsPublisher = delegate?.selectedFeedsPublisher else { return }
|
||||
|
||||
let toggledReadFilterPublisher = changeReadFilterSubject
|
||||
.map { Optional($0) }
|
||||
.withLatestFrom(selectedFeedsPublisher, resultSelector: { ($1, $0) })
|
||||
.share()
|
||||
|
||||
toggledReadFilterPublisher
|
||||
.sink { [weak self] (selectedFeeds, readFiltered) in
|
||||
if let feedID = selectedFeeds.first?.feedID {
|
||||
self?.readFilterEnabledTable[feedID] = readFiltered
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
let feedsReadFilterPublisher = selectedFeedsPublisher
|
||||
.map { [weak self] feeds -> ([Feed], Bool?) in
|
||||
guard let self = self else { return (feeds, nil) }
|
||||
|
||||
guard feeds.count == 1, let timelineFeed = feeds.first else {
|
||||
return (feeds, nil)
|
||||
}
|
||||
|
||||
guard timelineFeed.defaultReadFilterType != .alwaysRead else {
|
||||
return (feeds, nil)
|
||||
}
|
||||
|
||||
if let feedID = timelineFeed.feedID, let readFilterEnabled = self.readFilterEnabledTable[feedID] {
|
||||
return (feeds, readFilterEnabled)
|
||||
} else {
|
||||
return (feeds, timelineFeed.defaultReadFilterType == .read)
|
||||
}
|
||||
}
|
||||
|
||||
readFilterAndFeedsPublisher = toggledReadFilterPublisher
|
||||
.merge(with: feedsReadFilterPublisher)
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func subscribeToArticleFetchChanges() {
|
||||
guard let readFilterAndFeedsPublisher = readFilterAndFeedsPublisher,
|
||||
let selectedFeedsPublisher = delegate?.selectedFeedsPublisher else { return }
|
||||
|
||||
let sortDirectionPublisher = sortDirectionSubject.removeDuplicates()
|
||||
let groupByPublisher = groupByFeedSubject.removeDuplicates()
|
||||
|
||||
timelineItemsPublisher = selectedFeedsPublisher
|
||||
.map { [weak self] feeds -> Set<Article> in
|
||||
return self?.fetchArticles(feeds: feeds) ?? Set<Article>()
|
||||
timelineItemsPublisher = readFilterAndFeedsPublisher
|
||||
.map { [weak self] (feeds, readFilter) -> Set<Article> in
|
||||
return self?.fetchArticles(feeds: feeds, isReadFiltered: readFilter) ?? Set<Article>()
|
||||
}
|
||||
.combineLatest(sortDirectionPublisher, groupByPublisher)
|
||||
.compactMap { [weak self] articles, sortDirection, groupBy in
|
||||
print("************")
|
||||
let sortedArticles = Array(articles).sortedByDate(sortDirection ? .orderedDescending : .orderedAscending, groupByFeed: groupBy)
|
||||
return self?.buildTimelineItems(articles: sortedArticles) ?? TimelineItems()
|
||||
}
|
||||
|
@ -343,7 +352,7 @@ private extension TimelineModel {
|
|||
|
||||
// MARK: Article Fetching
|
||||
|
||||
func fetchArticles(feeds: [Feed]) -> Set<Article> {
|
||||
func fetchArticles(feeds: [Feed], isReadFiltered: Bool?) -> Set<Article> {
|
||||
if feeds.isEmpty {
|
||||
return Set<Article>()
|
||||
}
|
||||
|
|
|
@ -12,25 +12,26 @@ struct TimelineToolbarModifier: ViewModifier {
|
|||
|
||||
@EnvironmentObject private var sceneModel: SceneModel
|
||||
@EnvironmentObject private var timelineModel: TimelineModel
|
||||
|
||||
@State private var isReadFiltered: Bool? = nil
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.toolbar {
|
||||
#if os(iOS)
|
||||
ToolbarItem(placement: .primaryAction) {
|
||||
Button {
|
||||
withAnimation {
|
||||
timelineModel.toggleReadFilter()
|
||||
if let filter = isReadFiltered {
|
||||
timelineModel.changeReadFilterSubject.send(!filter)
|
||||
}
|
||||
} label: {
|
||||
if timelineModel.isReadFiltered ?? false {
|
||||
if isReadFiltered ?? false {
|
||||
AppAssets.filterActiveImage.font(.title3)
|
||||
} else {
|
||||
AppAssets.filterInactiveImage.font(.title3)
|
||||
}
|
||||
}
|
||||
.hidden(timelineModel.isReadFiltered == nil)
|
||||
.help(timelineModel.isReadFiltered ?? false ? "Show Read Articles" : "Filter Read Articles")
|
||||
.hidden(isReadFiltered == nil)
|
||||
.help(isReadFiltered ?? false ? "Show Read Articles" : "Filter Read Articles")
|
||||
}
|
||||
|
||||
ToolbarItem(placement: .bottomBar) {
|
||||
|
@ -48,6 +49,9 @@ struct TimelineToolbarModifier: ViewModifier {
|
|||
}
|
||||
#endif
|
||||
}
|
||||
.onReceive(timelineModel.readFilterAndFeedsPublisher!) { (_, filtered) in
|
||||
isReadFiltered = filtered
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ struct TimelineView: View {
|
|||
@EnvironmentObject private var timelineModel: TimelineModel
|
||||
@State private var timelineItems = TimelineItems()
|
||||
@State private var timelineItemFrames = [String: CGRect]()
|
||||
@State private var isReadFiltered: Bool? = nil
|
||||
|
||||
@ViewBuilder var body: some View {
|
||||
GeometryReader { geometryReaderProxy in
|
||||
|
@ -22,20 +23,20 @@ struct TimelineView: View {
|
|||
TimelineSortOrderView()
|
||||
Spacer()
|
||||
Button (action: {
|
||||
withAnimation {
|
||||
timelineModel.toggleReadFilter()
|
||||
if let filtered = isReadFiltered {
|
||||
timelineModel.changeReadFilterSubject.send(!filtered)
|
||||
}
|
||||
}, label: {
|
||||
if timelineModel.isReadFiltered ?? false {
|
||||
if isReadFiltered ?? false {
|
||||
AppAssets.filterActiveImage
|
||||
} else {
|
||||
AppAssets.filterInactiveImage
|
||||
}
|
||||
})
|
||||
.hidden(timelineModel.isReadFiltered == nil)
|
||||
.hidden(isReadFiltered == nil)
|
||||
.padding(.top, 8).padding(.trailing)
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
.help(timelineModel.isReadFiltered ?? false ? "Show Read Articles" : "Filter Read Articles")
|
||||
.help(isReadFiltered ?? false ? "Show Read Articles" : "Filter Read Articles")
|
||||
}
|
||||
ScrollViewReader { scrollViewProxy in
|
||||
List(timelineItems.items, selection: $timelineModel.selectedTimelineItemIDs) { timelineItem in
|
||||
|
@ -62,6 +63,9 @@ struct TimelineView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
.onReceive(timelineModel.readFilterAndFeedsPublisher!) { (_, filtered) in
|
||||
isReadFiltered = filtered
|
||||
}
|
||||
.onReceive(timelineModel.timelineItemsPublisher!) { items in
|
||||
withAnimation {
|
||||
timelineItems = items
|
||||
|
|
Loading…
Reference in New Issue