Commit Graph

360 Commits

Author SHA1 Message Date
Trey t
c94e373e33 fix: comprehensive codebase hardening — crashes, silent failures, performance, and security
Fixes ~95 issues from deep audit across 12 categories in 82 files:

- Crash prevention: double-resume in PhotoMetadataExtractor, force unwraps in
  DateRangePicker, array bounds checks in polls/achievements, ProGate hit-test
  bypass, Dictionary(uniqueKeysWithValues:) → uniquingKeysWith in 4 files
- Silent failure elimination: all 34 try? sites replaced with do/try/catch +
  logging (SavedTrip, TripDetailView, CanonicalSyncService, BootstrapService,
  CanonicalModels, CKModels, SportsTimeApp, and more)
- Performance: cached DateFormatters (7 files), O(1) team lookups via
  AppDataProvider, achievement definition dictionary, AnimatedBackground
  consolidated from 19 Tasks to 1, task cancellation in SharePreviewView
- Concurrency: UIKit drawing → MainActor.run, background fetch timeout guard,
  @MainActor on ThemeManager/AppearanceManager, SyncLogger read/write race fix
- Planning engine: game end time in travel feasibility, state-aware city
  normalization, exact city matching, DrivingConstraints parameter propagation
- IAP: unknown subscription states → expired, unverified transaction logging,
  entitlements updated before paywall dismiss, restore visible to all users
- Security: API key to Info.plist lookup, filename sanitization in PDF export,
  honest User-Agent, removed stale "Feels" analytics super properties
- Navigation: consolidated competing navigationDestination, boolean → value-based
- Testing: 8 sleep() → waitForExistence, duplicates extracted, Swift 6 compat
- Service bugs: infinite retry cap, duplicate achievement prevention, TOCTOU vote
  fix, PollVote.odg → voterId rename, deterministic placeholder IDs, parallel
  MKDirections, Sendable-safe POI struct

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 17:03:09 -06:00
Trey t
e046cb6b34 Fix custom item edit refresh and stabilize F069 UI test 2026-02-22 22:11:01 -06:00
Trey t
ec2bbb4764 Stabilize beta release with warning cleanup and edge-case fixes 2026-02-22 13:18:14 -06:00
Trey t
fddea81e36 fix: add contentShape to team picker rows for full-row tap targets
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 00:43:44 -06:00
Trey t
91c5eac22d fix: codebase audit fixes — safety, accessibility, and production hygiene
Address 16 issues from external audit:
- Move StoreKit transaction listener ownership to StoreManager singleton with proper deinit
- Remove noisy VoiceOver announcements, add missing accessibility on StatPill and BootstrapLoadingView
- Replace String @retroactive Identifiable with IdentifiableShareCode wrapper
- Add crash guard in AchievementEngine getContributingVisitIds + cache stadium lookups
- Pre-compute GamesHistoryViewModel filtered properties to avoid redundant SwiftUI recomputation
- Remove force-unwraps in ProgressMapView with safe guard-let fallback
- Add diff-based update gating in ItineraryTableViewWrapper to prevent unnecessary reloads
- Replace deprecated UIScreen.main with UIWindowScene lookup
- Add deinit task cancellation in ScheduleViewModel and SuggestedTripsGenerator
- Wrap ~234 unguarded print() calls across 27 files in #if DEBUG

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 00:07:53 -06:00
Trey t
826eadbc0f fix: ScenarioCPlanner endpoint merging and game validation
Eliminate redundant 0-mile travel segments when start/end city matches
the first/last game stop city, and fail early when no games exist at
endpoint cities within the selected date range.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 22:40:06 -06:00
Trey t
242634e03c Merge branch 'main' of github.com:akatreyt/sportstime 2026-02-21 20:45:32 -06:00
akatreyt
e948723c00 Merge pull request #6 from akatreyt/fix/issue-3 2026-02-21 18:06:04 -06:00
akatreyt
5689306895 Merge pull request #5 from akatreyt/fix/issue-2 2026-02-21 18:05:51 -06:00
akatreyt
9b7ac7d934 Merge pull request #4 from akatreyt/fix/issue-1 2026-02-21 18:05:39 -06:00
treyt
d98acd6a9a fix: resolve issue #3 - Iap coupon
Automated fix by Tony CI v3.
Closes #3

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-21 18:03:09 -06:00
treyt
ad3febf69b fix: resolve issue #2 - By router planning
Automated fix by Tony CI.
Closes #2

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-21 01:41:17 -06:00
Trey t
a4e9327b18 fix: restrict By Route wizard to stadium cities and filter sports by selected cities
LocationSearchSheet now shows only stadium cities (with sport badges) when
selecting start/end locations, preventing users from picking cities with no
stadiums. TripWizardViewModel filters available sports to the union of sports
at the selected cities, and clears invalid selections when locations change.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 22:47:46 -06:00
treyt
2a3fc8d5d7 fix: resolve issue #1 - Follow teams
Automated fix by Tony CI.
Closes #1

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-20 21:38:13 -06:00
Trey t
b062ced000 fix: improve text contrast for badges, category chips, and separators
- Change attraction category color from .yellow to orange variant for
  readable text in both light and dark mode (QuickAddItemSheet, POIDetailSheet)
