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>
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>
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>
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>
- 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>
- 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>
- 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>
- 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>
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>
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>
- 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>
- 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>
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>
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>
- 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>
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>
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>
Documents the complete sportstime_parser package including architecture,
multi-source scraping, name normalization with aliases, CloudKit uploads,
and workflows for manual review and adding new sports.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create shared TipsSection component for displaying planning tips
- Add TipsSection to all 22 home content variants
- Fix displayedTips population with onAppear in HomeView
- Add map buttons to GameRowCompact (opens stadium in Apple Maps)
- Add map buttons to TravelRowView (opens driving directions)
- Add map buttons to CustomItemRowView (opens location when GPS available)
- Add AppleMapsLauncher.openLocation() and openDirections() methods
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixed PDF export missing the last day when games occur on departure date.
Root cause: Trip.itineraryDays() calculated lastActivityDate as departure - 1,
assuming departure is always after the last activity. When games happen ON the
departure date, that day was skipped.
Fix: Check if the last stop has games. If so, include the departure date in
the itinerary loop.
Also includes:
- buildCompleteItineraryItems() to merge games, travel, and custom items
- Magazine-style PDF layout with sport-specific accent colors
- Proper iteration over all trip days in PDF generator
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add floating action button (bottom-right) on trip map
- Create AppleMapsLauncher service to handle route opening
- Collect all routable stops (trip stops + custom items with coords)
- Handle >16 waypoints with alert offering to open in parts
- Silently skip stops without coordinates
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace confusing dual-mode AddItemSheet with streamlined QuickAddItemSheet:
- Single flow with optional location (vs Search/Custom toggle)
- Card-based layout with shadows, borders, and theme colors
- Enhanced CategoryPicker with emoji circles and press animations
- Separate PlaceSearchSheet for focused location search
- Improved header button with capsule style and accessibility labels
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Travel constraint validation was not working because ItineraryConstraints
had no game items to validate against - games came from RichGame objects
but were never converted to ItineraryItem for constraint checking.
Changes:
- Add city parameter to ItemKind.game enum case
- Create game ItineraryItems from RichGame data in buildItineraryData()
- Update isValidTravelPosition to compare against actual game sortOrders
- Fix tests to use appropriate game sortOrder conventions
Now travel is properly constrained to appear before arrival city games
and after departure city games.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extract all itinerary reordering logic from ItineraryTableViewController into
ItineraryReorderingLogic.swift for testability. Key changes:
- Add flattenDays, dayNumber, travelRow, simulateMove pure functions
- Add calculateSortOrder with proper region classification (before/after games)
- Add computeValidDestinationRowsProposed with simulation+validation pattern
- Add coordinate space conversion helpers (proposedToOriginal, originalToProposed)
- Fix DragZones coordinate space mismatch (was mixing proposed/original indices)
- Add comprehensive documentation of coordinate space conventions
Test coverage includes:
- Row flattening order and semantic travel model
- Sort order calculation for before/after games regions
- Travel constraints validation
- DragZones coordinate space correctness
- Coordinate conversion helpers
- Edge cases (empty days, multi-day trips)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add cancelPendingSync() method for explicit cleanup
- Use [weak self] capture to prevent potential retain issues
- Check Task.isCancelled before and after sleep
- Catch CancellationError from Task.sleep for immediate cancellation response
- Extract debounceInterval as constant
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Update PDFGenerator to accept optional ItineraryItem array and render
items in user-specified sortOrder within each day. Adds support for:
- Custom items with icon, title, time, and address
- Travel segments from ItineraryItem (not just TravelSegment)
- Fallback to derived order when no itinerary items provided
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add constraint validation during drag using ItineraryConstraints
- Calculate invalid zones and barrier games when drag starts
- Apply visual dimming (alpha 0.3) to invalid drop zones during drag
- Highlight barrier games with gold border when dragging travel segments
- Block invalid drops using ItineraryConstraints.isValidPosition validation
- Add haptic feedback for drag interactions:
- Medium impact on pickup
- Light impact when entering valid zone
- Warning notification when entering invalid zone
- Soft impact on drop
The drag state is tracked via draggingItem, invalidRowIndices, and
barrierGameIds properties. Visual feedback is applied and removed
via applyDragVisualFeedback/removeDragVisualFeedback methods.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update CKModels.swift to remove deleted type references
- Migrate LocalCustomItem to LocalItineraryItem in SavedTrip.swift
- Update AppDelegate to handle subscription removal
- Refactor AddItemSheet to create ItineraryItem with CustomInfo
- Update ItineraryTableViewController and Wrapper for new model
- Refactor TripDetailView state, methods and callbacks
- Fix TripMapView to display custom items with new model structure
This completes the migration from the legacy CustomItineraryItem/TravelDayOverride
model to the unified ItineraryItem model with ItemKind enum.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add isEmpty parameter to show "No items yet, tap + to add" text
- Change date format to single line: "Day 1 - Friday, January 17"
- Update preview to test both empty and non-empty states
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add specialized row components for the new unified itinerary system:
- DayHeaderRow: Day number, date display, and add item button
- GameItemRow: Prominent card with sport color bar for games
- TravelItemRow: Gold-styled travel segments with drag handle
- CustomItemRow: Minimal custom items with icon, title, and optional time
All views follow existing Theme patterns and use SportColorBar for consistency.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Removed:
- CustomItineraryItem
- TravelDayOverride
- CustomItemService
- CustomItemSubscriptionService
- TravelOverrideService
- CustomItemRow
These are replaced by unified ItineraryItem model and service.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Debounced updates (1.5s), local-first with silent retry.
Supports game, travel, and custom item kinds.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Validates travel positions based on game locations:
- Travel must be after ALL departure city games
- Travel must be before ALL arrival city games
- Custom items have no constraints
- Games are fixed (cannot be moved)
12 tests covering all constraint scenarios including edge cases.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replaces CustomItineraryItem and TravelDayOverride with single model.
Supports game, travel, and custom item kinds.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>