perf: add pagination to schedule list
Loads 50 games at a time to fix lag with large datasets. - Add pagination state (displayedGames, currentPage, allFilteredGames) - Add loadInitialGames() and loadMoreGames() methods - Update gamesBySport to use displayedGames - Add infinite scroll trigger via onAppear on last game - Add ProgressView indicator when more games available - Reset pagination when search text changes Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -26,6 +26,22 @@ final class ScheduleViewModel {
|
||||
|
||||
private let dataProvider = AppDataProvider.shared
|
||||
|
||||
// MARK: - Pagination
|
||||
|
||||
private let pageSize = 50
|
||||
private(set) var displayedGames: [RichGame] = []
|
||||
private var currentPage = 0
|
||||
private var allFilteredGames: [RichGame] = []
|
||||
|
||||
var hasMoreGames: Bool {
|
||||
displayedGames.count < allFilteredGames.count
|
||||
}
|
||||
|
||||
/// The last game in the displayed list, used for infinite scroll detection
|
||||
var lastDisplayedGame: RichGame? {
|
||||
displayedGames.last
|
||||
}
|
||||
|
||||
// MARK: - Computed Properties
|
||||
|
||||
var filteredGames: [RichGame] {
|
||||
@@ -51,7 +67,7 @@ final class ScheduleViewModel {
|
||||
}
|
||||
|
||||
var gamesBySport: [(sport: Sport, games: [RichGame])] {
|
||||
let grouped = Dictionary(grouping: filteredGames) { game in
|
||||
let grouped = Dictionary(grouping: displayedGames) { game in
|
||||
game.game.sport
|
||||
}
|
||||
// Sort by sport order (use allCases index for consistent ordering)
|
||||
@@ -69,11 +85,35 @@ final class ScheduleViewModel {
|
||||
selectedSports.count < Sport.supported.count || !searchText.isEmpty
|
||||
}
|
||||
|
||||
// MARK: - Pagination Actions
|
||||
|
||||
/// Updates filtered games list based on current search text and resets pagination
|
||||
func updateFilteredGames() {
|
||||
allFilteredGames = filteredGames
|
||||
loadInitialGames()
|
||||
}
|
||||
|
||||
/// Loads the first page of games
|
||||
func loadInitialGames() {
|
||||
currentPage = 0
|
||||
displayedGames = Array(allFilteredGames.prefix(pageSize))
|
||||
}
|
||||
|
||||
/// Loads more games when scrolling to the bottom
|
||||
func loadMoreGames() {
|
||||
guard hasMoreGames else { return }
|
||||
currentPage += 1
|
||||
let start = currentPage * pageSize
|
||||
let end = min(start + pageSize, allFilteredGames.count)
|
||||
displayedGames.append(contentsOf: allFilteredGames[start..<end])
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
func loadGames() async {
|
||||
guard !selectedSports.isEmpty else {
|
||||
games = []
|
||||
updateFilteredGames()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -92,6 +132,7 @@ final class ScheduleViewModel {
|
||||
self.errorMessage = providerError
|
||||
self.error = dataProvider.error
|
||||
isLoading = false
|
||||
updateFilteredGames()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -109,6 +150,7 @@ final class ScheduleViewModel {
|
||||
}
|
||||
|
||||
isLoading = false
|
||||
updateFilteredGames()
|
||||
}
|
||||
|
||||
func clearError() {
|
||||
|
||||
Reference in New Issue
Block a user