refactor: TripDetailView loads games on demand, improve poll UI
- Refactor TripDetailView to fetch games from AppDataProvider when not provided, adding loading state indicator for better UX - Update all callers (25+ HomeContent variants, TripOptionsView, HomeView) to use simpler TripDetailView(trip:) initializer - Fix PollDetailView sheet issue by using sheet(item:) instead of sheet(isPresented:) to prevent blank screen on first tap - Improve PollDetailView UI with Theme styling, icons, and better visual hierarchy for share code, voting status, and results sections Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -12,7 +12,7 @@ struct TripDetailView: View {
|
||||
@Environment(\.colorScheme) private var colorScheme
|
||||
|
||||
let trip: Trip
|
||||
let games: [String: RichGame]
|
||||
private let providedGames: [String: RichGame]?
|
||||
|
||||
@Query private var savedTrips: [SavedTrip]
|
||||
@State private var showProPaywall = false
|
||||
@@ -25,10 +25,29 @@ struct TripDetailView: View {
|
||||
@State private var isSaved = false
|
||||
@State private var routePolylines: [MKPolyline] = []
|
||||
@State private var isLoadingRoutes = false
|
||||
@State private var loadedGames: [String: RichGame] = [:]
|
||||
@State private var isLoadingGames = false
|
||||
|
||||
private let exportService = ExportService()
|
||||
private let dataProvider = AppDataProvider.shared
|
||||
|
||||
/// Games dictionary - uses provided games if available, otherwise uses loaded games
|
||||
private var games: [String: RichGame] {
|
||||
providedGames ?? loadedGames
|
||||
}
|
||||
|
||||
/// Initialize with trip and games dictionary (existing callers)
|
||||
init(trip: Trip, games: [String: RichGame]) {
|
||||
self.trip = trip
|
||||
self.providedGames = games
|
||||
}
|
||||
|
||||
/// Initialize with just trip - games will be loaded from AppDataProvider
|
||||
init(trip: Trip) {
|
||||
self.trip = trip
|
||||
self.providedGames = nil
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
VStack(spacing: 0) {
|
||||
@@ -94,6 +113,9 @@ struct TripDetailView: View {
|
||||
.onAppear {
|
||||
checkIfSaved()
|
||||
}
|
||||
.task {
|
||||
await loadGamesIfNeeded()
|
||||
}
|
||||
.overlay {
|
||||
if isExporting {
|
||||
exportProgressOverlay
|
||||
@@ -300,7 +322,15 @@ struct TripDetailView: View {
|
||||
.font(.title2)
|
||||
.foregroundStyle(Theme.textPrimary(colorScheme))
|
||||
|
||||
ForEach(Array(itinerarySections.enumerated()), id: \.offset) { index, section in
|
||||
if isLoadingGames {
|
||||
HStack {
|
||||
Spacer()
|
||||
ProgressView("Loading games...")
|
||||
.padding(.vertical, Theme.Spacing.xl)
|
||||
Spacer()
|
||||
}
|
||||
} else {
|
||||
ForEach(Array(itinerarySections.enumerated()), id: \.offset) { index, section in
|
||||
switch section {
|
||||
case .day(let dayNumber, let date, let gamesOnDay):
|
||||
DaySection(
|
||||
@@ -313,6 +343,7 @@ struct TripDetailView: View {
|
||||
TravelSection(segment: segment)
|
||||
.staggeredAnimation(index: index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -493,6 +524,34 @@ struct TripDetailView: View {
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
/// Load games from AppDataProvider if not provided
|
||||
private func loadGamesIfNeeded() async {
|
||||
// Skip if games were provided
|
||||
guard providedGames == nil else { return }
|
||||
|
||||
// Collect all game IDs from the trip
|
||||
let gameIds = trip.stops.flatMap { $0.games }
|
||||
guard !gameIds.isEmpty else { return }
|
||||
|
||||
isLoadingGames = true
|
||||
|
||||
// Load RichGame data from AppDataProvider
|
||||
var loaded: [String: RichGame] = [:]
|
||||
for gameId in gameIds {
|
||||
do {
|
||||
if let game = try await dataProvider.fetchGame(by: gameId),
|
||||
let richGame = dataProvider.richGame(from: game) {
|
||||
loaded[gameId] = richGame
|
||||
}
|
||||
} catch {
|
||||
// Skip games that fail to load
|
||||
}
|
||||
}
|
||||
|
||||
loadedGames = loaded
|
||||
isLoadingGames = false
|
||||
}
|
||||
|
||||
private func exportPDF() async {
|
||||
isExporting = true
|
||||
exportProgress = nil
|
||||
@@ -911,8 +970,7 @@ struct ShareSheet: UIViewControllerRepresentable {
|
||||
startLocation: LocationInput(name: "New York"),
|
||||
endLocation: LocationInput(name: "Chicago")
|
||||
)
|
||||
),
|
||||
games: [:]
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user