Commit Graph

338 Commits

Author SHA1 Message Date
Trey t
ab1d7bc6b6 Add F-015, F-076, F-094 UI tests for home refresh, trip detail, diagnostics
- F-015: Featured trips refresh button works without crash
- F-076: Trip detail loads correctly with single-stop trip
- F-094: Schedule diagnostics sheet opens from filter menu

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 01:03:12 -06:00
Trey t
56c17e79a4 Mark 45 impossible/unreliable UI tests RED in QA test plan
Tests that require network control, StoreKit sandbox, CloudKit
operations, photo permissions, visual verification, or missing
accessibility IDs are marked RED as not automatable via XCUITest.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 00:55:09 -06:00
Trey t
4cb8d89fbd Add F-084, F-133, F-134 UI tests; mark F-131, F-132 red (toggle unreliable)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 00:52:07 -06:00
Trey t
be72367fb1 Add F-040, F-081, F-083 UI tests for review step and polls section
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 00:37:34 -06:00
Trey t
54be5cae3e Add F-030, F-031, F-032 UI tests for calendar date edge cases
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 00:30:48 -06:00
Trey t
61fd7f5209 Add F-041, F-047, F-051 UI tests for wizard and trip options
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 00:25:50 -06:00
Trey t
378f65f82c Add F-058, F-059, F-090 UI tests for trip options and schedule search
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 00:19:28 -06:00
Trey t
0f0e534214 Add F-018, F-060, F-062 UI tests and update QA test plan
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 00:06:38 -06:00
Trey t
2d759274a8 Add F-068, F-069, F-070 UI tests for custom itinerary item lifecycle
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 23:49:29 -06:00
Trey t
8421b23f0c Add F-100, F-101, F-106 UI tests and page objects for Progress feature
Adds 3 new UI tests covering stadium visit manual entry, required field
validation, and games history navigation. Includes accessibility IDs on
StadiumVisitSheet/ProgressTabView and new page objects (StadiumVisitSheetScreen,
GamesHistoryScreen) in the test framework.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 23:02:52 -06:00
Trey t
f10bc4fe59 fix: crash on launch from BGTask handler @MainActor isolation violation
BGTaskScheduler.register(using: nil) invokes the handler on a background
queue, but the closure captured @MainActor-isolated self. Swift 6 runtime
enforces this with dispatch_assert_queue which crashed on Thread 4.

Fix: pass DispatchQueue.main as the handler queue so the callback runs
on the main queue, satisfying @MainActor isolation. Also fix expiration
handlers to capture a local Logger copy instead of accessing self.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 17:41:33 -06:00
Trey t
c976ae5cb3 Add POI category filters, delete item button, and fix itinerary persistence
- Expand POI categories from 5 to 7 (restaurant, bar, coffee, hotel, parking, attraction, entertainment)
- Add category filter chips with per-category API calls and caching
- Add delete button with confirmation dialog to Edit Item sheet
- Fix itinerary items not persisting: use LocalItineraryItem (SwiftData) as primary store with CloudKit sync as secondary, register model in schema

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 16:04:53 -06:00
Trey t
999b5a1190 Fix game times with UTC data, restructure schedule by date
- Update games_canonical.json to use ISO 8601 UTC timestamps (game_datetime_utc)
- Fix BootstrapService timezone-aware parsing for venue-local fallback
- Fix thread-unsafe shared DateFormatter in RichGame local time display
- Bump SchemaVersion to 4 to force re-bootstrap with correct UTC data
- Restructure schedule view: group by date instead of sport, with sport
  icons on each row and date section headers showing game counts
- Fix schedule row backgrounds using Theme.cardBackground instead of black
- Sort games by UTC time with local-time tiebreaker for same-instant games

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 11:43:39 -06:00
Trey t
e6c4b8e12b Add nearby POIs to Add-to-Day sheet and improve PlaceSearchSheet empty state
- Add mapItem field to POISearchService.POI for Apple Maps integration
- Merge description + location into single combined card in QuickAddItemSheet
- Auto-load nearby POIs when regionCoordinate is available, with detail sheet
- Create POIDetailSheet with map preview, metadata, and one-tap add-to-day
- Add poiAddedToDay/poiDetailViewed analytics events
- Add initial state to PlaceSearchSheet with search suggestions and flow layout

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 10:45:36 -06:00
Trey t
e7420061a5 fix: 22 audit fixes — concurrency, memory, performance, accessibility
- Move 7 Data(contentsOf:) calls off MainActor via Task.detached (BootstrapService)
- Batch-fetch N+1 queries in sync merge loops (CanonicalSyncService)
- Predicate-based gamesForTeam fetch instead of fetching all games (DataProvider)
- Proper Sendable on RouteInfo with nonisolated(unsafe) polyline (LocationService)
- [weak self] in BGTaskScheduler register closures (BackgroundSyncManager)
- Cache tripDays, routeWaypoints as @State with recompute (TripDetailView)
- Remove unused AnyCancellable, add Task lifecycle management (TripDetailView)
- Cache sportStadiums, recentVisits as stored properties (ProgressViewModel)
- Dynamic Type fonts replacing hardcoded sizes (OnboardingPaywallView)
- Accessibility labels/hints on stadium picker, date picker, map, stats,
  settings toggle, and day cards

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 09:23:29 -06:00
Trey t
dad3270be7 Add F-010, F-017 UI tests and update QA test plan with 24 automation mappings
- F-010: Tap active tab scrolls to top (TabNavigationTests)
- F-017: Recent trips section with saved trips (HomeTests)
- Update SportsTime_QA_Test_Plan.xlsx with all 60 automated test mappings
- Green highlight on automated cells for visual tracking

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 23:18:00 -06:00
Trey t
d0cbf75fc4 fix: 14 audit fixes — concurrency, memory, performance, accessibility
Second audit round addressing data races, task stacking, unbounded
caches, and VoiceOver gaps across 7 files.

Concurrency:
- Move NSItemProvider @State access into MainActor block (3 drop handlers)
- Cancel stale ScheduleViewModel tasks on rapid filter changes

Memory:
- Replace unbounded image dict with LRUCache(countLimit: 50)
- Replace demo-mode asyncAfter with cancellable Task

Performance:
- Wrap debug NBA print() in #if DEBUG
- Cache visitsById via @State + onChange instead of per-render computed
- Pre-compute sportProgressFractions in ProgressViewModel
- Replace allGames computed property with hasGames bool check
- Cache sortedTrips via @State + onChange in SavedTripsListView

Accessibility:
- Add combined VoiceOver label to progress ring
- Combine away/home team text into single readable phrase
- Hide decorative StadiumDetailSheet icon from VoiceOver
- Add explicit accessibilityLabel to SportFilterChip
- Add combined accessibilityLabel to GameRowView

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 22:30:30 -06:00
Trey t
c32a08a49e Merge branch 'main' of github.com:akatreyt/sportstime 2026-02-18 22:12:36 -06:00
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
ba41866602 Fix flaky UI tests: increase calendar wait timeouts and disable parallel UI testing
Calendar navigation buttons used shortTimeout (5s) which was too tight under
simulator load, causing cascading failures in wizard and trip saving tests.
Bumped to defaultTimeout (15s) and disabled parallel execution for UI tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 21:44:08 -06:00
treyt
7eaa21abd4 Stabilize flaky UI wizard and settings test flows 2026-02-18 14:51:04 -06:00
treyt
7e54ff2ef2 Add XCUITest authoring docs and templates 2026-02-18 13:24:46 -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