Add basic timeline fetching
This commit is contained in:
parent
c1eb9ab927
commit
74f34b1c78
@ -20,10 +20,6 @@ final class SceneModel: ObservableObject {
|
||||
|
||||
extension SceneModel: SidebarModelDelegate {
|
||||
|
||||
func sidebarSelectionDidChange(_: SidebarModel, feeds: [Feed]?) {
|
||||
print("**** sidebar selection changed ***")
|
||||
}
|
||||
|
||||
func unreadCount(for feed: Feed) -> Int {
|
||||
// TODO: Get the count from the timeline if Feed is the current timeline
|
||||
return feed.unreadCount
|
||||
|
@ -11,7 +11,6 @@ import RSCore
|
||||
import Account
|
||||
|
||||
protocol SidebarModelDelegate: class {
|
||||
func sidebarSelectionDidChange(_: SidebarModel, feeds: [Feed]?)
|
||||
func unreadCount(for: Feed) -> Int
|
||||
}
|
||||
|
||||
|
@ -30,10 +30,3 @@ struct TimelineContainerView: View {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct TimelineContainerView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
TimelineContainerView()
|
||||
.environmentObject(SceneModel())
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,11 @@ struct TimelineItemView: View {
|
||||
var timelineItem: TimelineItem
|
||||
|
||||
var body: some View {
|
||||
Text(verbatim: timelineItem.article.title ?? "N/A")
|
||||
VStack {
|
||||
Text(verbatim: timelineItem.article.title ?? "N/A")
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
Divider()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
import Foundation
|
||||
import RSCore
|
||||
import Account
|
||||
import Articles
|
||||
|
||||
protocol TimelineModelDelegate: class {
|
||||
func timelineRequestedWebFeedSelection(_: TimelineModel, webFeed: WebFeed)
|
||||
@ -21,6 +22,28 @@ class TimelineModel: ObservableObject {
|
||||
@Published var timelineItems = [TimelineItem]()
|
||||
|
||||
private var feeds = [Feed]()
|
||||
private var fetchSerialNumber = 0
|
||||
private let fetchRequestQueue = FetchRequestQueue()
|
||||
private var exceptionArticleFetcher: ArticleFetcher?
|
||||
private var isReadFiltered = false
|
||||
|
||||
private var articles = [Article]()
|
||||
|
||||
private var sortDirection = AppDefaults.timelineSortDirection {
|
||||
didSet {
|
||||
if sortDirection != oldValue {
|
||||
sortParametersDidChange()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var groupByFeed = AppDefaults.timelineGroupByFeed {
|
||||
didSet {
|
||||
if groupByFeed != oldValue {
|
||||
sortParametersDidChange()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
}
|
||||
@ -29,12 +52,72 @@ class TimelineModel: ObservableObject {
|
||||
|
||||
func rebuildTimelineItems(_ feed: Feed) {
|
||||
feeds = [feed]
|
||||
fetchAndReplaceArticlesAsync()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private extension TimelineModel {
|
||||
|
||||
func sortParametersDidChange() {
|
||||
performBlockAndRestoreSelection {
|
||||
let unsortedArticles = Set(articles)
|
||||
replaceArticles(with: unsortedArticles)
|
||||
}
|
||||
}
|
||||
|
||||
func performBlockAndRestoreSelection(_ block: (() -> Void)) {
|
||||
// let savedSelection = selectedArticleIDs()
|
||||
block()
|
||||
// restoreSelection(savedSelection)
|
||||
}
|
||||
|
||||
// MARK: Article Fetching
|
||||
|
||||
func fetchAndReplaceArticlesAsync() {
|
||||
cancelPendingAsyncFetches()
|
||||
|
||||
var fetchers = feeds as [ArticleFetcher]
|
||||
if let fetcher = exceptionArticleFetcher {
|
||||
fetchers.append(fetcher)
|
||||
exceptionArticleFetcher = nil
|
||||
}
|
||||
|
||||
fetchUnsortedArticlesAsync(for: fetchers) { [weak self] (articles) in
|
||||
self?.replaceArticles(with: articles)
|
||||
}
|
||||
}
|
||||
|
||||
func cancelPendingAsyncFetches() {
|
||||
fetchSerialNumber += 1
|
||||
fetchRequestQueue.cancelAllRequests()
|
||||
}
|
||||
|
||||
func fetchUnsortedArticlesAsync(for representedObjects: [Any], completion: @escaping ArticleSetBlock) {
|
||||
// The callback will *not* be called if the fetch is no longer relevant — that is,
|
||||
// if it’s been superseded by a newer fetch, or the timeline was emptied, etc., it won’t get called.
|
||||
precondition(Thread.isMainThread)
|
||||
cancelPendingAsyncFetches()
|
||||
let fetchOperation = FetchRequestOperation(id: fetchSerialNumber, readFilter: isReadFiltered ?? true, representedObjects: representedObjects) { [weak self] (articles, operation) in
|
||||
precondition(Thread.isMainThread)
|
||||
guard !operation.isCanceled, let strongSelf = self, operation.id == strongSelf.fetchSerialNumber else {
|
||||
return
|
||||
}
|
||||
completion(articles)
|
||||
}
|
||||
fetchRequestQueue.add(fetchOperation)
|
||||
}
|
||||
|
||||
func replaceArticles(with unsortedArticles: Set<Article>) {
|
||||
articles = Array(unsortedArticles).sortedByDate(sortDirection, groupByFeed: groupByFeed)
|
||||
timelineItems = articles.map { TimelineItem(article: $0) }
|
||||
|
||||
// TODO: Update unread counts and other item done in didSet on AppKit
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Notifications
|
||||
|
||||
}
|
||||
|
@ -13,12 +13,19 @@ struct TimelineView: View {
|
||||
@EnvironmentObject private var timelineModel: TimelineModel
|
||||
|
||||
var body: some View {
|
||||
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
|
||||
}
|
||||
}
|
||||
|
||||
struct TimelineView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
TimelineView()
|
||||
ScrollView {
|
||||
LazyVStack() {
|
||||
ForEach(timelineModel.timelineItems) { timelineItem in
|
||||
TimelineItemView(timelineItem: timelineItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// var body: some View {
|
||||
// List(timelineModel.timelineItems) { timelineItem in
|
||||
// TimelineItemView(timelineItem: timelineItem)
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user