# TODO Bugs Design Document **Date:** 2026-01-12 **Status:** Ready for Implementation ## Overview This document outlines fixes for 9 bugs from TO-DOS.md affecting performance, UI polish, and the data scraper. --- ## Category A: Performance Issues (3 bugs) ### Bug 1: UI Unresponsive on Launch Until Featured Loads **Problem:** App freezes on launch while generating suggested trips. **Root Cause:** `SuggestedTripsGenerator` is `@MainActor`, so all `TripPlanningEngine` work (including TSP solving) blocks the main thread. **Files to Modify:** - `SportsTime/Core/Services/SuggestedTripsGenerator.swift` - `SportsTime/Features/Home/Views/HomeView.swift` **Fix:** 1. Move heavy computation to background using `Task.detached` 2. Show skeleton/placeholder UI immediately 3. Keep only UI state updates on `@MainActor` **Implementation:** ```swift func generateTrips() async { guard !isLoading else { return } isLoading = true loadingMessage = await loadingTextGenerator.generateMessage() // Move heavy work off main actor let result = await Task.detached(priority: .userInitiated) { [dataProvider, planningEngine] in // Ensure data is loaded let teams = await dataProvider.teams let stadiums = await dataProvider.stadiums let games = try? await dataProvider.filterGames(...) // All planning engine work here // Return generated trips }.value // Back on MainActor for UI update self.suggestedTrips = result isLoading = false } ``` --- ### Bug 2: Lag on Big Data Sets ("By Game") **Problem:** Schedule list view with 1000+ games causes UI lag. **Root Cause:** SwiftUI List struggles with large datasets. Each `GameRowView` performs: - Rich game lookups - Date formatting on every render - No virtualization **Files to Modify:** - `SportsTime/Features/Schedule/Views/ScheduleListView.swift` - `SportsTime/Features/Schedule/ViewModels/ScheduleViewModel.swift` **Fix:** 1. Implement pagination with visible window 2. Cache formatted dates and lookups 3. Use `LazyVStack` with explicit `.id()` for efficient diffing **Implementation:** ```swift struct ScheduleListView: View { @State private var visibleRange: Range = 0..<50 private let pageSize = 50 var body: some View { ScrollView { LazyVStack(spacing: 0) { ForEach(visibleGames) { game in GameRowView(game: game, showDate: true) .id(game.id) .onAppear { loadMoreIfNeeded(game) } } } } } private func loadMoreIfNeeded(_ game: RichGame) { // Load next page when approaching end } } ``` --- ### Bug 4: Select Games View Laggy and Two Different Colors **Problem:** Game selection view has performance issues and visual inconsistency. **Root Cause:** Same as Bug 2 - large list without virtualization. Color issue likely from inconsistent background modifiers. **Files to Modify:** - `SportsTime/Features/Trip/Views/TripCreationView.swift` (game selection section) **Fix:** 1. Apply same pagination fix as Bug 2 2. Audit background colors - use consistent `.background()` modifier 3. Ensure selected/unselected states use same base colors --- ## Category B: Missing Info & Animation Issues (3 bugs) ### Bug 3: "By Games" Missing Location Info **Problem:** Games grouped by date don't show stadium/city. **Root Cause:** `GameRowView` designed for team-grouped context where location is implicit. **Files to Modify:** - `SportsTime/Features/Schedule/Views/GameRowView.swift` - `SportsTime/Features/Schedule/Views/ScheduleListView.swift` **Fix:** 1. Add `showLocation: Bool` parameter to `GameRowView` 2. Pass `true` when `groupBy == .byGame` **Implementation:** ```swift struct GameRowView: View { let game: RichGame var showDate: Bool = false var showLocation: Bool = false // New parameter var body: some View { VStack(alignment: .leading, spacing: 4) { // Existing matchup content... if showLocation { Text("\(game.stadium.name), \(game.stadium.city)") .font(.caption) .foregroundStyle(.secondary) } } } } // In ScheduleListView: GameRowView(game: game, showDate: true, showLocation: groupBy == .byGame) ``` --- ### Bug 5: Weird Animation When Selecting a Game **Problem:** Unexpected animation when tapping a game in selection view. **Root Cause:** SwiftUI animation conflict between List selection and state update. **Files to Modify:** - `SportsTime/Features/Trip/Views/TripCreationView.swift` **Fix:** 1. Wrap selection in transaction to disable implicit animation 2. Or use `.animation(nil, value:)` to suppress **Implementation:** ```swift // Option A: Disable animation on selection Button { var transaction = Transaction() transaction.disablesAnimations = true withTransaction(transaction) { viewModel.toggleGameSelection(game) } } label: { GameSelectionRow(game: game, isSelected: isSelected) } .buttonStyle(.plain) // Option B: On the selection indicator Image(systemName: isSelected ? "checkmark.circle.fill" : "circle") .animation(nil, value: isSelected) ``` --- ### Bug 6: Trip Game Times Should Be UTC to Local **Problem:** Game times display in device timezone, not stadium local time. **Root Cause:** `DateFormatter` without explicit timezone uses device default. **Files to Modify:** - `SportsTime/Core/Models/Domain/Stadium.swift` - `SportsTime/Core/Models/Domain/Game.swift` **Fix:** 1. Add `timeZoneIdentifier: String` to Stadium (or derive from coordinates) 2. Create `localGameTime` computed property on `RichGame` 3. Display with timezone abbreviation: "7:05 PM ET" **Implementation:** ```swift // Stadium.swift - add timezone struct Stadium: Identifiable, Codable, Hashable { // ... existing properties let timeZoneIdentifier: String? // e.g., "America/New_York" var timeZone: TimeZone? { timeZoneIdentifier.flatMap { TimeZone(identifier: $0) } } } // RichGame extension extension RichGame { var localGameTime: String { let formatter = DateFormatter() formatter.dateFormat = "h:mm a z" formatter.timeZone = stadium.timeZone ?? .current return formatter.string(from: game.dateTime) } } ``` **Note:** Requires populating `timeZoneIdentifier` for all stadiums in: - Bundled JSON bootstrap data - CloudKit stadium records - Scraper stadium resolver --- ## Category C: UI Polish Issues (2 bugs) ### Bug 7: Pace Capsule Animation Looks Off **Problem:** "packed"/"moderate"/"relaxed" label has glitchy animation. **Root Cause:** Implicit animation conflict or text morphing transition. **Files to Modify:** - `SportsTime/Features/Home/Views/SavedTripsListView.swift` (or wherever pace capsule is) **Fix:** 1. Use `.contentTransition(.identity)` to prevent text morphing 2. Disable animation on the capsule when trip data changes **Implementation:** ```swift Text(trip.paceLabel) .font(.caption2) .fontWeight(.medium) .padding(.horizontal, 8) .padding(.vertical, 4) .background(paceColor.opacity(0.2)) .foregroundStyle(paceColor) .clipShape(Capsule()) .contentTransition(.identity) .animation(nil, value: trip.id) ``` --- ### Bug 8: Remove "My Trips" Title from Tab View **Problem:** Redundant title appearing in the My Trips tab. **Files to Modify:** - `SportsTime/Features/Home/Views/HomeView.swift` (or main TabView) **Fix:** 1. Remove `.navigationTitle()` from SavedTripsListView root 2. Or hide navigation bar if title is inherited **Implementation:** ```swift // In TabView: NavigationStack { SavedTripsListView(trips: savedTrips) .toolbar(.hidden, for: .navigationBar) // Or just remove any .navigationTitle("My Trips") } .tabItem { Label("My Trips", systemImage: "suitcase") } ``` --- ## Category D: Scraper Issue (1 bug) ### Bug 9: Frost Bank Center Team Mapping Missing **Problem:** NBA scraper can't resolve Frost Bank Center (Spurs home). **Root Cause:** Scraper likely returns old name "AT&T Center" which doesn't match. Stadium exists in resolver but alias is missing. **Files to Modify:** - `Scripts/stadium_aliases.json` **Fix:** 1. Add alias for "AT&T Center" → "stadium_nba_frost_bank_center" 2. Check scraper logs for exact unresolved name **Implementation:** ```json { "canonical_id": "stadium_nba_frost_bank_center", "alias_name": "AT&T Center", "sport": "nba", "valid_from": "2002-10-18", "valid_to": "2024-07-01", "notes": "Renamed to Frost Bank Center in 2024" } ``` **Investigation:** Run NBA scraper with verbose logging to confirm the exact stadium name being returned. --- ## Implementation Priority 1. **High Impact (Performance):** - Bug 1: Launch responsiveness (blocking UX) - Bug 2 & 4: List performance (core feature usability) 2. **Medium Impact (UX Polish):** - Bug 3: Location info (missing data) - Bug 6: Timezone display (incorrect data) - Bug 5: Selection animation (visual glitch) 3. **Low Impact (Minor Polish):** - Bug 7: Pace capsule animation - Bug 8: Remove redundant title - Bug 9: Scraper alias (data pipeline) --- ## Testing Checklist - [ ] App launches with responsive UI, trips load in background - [ ] "By Game" view scrolls smoothly with 1000+ games - [ ] "By Game" shows stadium and city for each game - [ ] Game selection has no weird animation on tap - [ ] Trip game times show stadium local time with timezone (e.g., "7:05 PM ET") - [ ] Pace capsule has no animation glitch - [ ] "My Trips" tab has no redundant title - [ ] NBA scraper resolves Spurs games to Frost Bank Center