Make macOS timeline load synchronous and add an id to list that changes when the feed selection changes to reset the scroll

This commit is contained in:
Maurice Parker 2020-07-27 10:36:37 -05:00
parent 6b08b1e48b
commit a48c894985
2 changed files with 56 additions and 4 deletions

View File

@ -28,6 +28,7 @@ class TimelineModel: ObservableObject, UndoableCommandRunner {
@Published var nameForDisplay = "" @Published var nameForDisplay = ""
@Published var selectedTimelineItemIDs = Set<String>() // Don't use directly. Use selectedTimelineItemsPublisher @Published var selectedTimelineItemIDs = Set<String>() // Don't use directly. Use selectedTimelineItemsPublisher
@Published var selectedTimelineItemID: String? = nil // Don't use directly. Use selectedTimelineItemsPublisher @Published var selectedTimelineItemID: String? = nil // Don't use directly. Use selectedTimelineItemsPublisher
@Published var listID = ""
var selectedArticles: [Article] { var selectedArticles: [Article] {
return selectedTimelineItems.map { $0.article } return selectedTimelineItems.map { $0.article }
@ -253,6 +254,12 @@ private extension TimelineModel {
} }
.assign(to: &$nameForDisplay) .assign(to: &$nameForDisplay)
selectedFeedsPublisher
.map { _ in
return UUID().uuidString
}
.assign(to: &$listID)
// Clear the selected timeline items when the selected feed(s) change // Clear the selected timeline items when the selected feed(s) change
selectedFeedsPublisher selectedFeedsPublisher
.sink { [weak self] _ in .sink { [weak self] _ in
@ -308,7 +315,7 @@ private extension TimelineModel {
// Download articles and transform them into timeline items // Download articles and transform them into timeline items
let inputTimelineItemsPublisher = readFilterAndFeedsPublisher let inputTimelineItemsPublisher = readFilterAndFeedsPublisher
.flatMap { (feeds, readFilter) in .flatMap { (feeds, readFilter) in
Self.fetchArticles(feeds: feeds, isReadFiltered: readFilter) Self.fetchArticlesPublisher(feeds: feeds, isReadFiltered: readFilter)
} }
.combineLatest(sortDirectionPublisher, groupByPublisher) .combineLatest(sortDirectionPublisher, groupByPublisher)
.compactMap { articles, sortDirection, groupBy -> TimelineItems in .compactMap { articles, sortDirection, groupBy -> TimelineItems in
@ -331,7 +338,7 @@ private extension TimelineModel {
let downloadTimelineItemsPublisher = accountDidDownloadPublisher let downloadTimelineItemsPublisher = accountDidDownloadPublisher
.withLatestFrom(readFilterAndFeedsPublisher) .withLatestFrom(readFilterAndFeedsPublisher)
.flatMap { (feeds, readFilter) in .flatMap { (feeds, readFilter) in
Self.fetchArticles(feeds: feeds, isReadFiltered: readFilter) Self.fetchArticlesPublisher(feeds: feeds, isReadFiltered: readFilter)
} }
.withLatestFrom(articlesSubject, sortDirectionPublisher, groupByPublisher, resultSelector: { (downloadArticles, latest) in .withLatestFrom(articlesSubject, sortDirectionPublisher, groupByPublisher, resultSelector: { (downloadArticles, latest) in
return (downloadArticles, latest.0, latest.1, latest.2) return (downloadArticles, latest.0, latest.1, latest.2)
@ -501,13 +508,54 @@ private extension TimelineModel {
// MARK: Article Fetching // MARK: Article Fetching
static func fetchArticles(feeds: [Feed], isReadFiltered: Bool?) -> Future<Set<Article>, Never> { func fetchArticles(feeds: [Feed], isReadFiltered: Bool?) -> Set<Article> {
if feeds.isEmpty {
return Set<Article>()
}
var fetchedArticles = Set<Article>()
for feed in feeds {
if isReadFiltered ?? true {
if let articles = try? feed.fetchUnreadArticles() {
fetchedArticles.formUnion(articles)
}
} else {
if let articles = try? feed.fetchArticles() {
fetchedArticles.formUnion(articles)
}
}
}
return fetchedArticles
}
static func fetchArticlesPublisher(feeds: [Feed], isReadFiltered: Bool?) -> Future<Set<Article>, Never> {
return Future<Set<Article>, Never> { promise in return Future<Set<Article>, Never> { promise in
if feeds.isEmpty { if feeds.isEmpty {
promise(.success(Set<Article>())) promise(.success(Set<Article>()))
} }
#if os(macOS)
var result = Set<Article>()
for feed in feeds {
if isReadFiltered ?? true {
if let articles = try? feed.fetchUnreadArticles() {
result.formUnion(articles)
}
} else {
if let articles = try? feed.fetchArticles() {
result.formUnion(articles)
}
}
}
promise(.success(result))
#else
let group = DispatchGroup() let group = DispatchGroup()
var result = Set<Article>() var result = Set<Article>()
@ -534,6 +582,8 @@ private extension TimelineModel {
promise(.success(result)) promise(.success(result))
} }
#endif
} }
} }

View File

@ -46,6 +46,7 @@ struct TimelineView: View {
TimelineItemView(selected: selected, width: geometryReaderProxy.size.width, timelineItem: timelineItem) TimelineItemView(selected: selected, width: geometryReaderProxy.size.width, timelineItem: timelineItem)
.background(TimelineItemFramePreferenceView(timelineItem: timelineItem)) .background(TimelineItemFramePreferenceView(timelineItem: timelineItem))
} }
.id(timelineModel.listID)
.onPreferenceChange(TimelineItemFramePreferenceKey.self) { preferences in .onPreferenceChange(TimelineItemFramePreferenceKey.self) { preferences in
for pref in preferences { for pref in preferences {
timelineItemFrames[pref.articleID] = pref.frame timelineItemFrames[pref.articleID] = pref.frame
@ -80,6 +81,7 @@ struct TimelineView: View {
}.buttonStyle(PlainButtonStyle()) }.buttonStyle(PlainButtonStyle())
} }
} }
.id(timelineModel.listID)
.onPreferenceChange(TimelineItemFramePreferenceKey.self) { preferences in .onPreferenceChange(TimelineItemFramePreferenceKey.self) { preferences in
for pref in preferences { for pref in preferences {
timelineItemFrames[pref.articleID] = pref.frame timelineItemFrames[pref.articleID] = pref.frame