Commit Graph

317 Commits

Author SHA1 Message Date
Trey t
5511e07538 fix: 13 audit fixes — memory, concurrency, performance, accessibility
Critical:
- ProgressViewModel: use single stored ModelContext instead of creating
  new ones per operation (deleteVisit silently no-op'd)
- ProgressViewModel: convert expensive computed properties to stored
  with explicit recompute after mutations (3x recomputation per render)

Memory:
- AnimatedSportsIcon: replace recursive GCD asyncAfter with Task loop,
  cancelled in onDisappear (19 unkillable timer chains)
- ItineraryItemService: remove [weak self] from actor Task (semantically
  wrong, silently drops flushPendingUpdates)
- VisitPhotoService: remove [weak self] from @MainActor Task closures

Concurrency:
- StoreManager: replace nested MainActor.run{Task{}} with direct await
  in listenForTransactions (fire-and-forget race)
- VisitPhotoService: move JPEG encoding/file writing off MainActor via
  nonisolated static helper + Task.detached
- SportsIconImageGenerator: replace GCD dispatch with Task.detached for
  structured concurrency compliance

Performance:
- Game/RichGame: cache DateFormatters as static lets instead of
  allocating per-call (hundreds of allocations in schedule view)
- TripDetailView: wrap ~10 routeWaypoints print() in #if DEBUG, remove
  2 let _ = print() from TripMapView.body (fires every render)

Accessibility:
- GameRow: add combined VoiceOver label (was reading abbreviations
  letter-by-letter)
- Sport badges: add accessibilityLabel to prevent SF symbol name readout
- SportsTimeApp: post UIAccessibility.screenChanged after bootstrap
  completes so VoiceOver users know app is ready

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 22:09:06 -06:00
treyt
20ac1a7e59 Stabilize unit and UI tests for SportsTime 2026-02-18 13:00:15 -06:00
Trey t
1488be7c1f fix: 5 failing UI tests — scroll direction, sport availability race, launch args, demo toggle conflict
- Screens.swift: selectPlanningMode scrolls UP (modes are at top of wizard)
- TripWizardFlowTests: F040 selects sport before dates to avoid async
  availability check disabling MLB for December off-season
- SportsTimeUITests: add --ui-testing/--disable-animations/--reset-state
  launch args to accessibility, demo, and manual flow tests
- Demo test: remove -DemoMode flag that caused double-toggle (demo
  auto-selects + manual taps toggled sports/regions off)
- Manual test: replace blind swipes with scrollIntoView + wait-for-enabled
  for Plan button; use waitForExistence for trip card selection

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 21:34:57 -06:00
Trey t
9b0cb96638 fix: 10 audit fixes — memory safety, performance, accessibility, architecture
- Add a11y label to ProgressMapView reset button and progress bar values
- Fix CADisplayLink retain cycle in ItineraryTableViewController via deinit
- Add [weak self] to PhotoGalleryViewModel Task closure
- Add @MainActor to TripWizardViewModel, remove manual MainActor.run hop
- Fix O(n²) rank lookup in PollDetailView/DebugPollPreviewView with enumerated()
- Cache itinerarySections via ItinerarySectionBuilder static extraction + @State
- Convert CanonicalSyncService/BootstrapService from actor to @MainActor final class
- Add .accessibilityHidden(true) to RegionMapSelector Map to prevent duplicate VoiceOver

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 12:00:35 -06:00
Trey t
46434af4ab fix: F097 sport selector test uses label match instead of overridden identifier
The parent SportSelectorGrid's accessibilityIdentifier propagates to child
buttons, overriding their individual progress.sport.{name} identifiers.
Match by accessibility label (e.g., "MLB") instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 10:22:23 -06:00
Trey t
dc142bd14b feat: expand XCUITest coverage to 54 QA scenarios with accessibility IDs and fix test failures
Add 22 new UI tests across 8 test files covering Home, Schedule, Progress,
Settings, TabNavigation, TripSaving, and TripOptions. Add accessibility
identifiers to 11 view files for test element discovery. Fix sport chip
assertion logic (all sports start selected, tap deselects), scroll container
issues on iOS 26 nested ScrollViews, toggle interaction, and delete trip flow.
Update QA coverage map from 32 to 54 automated test cases.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 19:44:22 -06:00
Trey t
d53f222489 feat: add XCUITest suite with 10 critical flow tests and QA test plan
Add comprehensive UI test infrastructure with Page Object pattern,
accessibility identifiers, UI test mode (--ui-testing, --reset-state,
--disable-animations), and 10 passing tests covering app launch, tab
navigation, trip wizard, trip saving, settings, schedule, and
accessibility at XXXL Dynamic Type. Also adds a 229-case QA test plan
Excel workbook for manual QA handoff.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 16:23:59 -06:00
Trey t
787a0f795e fix: 12 planning engine bugs + App Store preview export at 886x1920
Planning engine fixes (from adversarial code review):
- Bug #1: sortByLeisure tie-breaking uses totalDrivingHours
- Bug #2: allDates/calculateRestDays guard-let-break prevents infinite loop
- Bug #3: same-day trip no longer rejected (>= in dateRange guard)
- Bug #4: ScenarioD rationale shows game count not stop count
- Bug #5: ScenarioD departureDate advanced to next day after last game
- Bug #6: ScenarioC date range boundary uses <= instead of <
- Bug #7: DrivingConstraints clamps maxHoursPerDriverPerDay via max(1.0,...)
- Bug #8: effectiveTripDuration uses inclusive day counting (+1)
- Bug #9: TripWizardViewModel validates endDate >= startDate
- Bug #10: allDates() uses min/max instead of first/last for robustness
- Bug #12: arrivalBeforeGameStart accounts for game end time at departure
- Bug #15: ScenarioBPlanner replaces force unwraps with safe unwrapping

Tests: 16 regression test suites + updated existing test expectations
Marketing: Remotion canvas set to 886x1920 for App Store preview spec

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 17:08:50 -06:00
Trey t
b320a773aa update claudemd file 2026-02-14 13:31:56 -06:00
Trey t
debe931f76 add in marketing screens file 2026-02-13 12:12:02 -06:00
Trey t
a009c35d5e chore: gitignore screens/ and marketing-videos/out/
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 12:10:34 -06:00
Trey t
5f5b137e64 feat: add marketing video mode and Remotion marketing video project
Add debug-only Marketing Video Mode toggle that enables hands-free
screen recording across the app: auto-scrolling Featured Trips carousel,
auto-filling trip wizard, smooth trip detail scrolling via CADisplayLink,
and trip options auto-sort with scroll.

Add Remotion marketing video project with 6 scene compositions using
image sequences extracted from screen recordings, varied phone entrance
animations, and deduped frames for smooth playback.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 12:07:35 -06:00
Trey t
67965cbac6 fix: region map tap selecting wrong region due to accessibility button overlay
Accessibility buttons split the map into equal-width thirds, intercepting
taps before the coordinate-based logic. Tapping the visual West region
could hit the Central button. Adding .allowsHitTesting(false) lets taps
pass through to MapReader's coordinate detection; VoiceOver still works.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 09:12:00 -06:00
Trey t
9736773475 feat: improve planning engine travel handling, itinerary reordering, and scenario planners
Add TravelInfo initializers and city normalization helpers to fix repeat
city-pair disambiguation. Improve drag-and-drop reordering with segment
index tracking and source-row-aware zone calculation. Enhance all five
scenario planners with better next-day departure handling and travel
segment placement. Add comprehensive tests across all planners.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 08:55:23 -06:00
Trey t
1c97f35754 feat: enforce custom Theme colors app-wide, add debug sample trips and poll
Replace all system colors (.secondary, Color(.secondarySystemBackground),
etc.) with Theme.textPrimary/textSecondary/textMuted/cardBackground/
surfaceGlow across 13 views. Remove PostHog debug logging. Add debug
settings for sample trips and hardcoded group poll preview.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 08:54:19 -06:00
Trey t
ff6f4b6c2c fix: resolve travel anchor ID collision for repeat city pairs
Include segment index in travel anchor IDs ("travel:INDEX:from->to")
so Follow Team trips visiting the same city pair multiple times get
unique, independently addressable travel segments. Prevents override
dictionary collisions and incorrect validDayRange lookups.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 10:57:53 -06:00
Trey t
633f7d883f fix: correct travel segment placement for next-day departures
Travel segments appeared one day too late on featured/suggested trips
when stops had next-morning departures. The placement formula double-
counted by using fromDayNum+1 as both minDay and defaultDay, then the
invalid-range fallback used minDay (the overshooting value) instead of
the arrival day. Also replaced TripDetailView's inline copy of the
placement logic with TravelPlacement.computeTravelByDay() so the UI
uses the same tested algorithm.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 09:53:25 -06:00
Trey t
d63d311cab feat: add WCAG AA accessibility app-wide, fix CloudKit container config, remove debug logs
- Add VoiceOver labels, hints, and element grouping across all 60+ views
- Add Reduce Motion support (Theme.Animation.prefersReducedMotion) to all animations
- Replace fixed font sizes with semantic Dynamic Type styles
- Hide decorative elements from VoiceOver with .accessibilityHidden(true)
- Add .minimumHitTarget() modifier ensuring 44pt touch targets
- Add AccessibilityAnnouncer utility for VoiceOver announcements
- Improve color contrast values in Theme.swift for WCAG AA compliance
- Extract CloudKitContainerConfig for explicit container identity
- Remove PostHog debug console log from AnalyticsManager

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 09:27:23 -06:00
Trey t
e9c15d70b1 perf: optimize featured cross-country trip generation and add tests 2026-02-10 20:11:38 -06:00
Trey t
c6fa6386fd debug: add CloudKit container diagnostics to sync logs
Logs bundle ID, CKContainer.default() identifier, and iCloud account
status at sync start to help diagnose "Invalid bundle ID for container"
errors on TestFlight.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 19:30:50 -06:00
Trey t
53cc532ca9 chore: commit all pending changes 2026-02-10 18:15:36 -06:00
Trey t
b993ed3613 feat: add privacy policy and EULA pages, use real app icon on landing site
- Create privacy.html and eula.html matching landing page style
- Update Settings links to sportstime.88oakapps.com/privacy.html and /eula.html
- Rename "Terms of Service" to "EULA" in Settings
- Replace emoji logo with real app icon across all landing pages
- Update footer links in index.html

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 15:50:52 -06:00
Trey t
5f510181eb chore: set production PostHog API key
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 15:19:53 -06:00
Trey t
2917ae22b1 feat: add PostHog analytics with full event tracking across app
Integrate self-hosted PostHog (SPM) with AnalyticsManager singleton wrapping
all SDK calls. Adds ~40 type-safe events covering trip planning, schedule,
progress, IAP, settings, polls, export, and share flows. Includes session
replay, autocapture, network telemetry, privacy opt-out toggle in Settings,
and super properties (app version, device, pro status, selected sports).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 15:12:16 -06:00
Trey t
5389fe3759 ui: make Featured Trips carousel scroll edge-to-edge
Use contentMargins on horizontal ScrollView so cards start inset but
scroll to screen edges. Pad headers and error states individually.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 17:52:38 -06:00
Trey t
68cb8927cf perf: parallelize regional trip generation with async let
Run East/Central/West regional trips and cross-country routes
concurrently instead of sequentially, reducing wall-clock time.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 17:44:25 -06:00
Trey t
1c57c47041 perf: optimize Progress tab with O(1) lookups and loading state
Replace O(n) linear searches with dictionary lookups in ProgressViewModel
and ProgressTabView. Add loading spinner while data loads.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 17:38:59 -06:00
Trey t
213d2bae53 feat: replace custom paywalls with SubscriptionStoreView
Use Apple's SubscriptionStoreView for subscription UI instead of
custom pricing cards. Onboarding flow keeps feature pages but
embeds PaywallView for the pricing page. Removes ~500 lines of
custom pricing UI (PricingOptionCard, OnboardingPricingRow,
PricingBackground, SportsIconWithGlow).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 17:20:20 -06:00
Trey t
244ea5e107 feat: redesign all share cards, remove unused achievement types, fix sport selector
Redesign trip, progress, and achievement share cards with premium
sports-media aesthetic. Remove unused milestone/context achievement card
types (only used in debug exporter). Fix gold text unreadable in light
mode. Fix sport selector to only show stroke on selected sport.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 14:55:53 -06:00
Trey t
1a7ce78ae4 feat(debug): add bulk share image export and stadium visit tools
Adds debug-only DebugShareExporter that bulk-exports ~298 shareable
image variations (achievement cards, progress cards, trip cards, icons)
to Documents/DebugExport/ for visual QA. Also adds a button to populate
all stadium visits for testing the fully-unlocked app state.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 12:39:20 -06:00
Trey t
e6ed766ccd Remove marketing-videos Remotion project
The standalone Remotion video project is no longer needed in this repo.
Also updates local Claude Code settings with additional tool permissions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 00:03:49 -06:00
Trey t
46d37875e5 update icon colors 2026-02-06 18:07:37 -06:00
Trey t
8e937a5646 feat: fix travel placement bug, add theme-based alternate icons, fix animated background crash
- Fix repeat-city travel placement: use stop indices instead of global city name
  matching so Follow Team trips with repeat cities show travel correctly
- Add TravelPlacement helper and regression tests (7 tests)
- Add alternate app icons for each theme, auto-switch on theme change
- Fix index-out-of-range crash in AnimatedSportsBackground (19 configs, was iterating 20)
- Add marketing video configs, engine, and new video components
- Add docs and data exports

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 09:36:34 -06:00
Trey t
fdcecafaa3 feat: rewrite bootstrap, fix CloudKit sync, update canonical data, and UI fixes
- Rewrite BootstrapService: remove all legacy code paths (JSONStadium,
  JSONGame, bootstrapStadiumsLegacy, bootstrapGamesLegacy, venue aliases,
  createDefaultLeagueStructure), require canonical JSON files only
- Add clearCanonicalData() to handle partial bootstrap recovery (prevents
  duplicate key crashes from interrupted first-launch)
- Fix nullable stadium_canonical_id in games (4 MLS games have null)
- Fix CKModels: logoUrl case, conference/division field keys
- Fix CanonicalSyncService: sync conferenceCanonicalId/divisionCanonicalId
- Add sports_canonical.json and DemoMode.swift
- Delete legacy stadiums.json and games.json
- Update all canonical resource JSON files with latest data
- Fix TripWizardView horizontal scrolling with GeometryReader constraint
- Update RegionMapSelector, TripDetailView, TripOptionsView UI improvements
- Add DateRangePicker, PlanningModeStep, SportsStep enhancements
- Update UI tests and marketing-videos config

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 00:06:19 -06:00
Trey t
12f959ab8d feat(data): add 2026 season game data
- Import 2026 game data from ESPN source file
- Add MLB, NBA, NHL, NFL games for 2026 season
- Total games: 9879 (up from ~6800)
- June 2026 MLB: 394 games now available

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 22:58:52 -06:00
Trey t
61c4e39807 feat: update bundle ID config, CloudKit container, and add landing page
- Update CloudKit container ID to iCloud.com.88oakapps.SportsTime across all services
- Update IAP product IDs to match new bundle ID (com.88oakapps.SportsTime)
- Add app landing page with light, welcoming design matching app aesthetic
- Update entitlements and project configuration

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 22:47:55 -06:00
Trey t
dbb0099776 chore: remove scraper, add docs, add marketing-videos gitignore
- Remove Scripts/ directory (scraper no longer needed)
- Add themed background documentation to CLAUDE.md
- Add .gitignore for marketing-videos to prevent node_modules tracking

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 18:13:12 -06:00
Trey t
bfa172de38 update icon shit 2026-01-21 18:07:13 -06:00
Trey t
e1d84ac769 feat(ui): apply animated background to all screens via themedBackground modifier
Extract AnimatedSportsBackground components to shared file and update
ThemedBackground modifier to conditionally show animations when enabled
in settings. All views using .themedBackground() now get animated background.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 18:06:58 -06:00
Trey t
3a135743f8 add in icon pxd file 2026-01-21 17:59:20 -06:00
Trey t
d97dec44b2 fix(planning): gameFirst mode now uses full date range and shows correct month
Two bugs fixed in "By Games" trip planning mode:

1. Calendar navigation: DateRangePicker now navigates to the selected
   game's month when startDate changes externally, instead of staying
   on the current month.

2. Date range calculation: Fixed race condition where date range was
   calculated before games were loaded. Now updateDateRangeForSelectedGames()
   is called after loadSummaryGames() completes.

3. Bonus games: planTrip() now uses the UI-selected 7-day date range
   instead of overriding it with just the anchor game dates. This allows
   ScenarioBPlanner to find additional games within the trip window.

Added regression tests to verify gameFirst mode includes bonus games.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 16:37:19 -06:00
Trey t
4d097883a6 fix(data): add timezone handling for Sports-Reference scrapers and new stadiums
- Add ET timezone (America/New_York) to all Sports-Reference scrapers:
  - NBA: Basketball-Reference times parsed as ET
  - NFL: Pro-Football-Reference times parsed as ET
  - NHL: Hockey-Reference times parsed as ET
  - MLB: Baseball-Reference times parsed as ET
- Document source timezones in scraper docstrings
- Add 11 new stadiums to STADIUM_MAPPINGS:
  - NFL: 5 international venues (Corinthians Arena, Croke Park,
    Olympic Stadium Berlin, Santiago Bernabéu, Tom Benson Hall of Fame)
  - MLS: 4 alternate venues (Miami Freedom Park, Citi Field,
    LA Memorial Coliseum, M&T Bank Stadium)
  - NWSL: 2 alternate venues (Northwestern Medicine Field, ONE Spokane)
- Add 15 stadium aliases for MLS/NWSL team-based lookups
- Fix CanonicalSyncService to sync timezone identifier to SwiftData
- Update debug logging to use stadium timezone for display

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 16:04:45 -06:00
Trey t
b339a53db3 fix(data): add correct timezone values to all stadiums in STADIUM_MAPPINGS
- Added explicit timezones for ~100 stadiums that were defaulting to America/New_York
- Coverage by timezone:
  - America/Chicago: TX, IL, MO, MN, WI, OK, TN, LA stadiums
  - America/Los_Angeles: CA, WA, OR, NV stadiums
  - America/Denver: CO, UT stadiums
  - America/Phoenix: AZ stadiums (no DST)
  - America/Toronto: ON, QC stadiums
  - America/Vancouver: BC stadiums
  - America/Edmonton: AB stadiums
  - America/Winnipeg: MB stadiums
- All 7 sports leagues now have correct timezone data for scrapers

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 23:01:59 -06:00
Trey t
12ddca4d10 fix(data): populate stadium timezone in scrapers and CloudKit sync
Stadium timezones were always null because scrapers weren't passing
the timezone from STADIUM_MAPPINGS to the Stadium constructor. This
fix propagates timezone data through the entire pipeline: scrapers,
CloudKit uploader, and Swift CloudKit model.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 22:45:30 -06:00
Trey t
166ad5d6f9 fix(display): use stadium timezone for all game time displays
Game times were incorrectly using device timezone in several views.
Now all game time displays use the stadium's local timezone via
RichGame.localGameTime/localGameTimeShort properties.

Fixes:
- PDFGenerator: was using game.gameDate (midnight UTC) instead of actual time
- GameRowCompact: was formatting with device timezone
- TimelineGameRow: was using .formatted() with device timezone

Also adds Date+GameTime.swift extension for centralized timezone formatting.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 22:44:40 -06:00
Trey t
74fd21590b wip 2026-01-20 22:26:48 -06:00
Trey t
87079b434d fix(schedule): use start of day for date range queries
- Fix startDate to use Calendar.startOfDay instead of Date() to include
  games earlier in the current day
- Add SyncLogger for file-based sync logging viewable in Settings
- Add "View Sync Logs" button in Settings debug section
- Add diagnostics and NBA game logging to ScheduleViewModel
- Add dropped game logging to DataProvider.filterRichGames
- Use SyncLogger in SportsTimeApp and CloudKitService for sync operations

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 22:25:44 -06:00
Trey t
8ea3e6112a feat(scripts): complete data pipeline remediation
Scripts changes:
- Add WNBA abbreviation aliases to team_resolver.py
- Fix NHL stadium coordinates in stadium_resolver.py
- Add validate_aliases.py script for orphan detection
- Update scrapers with improved error handling
- Add DATA_AUDIT.md and REMEDIATION_PLAN.md documentation
- Update alias JSON files with new mappings

iOS bundle updates:
- Update games_canonical.json with latest scraped data
- Update teams_canonical.json and stadiums_canonical.json
- Sync alias files with Scripts versions

All 5 remediation phases complete.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 18:58:47 -06:00
Trey t
51419fccf2 feat(debug): add per-entity CloudKit sync status in Settings
Add debug-only sync status monitoring to help diagnose CloudKit sync issues.
Shows last sync time, success/failure, and record counts for each entity type.
Includes manual sync trigger and re-enable button when sync is paused.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 13:12:56 -06:00
Trey t
c49206bb7c wip 2026-01-20 12:25:00 -06:00