- Fix "optional" badge to use textPrimary-based colors for strong contrast
- Bump paywall dashed separator from 0.4 to 0.6 opacity
- Fix SuggestedTripCard bullet separator from textMuted(0.5) to textSecondary

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 14:26:21 -06:00
Trey t
c52b70089e fix: move paywall sheet to stable scope and fix map button positions
Move .sheet(isPresented: $showPaywall) from subscription section to
top-level body so StoreManager state changes don't dismiss the sheet.
Reposition heart (16pt top/trailing) and map (16pt bottom/trailing,
above gradient) buttons on trip detail map.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 14:05:37 -06:00
Trey t
d1429071f6 redesign PaywallView with premium header, feature grid, and ticket separator
Replace the generic paywall header with a branded "SportsTime Pro / Your All-Access Pass"
hero section, 4 uniform feature cards (GeometryReader-sized squares), and a dashed ticket
perforation separator. Add "Show Paywall" debug button in Settings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 13:51:38 -06:00
Trey t
e2d449046b fix: add contentShape to all tappable rows so entire row area registers taps
Adds .contentShape(Rectangle()) or .contentShape(Capsule()) to 11 buttons,
NavigationLinks, and onTapGesture handlers across 8 files where only the
visible content (text/icons) was receiving taps instead of the full row.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 13:30:41 -06:00
Trey t
1b05f0ebaf fix: hide Creative Tools in release builds and improve Add Item keyboard behavior
Gate Icon Generator section behind #if DEBUG and group it with other debug
sections at the bottom of Settings. Remove auto-focus on description field,
dismiss keyboard on return key and on scroll.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 13:23:38 -06:00
Trey t
6aaa3dc073 Add F-082, F-099 UI tests and mark remaining tests RED in QA plan
Final batch: F-082 (create poll with 2+ saved trips) and F-099 (progress
percentage updates after stadium visit). Also marks 9 more impossible tests
RED (F-016, F-039, F-048, F-050, F-107, F-108, F-111, F-129, F-130).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 01:46:08 -06:00
Trey t
e6584d443d Add F-045, F-056, F-057 UI tests for planning errors and trip option filters
- F-045: Planning with conflicting constraints handles gracefully
- F-056: Pace filter (Packed/Moderate/Relaxed) selects correctly
- F-057: Cities filter limits trip results

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 01:28:05 -06:00
Trey t
4e6d8ff37f Add F-037, F-038, F-091 UI tests; mark F-016 red (no accessibility ID)
- F-037: Route preference cards (Direct/Scenic/Balanced) selectable
- F-038: Allow repeat cities toggle buttons work
- F-091: Date range filter sheet opens and applies correctly
- F-016: Marked RED - featured trip card buttons lack accessibility IDs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 01:16:25 -06:00
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