Games in schedule view now display in sport sections (MLB, NBA, etc.) with games sorted by date within each section. Each game row shows its date since the section header now shows sport instead. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
148 lines
4.2 KiB
Swift
148 lines
4.2 KiB
Swift
//
|
|
// ScheduleViewModel.swift
|
|
// SportsTime
|
|
//
|
|
|
|
import Foundation
|
|
import SwiftUI
|
|
|
|
@MainActor
|
|
@Observable
|
|
final class ScheduleViewModel {
|
|
|
|
// MARK: - Filter State
|
|
|
|
var selectedSports: Set<Sport> = Set(Sport.supported)
|
|
var startDate: Date = Date()
|
|
var endDate: Date = Calendar.current.date(byAdding: .day, value: 14, to: Date()) ?? Date()
|
|
var searchText: String = ""
|
|
|
|
// MARK: - Data State
|
|
|
|
private(set) var games: [RichGame] = []
|
|
private(set) var isLoading = false
|
|
private(set) var error: Error?
|
|
private(set) var errorMessage: String?
|
|
|
|
private let dataProvider = AppDataProvider.shared
|
|
|
|
// MARK: - Computed Properties
|
|
|
|
var filteredGames: [RichGame] {
|
|
guard !searchText.isEmpty else { return games }
|
|
|
|
let query = searchText.lowercased()
|
|
return games.filter { game in
|
|
game.homeTeam.name.lowercased().contains(query) ||
|
|
game.homeTeam.city.lowercased().contains(query) ||
|
|
game.awayTeam.name.lowercased().contains(query) ||
|
|
game.awayTeam.city.lowercased().contains(query) ||
|
|
game.stadium.name.lowercased().contains(query) ||
|
|
game.stadium.city.lowercased().contains(query)
|
|
}
|
|
}
|
|
|
|
var gamesByDate: [(date: Date, games: [RichGame])] {
|
|
let calendar = Calendar.current
|
|
let grouped = Dictionary(grouping: filteredGames) { game in
|
|
calendar.startOfDay(for: game.game.dateTime)
|
|
}
|
|
return grouped.sorted { $0.key < $1.key }.map { ($0.key, $0.value) }
|
|
}
|
|
|
|
var gamesBySport: [(sport: Sport, games: [RichGame])] {
|
|
let grouped = Dictionary(grouping: filteredGames) { game in
|
|
game.game.sport
|
|
}
|
|
// Sort by sport order (use allCases index for consistent ordering)
|
|
// Within each sport, games are sorted by date
|
|
return grouped
|
|
.sorted { lhs, rhs in
|
|
let lhsIndex = Sport.allCases.firstIndex(of: lhs.key) ?? 0
|
|
let rhsIndex = Sport.allCases.firstIndex(of: rhs.key) ?? 0
|
|
return lhsIndex < rhsIndex
|
|
}
|
|
.map { (sport: $0.key, games: $0.value.sorted { $0.game.dateTime < $1.game.dateTime }) }
|
|
}
|
|
|
|
var hasFilters: Bool {
|
|
selectedSports.count < Sport.supported.count || !searchText.isEmpty
|
|
}
|
|
|
|
// MARK: - Actions
|
|
|
|
func loadGames() async {
|
|
guard !selectedSports.isEmpty else {
|
|
games = []
|
|
return
|
|
}
|
|
|
|
isLoading = true
|
|
error = nil
|
|
errorMessage = nil
|
|
|
|
do {
|
|
// Load initial data if needed
|
|
if dataProvider.teams.isEmpty {
|
|
await dataProvider.loadInitialData()
|
|
}
|
|
|
|
// Check if data provider had an error
|
|
if let providerError = dataProvider.errorMessage {
|
|
self.errorMessage = providerError
|
|
self.error = dataProvider.error
|
|
isLoading = false
|
|
return
|
|
}
|
|
|
|
games = try await dataProvider.fetchRichGames(
|
|
sports: selectedSports,
|
|
startDate: startDate,
|
|
endDate: endDate
|
|
)
|
|
} catch let cloudKitError as CloudKitError {
|
|
self.error = cloudKitError
|
|
self.errorMessage = cloudKitError.errorDescription
|
|
} catch {
|
|
self.error = error
|
|
self.errorMessage = error.localizedDescription
|
|
}
|
|
|
|
isLoading = false
|
|
}
|
|
|
|
func clearError() {
|
|
error = nil
|
|
errorMessage = nil
|
|
}
|
|
|
|
func toggleSport(_ sport: Sport) {
|
|
if selectedSports.contains(sport) {
|
|
selectedSports.remove(sport)
|
|
} else {
|
|
selectedSports.insert(sport)
|
|
}
|
|
Task {
|
|
await loadGames()
|
|
}
|
|
}
|
|
|
|
func resetFilters() {
|
|
selectedSports = Set(Sport.supported)
|
|
searchText = ""
|
|
startDate = Date()
|
|
endDate = Calendar.current.date(byAdding: .day, value: 14, to: Date()) ?? Date()
|
|
Task {
|
|
await loadGames()
|
|
}
|
|
}
|
|
|
|
func updateDateRange(start: Date, end: Date) {
|
|
startDate = start
|
|
endDate = end
|
|
Task {
|
|
await loadGames()
|
|
}
|
|
}
|
|
}
|