Commit Graph

4 Commits

Author SHA1 Message Date
Millian Lamiaux
fe005ee7f3 feat: Live Activity accessibility and supplemental families (small/medium)
- Add @Environment activityFamily, isActivityFullscreen, isLuminanceReduced
- Split into lockScreenView() and smallLockScreenView() variants
- Add supplementalActivityFamilies([.small, .medium]) support
- Add keylineTint and contentMargins to Dynamic Island
- Add accessibility labels throughout (VoiceOver support)
- Hide music bar animation when isLuminanceReduced
2026-05-15 22:41:20 +02:00
Millian Lamiaux
d43142641f refactor: clean up Dynamic Island expanded layout with phase badge, symmetric edges, and music+heart rate in bottom region 2026-05-03 20:59:03 +02:00
Millian Lamiaux
c715c797f9 fix: move Live Activity ownership to ViewModel, fix timer-at-0 and background freeze
**Architecture (PlayerViewModel):**
- Move ActivityKit lifecycle from SwiftUI View to ViewModel (MVVM correction)
- call syncActivity() at END of enterPhase() — after all state is set,
  eliminating the race where phase was Published before timeRemaining
- Always recalculate phaseEndDate = Date() + timeRemaining (no stale cache)
- Dedicated Timer in ViewModel for periodic heart-rate/track sync (5s)
- Start/stop activity sync timer on play/pause/resume/abandon/finish
- stale activity reference discard + recreate-on-failure fallback
- Modern iOS 16.2+ API: ActivityContent, non-throwing update()

**PlayerView:**
- Remove all ActivityKit code (import, @State workoutActivity,
  phaseEndDate, dynamicIslandAvailable, 4 methods, .onReceive timer)
- Delegate to ViewModel: onChange(musicVM.currentTrack) sets vm.trackTitle/Artist
  and calls vm.syncActivity(); onDisappear calls await vm.endActivity()
- Music/audio onChange handlers no longer contain activity logic

**Info.plist:**
- Add UIBackgroundModes → audio so music continues and app stays alive
  in background, allowing Timer-based activity updates
- Widget Info.plist: add NSSupportsLiveActivitiesFrequentUpdates

**WorkoutActivityAttributes.ContentState:**
- Add Sendable conformance for Swift 6 strict concurrency

Fixes: timer stuck at 0 on first work phase, exercise name missing,
       music stopping in background, Dynamic Island freezing in background,
       widget drift due to cached phaseEndDate
2026-05-03 15:40:36 +02:00
Millian Lamiaux
b0d364eca2 feat: redesign player with Dynamic Island, compact timer, and fix Live Activity timer drift
Some checks failed
CI / TypeScript (pull_request) Failing after 5s
CI / ESLint (pull_request) Failing after 3s
CI / Tests (pull_request) Failing after 5s
CI / Build Check (pull_request) Has been skipped
CI / Admin Web Tests (pull_request) Successful in 2m5s
CI / Deploy Edge Functions (pull_request) Has been skipped
## What changed

### Player Redesign (video-first layout)
- New compact timer ring (110pt) with phase label, replaces 240pt ring
- Auto-hide top bar with block progress dots (3s auto-dismiss)
- Expandable now-playing music pill with skip control
- Bottom control bar with heart rate, play/pause, and skip
- Exercise caption with 'Next' preview during rest phases
- Compact round counter (capsule dots)

### Dynamic Island & Live Activities
- WorkoutLiveActivity widget: expanded, compact, and minimal views
- Phase-colored timers with Text(timerInterval:) countdown
- Shows exercise name, round progress, heart rate, music track
- MusicLiveActivity: standalone music now-playing widget
- LiveActivityMusicBars animated component
- Deep link from Dynamic Island back to app

### Timer Drift Fix (critical)
- Store a stable phaseEndDate once per phase instead of
  recalculating Date() + timeRemaining on every update
- Prevents dynamic island countdown from rubber-banding
  due to 5-second periodic update recalculation drift
- Reset phaseEndDate on phase change and resume from pause
- Guard Live Activity updates behind vm.isRunning to prevent
  premature creation when music track loads before workout start
- Fixes timer showing 0 in Dynamic Island when expanding
  from home screen

### New PlayerViewModel timer engine
- Full phase support: prep, warmup, work, rest,
  interBlockRest, cooldown, complete
- 1-second countdown with audio cues at 3-2-1
- Phase transitions with spring animation and haptics
- HealthKit live session integration
- Workout session recording with completion

### Music Service
- New MusicPlayerViewModel with vibe-based playlist loading
- Track info exposed for Dynamic Island display
- Skip track support from Dynamic Island notification action
- Automatic play/pause based on phase and running state

### Additional
- ZoneHighlightIcon component for HomeTab zone cards
- Updated watchOS localizations with complication strings
- Info.plist updated for widget extension
2026-04-25 23:51:46 +02:00