diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/Components/LibraryItemFetcher.swift b/apple/OmnivoreKit/Sources/App/Views/Home/Components/LibraryItemFetcher.swift index ebe8626cf..bf6abc29b 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/Components/LibraryItemFetcher.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/Components/LibraryItemFetcher.swift @@ -17,6 +17,7 @@ import Views @AppStorage(UserDefaultKey.lastSelectedFeaturedItemFilter.rawValue) var featureFilter = FeaturedItemFilter.continueReading.rawValue var cursor: String? + var totalCount: Int? // These are used to make sure we handle search result // responses in the right order @@ -82,6 +83,8 @@ import Views receivedIdx = thisSearchIdx cursor = queryResult.cursor + totalCount = queryResult.totalCount + if let username = dataService.currentViewer?.username { await dataService.prefetchPages(itemIDs: newItems.map(\.unwrappedID), username: username) } @@ -112,10 +115,15 @@ import Views BadgeCountHandler.updateBadgeCount(dataService: dataService) } - func loadMoreItems(dataService: DataService, filterState: FetcherFilterState) async { + func loadMoreItems(dataService: DataService, filterState: FetcherFilterState, loadCursor: String? = nil) async { if let appliedFilter = filterState.appliedFilter, appliedFilter.shouldRemoteSearch { let idx = max(items.count - 1, 0) - await loadSearchQuery(dataService: dataService, filterState: filterState, isRefresh: false, loadCursor: idx.description) + await loadSearchQuery( + dataService: dataService, + filterState: filterState, + isRefresh: false, + loadCursor: loadCursor ?? idx.description + ) } } diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift index b2ef10d81..1c98bab9a 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift @@ -647,7 +647,7 @@ struct AnimatingCellHeight: AnimatableModifier { } var listItems: some View { - ForEach(Array(viewModel.fetcher.items.enumerated()), id: \.1.unwrappedID) { _, item in + ForEach(Array(viewModel.fetcher.items.enumerated()), id: \.1.unwrappedID) { idx, item in let horizontalInset = CGFloat(UIDevice.isIPad ? 20 : 10) LibraryItemListNavigationLink( @@ -680,6 +680,13 @@ struct AnimatingCellHeight: AnimatableModifier { swipeActionButton(action: action, item: item) } } + .onAppear { + if idx >= viewModel.fetcher.items.count - 5 { + Task { + await viewModel.loadMore(dataService: dataService, loadCursor: idx.description) + } + } + } } } @@ -1028,8 +1035,8 @@ struct BottomView: View { AnyView(Color.clear) } else { AnyView(HStack { - if !autoLoading { - Text("You are all caught up.") + if let totalCount = viewModel.fetcher.totalCount { + Text("\(viewModel.fetcher.items.count) of \(totalCount) items.") } Spacer() if viewModel.isLoading { @@ -1040,7 +1047,11 @@ struct BottomView: View { await viewModel.loadMore(dataService: dataService) } }, label: { - Text("Refresh library") + if let totalCount = viewModel.fetcher.totalCount { + Text("Fetch more.") + } else { + Text("Refresh") + } }) .foregroundColor(Color.blue) } diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift index 062eb00c6..fdbe7e2a7 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift @@ -83,7 +83,7 @@ import Views } } - func loadMore(dataService: DataService) async { + func loadMore(dataService: DataService, loadCursor: String? = nil) async { if isLoading { return } let start = Date.now @@ -93,7 +93,7 @@ import Views } isLoading = true - await fetcher.loadMoreItems(dataService: dataService, filterState: filterState) + await fetcher.loadMoreItems(dataService: dataService, filterState: filterState, loadCursor: loadCursor) isLoading = false lastMoreFetched = start diff --git a/apple/OmnivoreKit/Sources/Models/DataModels/FeedItem.swift b/apple/OmnivoreKit/Sources/Models/DataModels/FeedItem.swift index 8c999c3da..97892c561 100644 --- a/apple/OmnivoreKit/Sources/Models/DataModels/FeedItem.swift +++ b/apple/OmnivoreKit/Sources/Models/DataModels/FeedItem.swift @@ -5,10 +5,12 @@ import Utils public struct LinkedItemQueryResult { public let itemIDs: [NSManagedObjectID] public let cursor: String? + public let totalCount: Int? - public init(itemIDs: [NSManagedObjectID], cursor: String?) { + public init(itemIDs: [NSManagedObjectID], cursor: String?, totalCount: Int?) { self.itemIDs = itemIDs self.cursor = cursor + self.totalCount = totalCount } } diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Public/LinkedItemLoading.swift b/apple/OmnivoreKit/Sources/Services/DataService/Public/LinkedItemLoading.swift index d0e315318..e9d1028da 100644 --- a/apple/OmnivoreKit/Sources/Services/DataService/Public/LinkedItemLoading.swift +++ b/apple/OmnivoreKit/Sources/Services/DataService/Public/LinkedItemLoading.swift @@ -51,7 +51,7 @@ public extension DataService { throw BasicError.message(messageText: "CoreData error") } - return LinkedItemQueryResult(itemIDs: itemIDs, cursor: fetchResult.cursor) + return LinkedItemQueryResult(itemIDs: itemIDs, cursor: fetchResult.cursor, totalCount: fetchResult.totalCount) } /// Requests a single `LinkedItem` from the server and stores it in CoreData diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Queries/LinkedItemNetworkQuery.swift b/apple/OmnivoreKit/Sources/Services/DataService/Queries/LinkedItemNetworkQuery.swift index 649c353dd..3ad941930 100644 --- a/apple/OmnivoreKit/Sources/Services/DataService/Queries/LinkedItemNetworkQuery.swift +++ b/apple/OmnivoreKit/Sources/Services/DataService/Queries/LinkedItemNetworkQuery.swift @@ -6,6 +6,7 @@ import SwiftGraphQL struct InternalLinkedItemQueryResult { let items: [InternalLibraryItem] let cursor: String? + let totalCount: Int? } struct InternalLinkedItemUpdatesQueryResult { @@ -144,6 +145,9 @@ extension DataService { items: try $0.edges(selection: searchItemEdgeSelection.list), cursor: try $0.pageInfo(selection: Selection.PageInfo { try $0.endCursor() + }), + totalCount: try $0.pageInfo(selection: Selection.PageInfo { + try $0.totalCount() }) ) )