Merge pull request #111 from omnivore-app/feature/incremental-library-loading
Incremental Home Feed Loading [Apple]
This commit is contained in:
@ -12,15 +12,17 @@ extension HomeFeedViewModel {
|
||||
}
|
||||
|
||||
viewModel.bind(services: services)
|
||||
viewModel.loadItems(dataService: services.dataService, searchQuery: nil)
|
||||
viewModel.loadItems(dataService: services.dataService, searchQuery: nil, isRefresh: false)
|
||||
return viewModel
|
||||
}
|
||||
|
||||
func bind(services: Services) {
|
||||
performActionSubject.sink { [weak self] action in
|
||||
switch action {
|
||||
case let .refreshItems(query: query):
|
||||
self?.loadItems(dataService: services.dataService, searchQuery: query, isRefresh: true)
|
||||
case let .loadItems(query):
|
||||
self?.loadItems(dataService: services.dataService, searchQuery: query)
|
||||
self?.loadItems(dataService: services.dataService, searchQuery: query, isRefresh: false)
|
||||
case let .archive(linkId):
|
||||
self?.setLinkArchived(dataService: services.dataService, linkId: linkId, archived: true)
|
||||
case let .unarchive(linkId):
|
||||
@ -39,7 +41,7 @@ extension HomeFeedViewModel {
|
||||
.store(in: &subscriptions)
|
||||
}
|
||||
|
||||
private func loadItems(dataService: DataService, searchQuery: String?) {
|
||||
private func loadItems(dataService: DataService, searchQuery: String?, isRefresh: Bool) {
|
||||
// Clear offline highlights since we'll be populating new FeedItems with the correct highlights set
|
||||
dataService.clearHighlights()
|
||||
|
||||
@ -59,10 +61,10 @@ extension HomeFeedViewModel {
|
||||
}
|
||||
|
||||
dataService.libraryItemsPublisher(
|
||||
limit: 100,
|
||||
limit: 10,
|
||||
sortDescending: true,
|
||||
searchQuery: searchQuery,
|
||||
cursor: nil
|
||||
cursor: isRefresh ? nil : cursor
|
||||
)
|
||||
.sink(
|
||||
receiveCompletion: { [weak self] completion in
|
||||
@ -80,9 +82,10 @@ extension HomeFeedViewModel {
|
||||
if thisSearchIdx > 0, thisSearchIdx <= self?.receivedIdx ?? 0 {
|
||||
return
|
||||
}
|
||||
self?.items = result.items
|
||||
self?.items = isRefresh ? result.items : (self?.items ?? []) + result.items
|
||||
self?.isLoading = false
|
||||
self?.receivedIdx = thisSearchIdx
|
||||
self?.cursor = result.cursor
|
||||
stopNetworkActivityIndicator()
|
||||
}
|
||||
)
|
||||
|
||||
@ -2,9 +2,11 @@ import Foundation
|
||||
|
||||
public struct HomeFeedData {
|
||||
public let items: [FeedItem]
|
||||
public let cursor: String?
|
||||
|
||||
public init(items: [FeedItem]) {
|
||||
public init(items: [FeedItem], cursor: String?) {
|
||||
self.items = items
|
||||
self.cursor = cursor
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -71,7 +71,10 @@ public extension DataService {
|
||||
articlesSuccess: .init {
|
||||
QueryResult.success(
|
||||
result: HomeFeedData(
|
||||
items: try $0.edges(selection: articleEdgeSelection.list)
|
||||
items: try $0.edges(selection: articleEdgeSelection.list),
|
||||
cursor: try $0.pageInfo(selection: Selection.PageInfo {
|
||||
try $0.endCursor()
|
||||
})
|
||||
)
|
||||
)
|
||||
},
|
||||
|
||||
@ -10,6 +10,7 @@ public final class HomeFeedViewModel: ObservableObject {
|
||||
@Published public var items = [FeedItem]()
|
||||
@Published public var isLoading = false
|
||||
@Published public var showPushNotificationPrimer = false
|
||||
public var cursor: String?
|
||||
|
||||
// These are used to make sure we handle search result
|
||||
// responses in the right order
|
||||
@ -17,6 +18,7 @@ public final class HomeFeedViewModel: ObservableObject {
|
||||
public var receivedIdx = 0
|
||||
|
||||
public enum Action {
|
||||
case refreshItems(query: String)
|
||||
case loadItems(query: String)
|
||||
case archive(linkId: String)
|
||||
case unarchive(linkId: String)
|
||||
@ -31,15 +33,14 @@ public final class HomeFeedViewModel: ObservableObject {
|
||||
self.detailViewModelCreator = detailViewModelCreator
|
||||
}
|
||||
|
||||
func itemAppeared(item: FeedItem) {
|
||||
func itemAppeared(item: FeedItem, searchQuery: String) {
|
||||
if isLoading { return }
|
||||
let itemIndex = items.firstIndex(where: { $0.id == item.id })
|
||||
let thresholdIndex = items.index(items.endIndex, offsetBy: -5)
|
||||
|
||||
// Check if user has scrolled to the last five items in the list
|
||||
if itemIndex == thresholdIndex {
|
||||
print("load more items triggered")
|
||||
// performActionSubject.send(.loadItems)
|
||||
if let itemIndex = itemIndex, itemIndex > thresholdIndex, items.count < thresholdIndex + 10 {
|
||||
performActionSubject.send(.loadItems(query: searchQuery))
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,7 +125,7 @@ public struct HomeFeedView: View {
|
||||
.opacity(0)
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
.onAppear {
|
||||
viewModel.itemAppeared(item: item)
|
||||
viewModel.itemAppeared(item: item, searchQuery: searchQuery)
|
||||
}
|
||||
FeedCard(item: item)
|
||||
}.contextMenu {
|
||||
@ -268,6 +269,6 @@ public struct HomeFeedView: View {
|
||||
}
|
||||
|
||||
private func refresh() {
|
||||
viewModel.performActionSubject.send(.loadItems(query: searchQuery))
|
||||
viewModel.performActionSubject.send(.refreshItems(query: searchQuery))
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user