Commit Graph

50 Commits

Author SHA1 Message Date
8f10fe763d feat(home): custom SVG zone icons for body-zone cards (#15)
All checks were successful
Admin Web Docker / Admin Web Tests (push) Successful in 33s
CI / Detect Changes (push) Successful in 4s
Admin Web Docker / Docker Build Validation (push) Has been skipped
Admin Web Docker / Semantic Release (push) Successful in 10s
CI / YouTube Worker (push) Has been skipped
Admin Web Docker / Build & Push Docker Image (push) Successful in 53s
CI / Deploy (push) Has been skipped
2026-07-20 14:31:34 +02:00
6937a36ccd fix(ci): pr-iphone-deploy — cache, secrets, LGTM regex, concurrency, watch build, scope (#12)
All checks were successful
Admin Web Docker / Docker Build Validation (push) Has been skipped
Admin Web Docker / Admin Web Tests (push) Successful in 33s
CI / Detect Changes (push) Successful in 3s
Admin Web Docker / Semantic Release (push) Successful in 11s
CI / YouTube Worker (push) Has been skipped
Admin Web Docker / Build & Push Docker Image (push) Successful in 22s
CI / Deploy (push) Has been skipped
2026-07-19 20:59:33 +02:00
Millian Lamiaux
316dab2f9c fix: restore UIBackgroundModes audio for background playback
All checks were successful
PR → Build → WiFi/USB Deploy → LGTM / Wait for LGTM comment (pull_request) Successful in 33s
CI / Detect Changes (pull_request) Successful in 3s
CI / Admin Web CI (pull_request) Has been skipped
CI / YouTube Worker (pull_request) Has been skipped
CI / Deploy (pull_request) Has been skipped
PR → Build → WiFi/USB Deploy → LGTM / Build & Deploy to iPhone (WiFi/USB) (pull_request) Successful in 2m26s
- Restaure UIBackgroundModes[audio] dans Info.plist (supprimé par erreur
  au commit précédent) → empêche iOS de suspendre l'app en arrière-plan
- Ajoute UIBackgroundModes dans project.yml pour robustesse
2026-07-04 19:35:14 +02:00
Millian Lamiaux
f5a36f0a26 fix: add NSSupportsLiveActivities to main app target + cleanup info plists
Some checks failed
CI / Detect Changes (pull_request) Successful in 3s
CI / Admin Web CI (pull_request) Has been skipped
CI / YouTube Worker (pull_request) Has been skipped
CI / Deploy (pull_request) Has been skipped
PR → Build → WiFi/USB Deploy → LGTM / Build & Deploy to iPhone (WiFi/USB) (pull_request) Successful in 2m24s
PR → Build → WiFi/USB Deploy → LGTM / Wait for LGTM comment (pull_request) Failing after 33s
- Ajout de NSSupportsLiveActivities et NSSupportsLiveActivitiesFrequentUpdates
  dans info.properties du target TabataGo (project.yml) — corrige l'erreur
  'Target does not include NSSupportsLiveActivities plist key' au runtime
- Nettoie Info.plist (retire CFBundleURLTypes, UIBackgroundModes redondants)
- Aligne les entitlements (retire aps-environment, ajoute app groups)
- Corrige le bundle version du widget (2 → 1)
2026-07-04 19:25:50 +02:00
Millian Lamiaux
309d67c88a fix(widget): add NSExtension to info.properties for xcodegen compat
Some checks failed
CI / Detect Changes (pull_request) Successful in 3s
CI / Admin Web CI (pull_request) Has been skipped
CI / YouTube Worker (pull_request) Has been skipped
CI / Deploy (pull_request) Has been skipped
PR → Build → WiFi/USB Deploy → LGTM / Build & Deploy to iPhone (WiFi/USB) (pull_request) Successful in 2m39s
PR → Build → WiFi/USB Deploy → LGTM / Wait for LGTM comment (pull_request) Failing after 32s
xcodegen parses info.path plist and generates INFOPLIST_KEY_* settings,
but silently drops nested dictionaries like NSExtension.  Adding it to
info.properties ensures xcodegen produces:
  INFOPLIST_KEY_NSExtension_NSExtensionPointIdentifier = widgetkit-extension
which Xcode preserves at build time.
2026-07-04 19:13:54 +02:00
Millian Lamiaux
807361449b fix: remove info.properties from widget target — use on-disk Info.plist with NSSupportsLiveActivities
Some checks failed
CI / Detect Changes (pull_request) Successful in 4s
CI / Admin Web CI (pull_request) Has been skipped
CI / YouTube Worker (pull_request) Has been skipped
CI / Deploy (pull_request) Has been skipped
PR → Build → WiFi/USB Deploy → LGTM / Build & Deploy to iPhone (WiFi/USB) (pull_request) Failing after 2m47s
PR → Build → WiFi/USB Deploy → LGTM / Wait for LGTM comment (pull_request) Has been skipped
2026-07-04 18:26:12 +02:00
Millian Lamiaux
ee01380be8 fix: remove remaining force-unwrap in widgetURL (AGENTS.md §7) 2026-07-04 18:19:37 +02:00
Millian Lamiaux
210c254298 fix: scope shared model sources to TabataGoWidget only
Some checks failed
PR → Build → WiFi/USB Deploy → LGTM / Build & Deploy to iPhone (WiFi/USB) (pull_request) Successful in 2m33s
PR → Build → WiFi/USB Deploy → LGTM / Wait for LGTM comment (pull_request) Failing after 33s
CI / Detect Changes (pull_request) Successful in 3s
CI / Admin Web CI (pull_request) Has been skipped
CI / YouTube Worker (pull_request) Has been skipped
CI / Deploy (pull_request) Has been skipped
Previous sed command accidentally injected shared model files into
TabataGo, TabataGoWatch, and TabataGoWatchWidget targets, corrupting
their YAML structure. Restored all targets; shared files now correctly
scoped to TabataGoWidget target only.
2026-07-04 18:15:11 +02:00
Millian Lamiaux
1bb6a544e4 fix: add shared model sources to TabataGoWidget target
Some checks failed
CI / Detect Changes (pull_request) Successful in 3s
CI / Admin Web CI (pull_request) Has been skipped
PR → Build → WiFi/USB Deploy → LGTM / Build & Deploy to iPhone (WiFi/USB) (pull_request) Failing after 8s
PR → Build → WiFi/USB Deploy → LGTM / Wait for LGTM comment (pull_request) Has been skipped
CI / YouTube Worker (pull_request) Has been skipped
CI / Deploy (pull_request) Has been skipped
Widget extension references WorkoutPhase, WorkoutActivityAttributes,
TogglePauseIntent (WorkoutLiveActivity) and MusicActivityAttributes
(MusicLiveActivity) but cannot import parent app module. Shared source
files must be compiled in both targets, following existing pattern
of WatchConnectivityTypes.swift shared between iOS/watch.
2026-07-04 18:11:16 +02:00
Millian Lamiaux
a17367081c fix: add widget target, push entitlement, error handling, force-unwrap
Some checks failed
CI / Deploy (pull_request) Has been skipped
CI / Detect Changes (pull_request) Successful in 3s
CI / Admin Web CI (pull_request) Has been skipped
CI / YouTube Worker (pull_request) Has been skipped
PR → Build → WiFi/USB Deploy → LGTM / Build & Deploy to iPhone (WiFi/USB) (pull_request) Failing after 46s
PR → Build → WiFi/USB Deploy → LGTM / Wait for LGTM comment (pull_request) Has been skipped
Phase 1 — Widget extension target:
- Add TabataGoWidget target to project.yml (app-extension, iOS 26.0)
- Embed in TabataGo, add to scheme build targets

Phase 2 — Push notification capability:
- Add aps-environment: development to TabataGo.entitlements
- Required by iOS 26 for AppIntents-based Live Activity buttons

Phase 3 — Error handling hardening:
- Catch ActivityAuthorizationError specifically with user message
- Truncate generic errors to 200 chars to avoid UI overflow
- Fix force-unwrap at WorkoutLiveActivity.swift:556 (widgetURL)
2026-07-04 18:07:59 +02:00
Millian Lamiaux
40ffb04f31 fix: Dynamic Island, Live Activity errors, background audio stop, CFBundleVersion
Some checks failed
CI / Deploy (pull_request) Has been skipped
PR → Build → WiFi/USB Deploy → LGTM / Wait for LGTM comment (pull_request) Failing after 8m7s
CI / Detect Changes (pull_request) Successful in 3s
CI / Admin Web CI (pull_request) Has been skipped
CI / YouTube Worker (pull_request) Has been skipped
PR → Build → WiFi/USB Deploy → LGTM / Build & Deploy to iPhone (WiFi/USB) (pull_request) Successful in 2m58s
Phase 1 — Audio background resilience:
- Remove .mixWithOthers, app takes audio focus for coach cues
- Add AVAudioSession.interruptionNotification handler (re-activates after calls/Siri)
- Add AVAudioSession.routeChangeNotification handler (re-activates on headphone changes)

Phase 2 — Live Activity error surfacing:
- PlayerViewModel: @Published liveActivityError captures auth-denied + request errors
- PlayerView: translucent error banner with dismiss button (non-blocking)

Phase 3 — CFBundleVersion 1→2 in all 4 Info.plist files (match project.yml CURRENT_PROJECT_VERSION: 2)
2026-07-04 17:54:09 +02:00
Millian Lamiaux
560b056dc9 fix: update workflow references to .github/ and gitignore xcuserstate
All checks were successful
CI / Detect Changes (push) Successful in 3s
CI / Admin Web CI (push) Has been skipped
CI / YouTube Worker (push) Has been skipped
CI / Deploy (push) Has been skipped
Update AGENTS.md and docs/ci-cd-setup.md from .gitea/workflows to .github/workflows

Add *.xcuserstate to .gitignore

Remove tracked UserInterfaceState.xcuserstate from git (preserve on disk)
2026-07-04 16:46:09 +02:00
Millian Lamiaux
a309b2b593 chore: purge obsolete .archive docs and relocate CI workflows
- Remove 5 outdated TabataFit/Expo concept docs (.archive/)
- Rename .gitea/workflows -> .github/workflows
- Ignore build/ directory
- Add Xcode UserInterfaceState
2026-07-04 16:41:34 +02:00
a382ad62dc fix: move #if outside array literal in TabataGoComplication.supportedFamilies
All checks were successful
PR → Build → Firebase OTA → LGTM / Build & Deploy to Firebase OTA (pull_request) Successful in 4m59s
PR → Build → Firebase OTA → LGTM / Wait for LGTM comment (pull_request) Successful in 4m5s
2026-06-27 20:40:39 +00:00
52346fb4ac fix: align bundleIdPrefix with Firebase app (com.tabatago → fr.millianlmx.tabatago)
Some checks failed
PR → Build → Firebase OTA → LGTM / Build & Deploy to Firebase OTA (pull_request) Failing after 10m58s
PR → Build → Firebase OTA → LGTM / Wait for LGTM comment (pull_request) Has been skipped
2026-06-27 20:00:35 +00:00
6cc25300d1 fix: change bundle ID from com.tabatago.app to fr.millianlmx.tabatago
Some checks failed
PR → Build → Firebase OTA → LGTM / Build & Deploy to Firebase OTA (pull_request) Failing after 4m2s
PR → Build → Firebase OTA → LGTM / Wait for LGTM comment (pull_request) Has been skipped
2026-06-27 08:29:33 +00:00
c45e85edb6 fix(ci): remove stale .xcodeproj from git, nuke before xcodegen to prevent PostHog ghost dependency
Some checks failed
PR → Build → Firebase OTA → LGTM / Build & Deploy to Firebase OTA (pull_request) Failing after 7m8s
PR → Build → Firebase OTA → LGTM / Wait for LGTM comment (pull_request) Has been skipped
2026-06-27 01:35:07 +00:00
afc161c8ce fix: remove PostHog from project.yml — EmitSwiftModule crashes on Xcode 26
Some checks failed
PR → Build → Firebase OTA → LGTM / Build & Deploy to Firebase OTA (pull_request) Failing after 2s
PR → Build → Firebase OTA → LGTM / Wait for LGTM comment (pull_request) Has been skipped
2026-06-27 00:48:00 +00:00
58177102a4 fix: permanently fix PostHog module emission error
Some checks failed
PR → Build → Firebase OTA → LGTM / Build & Deploy to Firebase OTA (pull_request) Failing after 0s
PR → Build → Firebase OTA → LGTM / Wait for LGTM comment (pull_request) Has been skipped
- Change PostHog from exactVersion to revision (tag 3.62.0) in project.yml
- Add deep cache clean step before xcodegen in CI workflow
- Add ONLY_ACTIVE_ARCH=NO to xcodebuild command
- Wrap PostHog usage in #if canImport(PostHog) for graceful degradation
- Add Package.resolved, .build/, DerivedData/ to .gitignore
- Remove stale SPM caches between CI runs (DerivedData, swiftpm caches, act cache)
2026-06-27 00:39:52 +00:00
7fb44198e7 fix: kill stale Package.resolved, pin PostHog exact, add module emission flags
Some checks failed
PR → Build → Firebase OTA → LGTM / Build & Deploy to Firebase OTA (pull_request) Failing after 8m39s
PR → Build → Firebase OTA → LGTM / Wait for LGTM comment (pull_request) Has been skipped
2026-06-27 00:28:40 +00:00
60ac624487 fix: use canImport(WatchKit) guard for accessoryCorner, use -scheme in CI build
Some checks failed
PR → Build → Firebase OTA → LGTM / Build & Deploy to Firebase OTA (pull_request) Failing after 2m8s
PR → Build → Firebase OTA → LGTM / Wait for LGTM comment (pull_request) Has been skipped
- Replace #if os(watchOS) with #if canImport(WatchKit) for all .accessoryCorner
  usages. When the CI builds with forced iphoneos SDK, os(watchOS) evaluates
  to true (target platform is watchOS) but the SDK doesn't have WatchKit, so
  accessoryCorner is unavailable. canImport(WatchKit) correctly checks SDK
  availability rather than target platform.

- Wrap CornerComplicationView struct and its preview in #if canImport(WatchKit)
  with @available(watchOS 10.0, *) annotation.

- Change CI build from -target TabataGo -sdk iphoneos to -scheme TabataGo
  (without -sdk). This lets xcodebuild pick the correct SDK per target:
  iphoneos for iOS, watchos for watchOS. The scheme already exists in
  project.yml.
2026-06-26 23:39:38 +00:00
a6ea4ca904 fix: wrap accessoryCorner in #if os(watchOS) for Xcode 26 compat
Some checks failed
PR → Build → Firebase OTA → LGTM / Build & Deploy to Firebase OTA (pull_request) Failing after 6m41s
PR → Build → Firebase OTA → LGTM / Wait for LGTM comment (pull_request) Has been skipped
2026-06-26 23:35:12 +00:00
5b67aea7ce fix: update PostHog to 3.62.0 to fix SwiftDriver compilation error
Some checks failed
PR → Build → Firebase OTA → LGTM / Build & Deploy to Firebase OTA (pull_request) Failing after 9m28s
PR → Build → Firebase OTA → LGTM / Wait for LGTM comment (pull_request) Has been skipped
- Pin PostHog to 3.62.0 (vendored PLCrashReporter, drops external
  plcrashreporter dependency that fails with Xcode 26/Swift 6)
- Update project.yml and pbxproj minimumVersion
- Delete Package.resolved to force clean SPM resolution
- Add -resolvePackageDependencies step in CI workflow
- Add -skipPackagePluginValidation to build command
- Fix docs/ci-cd-setup.md manual build command
2026-06-26 23:07:57 +00:00
86fb683428 fix: add DEVELOPMENT_TEAM to project.yml
Some checks failed
PR → Build → Firebase OTA → LGTM / Build & Deploy to Firebase OTA (pull_request) Failing after 3m30s
PR → Build → Firebase OTA → LGTM / Wait for LGTM comment (pull_request) Has been skipped
2026-06-26 22:48:01 +00:00
cfe8e8cd1b fix: add TabataGo scheme to project.yml (xcodegen 2.45.4 compatibility)
Some checks failed
PR → Build → Firebase OTA → LGTM / Build & Deploy to Firebase OTA (pull_request) Failing after 50s
PR → Build → Firebase OTA → LGTM / Wait for LGTM comment (pull_request) Has been skipped
2026-06-26 22:41:13 +00:00
Millian Lamiaux
310124ad63 feat: move HealthKit permission to onboarding, remove HR write
All checks were successful
CI / Detect Changes (pull_request) Successful in 3s
CI / Admin Web CI (pull_request) Has been skipped
CI / YouTube Worker (pull_request) Has been skipped
CI / Deploy (pull_request) Has been skipped
- Add .health step to onboarding between frequency and ready
- HealthStep with non-blocking permission flow (Not Now skips)
- Remove requestAuthorization() from PlayerViewModel.startWorkout()
- Guard live session start with isAuthorized check
- Remove heart rate write from HealthKit authorization popup
- Remove HR sample writing from saveWorkout (now without permission)
- Add L10n keys: healthAccess, healthAccessSubtitle, allowHealthAccess, notNow
- Add EN/DE/ES/FR translations
- Track permission decisions through analytics
- Entry animation on HealthStep (fade-in + slide-up)

HealthKit permission is now asked once during onboarding,
never interrupting workouts again.
2026-05-24 15:18:11 +02:00
Millian Lamiaux
df9fd48964 chore: update tabatago-swift submodule (Live Activity fix)
Some checks failed
CI / TypeScript (pull_request) Failing after 4s
CI / ESLint (pull_request) Failing after 4s
CI / Tests (pull_request) Failing after 6s
CI / Build Check (pull_request) Has been skipped
CI / Admin Web Tests (pull_request) Successful in 2m7s
CI / Deploy Edge Functions (pull_request) Has been skipped
Includes fix for stale Live Activity persisting after workout
cancel/background. See submodule commit e42c121 for details.
2026-05-23 00:41:41 +02:00
Millian Lamiaux
e42c1217db fix: Live Activity persists after workout cancel/background
Root cause: observeActivityState() prematurely set workoutActivity=nil
when the activity went .stale (e.g. app backgrounded >2 minutes). This
prevented endActivity() from calling .end() on the stale activity,
leaving it visible on the Lock Screen and Dynamic Island indefinitely.

Fixes (all in PlayerViewModel.swift):

1. observeActivityState(): Split the monolithic stale/ended/dismissed
   handler. .stale now only stops the sync timer but keeps the
   workoutActivity reference so endActivity() can still call .end()
   to properly dismiss the stale Live Activity.

2. syncActivity() nil guard: Changed from != .active to explicit
   == .ended || == .dismissed so stale activities are not prematurely
   discarded when tick() re-enters syncActivity().

3. endActivity(): Added stopActivitySyncTimer() defensive call at top
   to prevent orphaned timer from racing in and recreating the activity
   during .end(). Also relaxed the guard from == .active to
   != .ended && != .dismissed so stale activities can be ended.

4. abandonWorkout(): Explicitly set isRunning=false + isPaused=false
   before cleanup to prevent accidental Live Activity recreation.
2026-05-23 00:40:41 +02:00
Millian Lamiaux
cd6fea9b53 ci: add App Store submission pipeline via GitHub Actions
Some checks failed
CI / TypeScript (pull_request) Failing after 4s
CI / ESLint (pull_request) Failing after 4s
CI / Tests (pull_request) Failing after 12s
CI / Build Check (pull_request) Has been skipped
CI / Admin Web Tests (pull_request) Successful in 2m7s
CI / Deploy Edge Functions (pull_request) Has been skipped
2026-05-21 10:47:48 +02:00
Millian Lamiaux
c152c22ffb feat: redesign Dynamic Island with phase-driven UI and animations 2026-05-21 10:21:22 +02:00
Millian Lamiaux
67e2bdc8c3 Redesign workout live activity with circular timer ring, phase icons, and smoother updates
Some checks failed
CI / TypeScript (pull_request) Failing after 19s
CI / ESLint (pull_request) Failing after 4s
CI / Tests (pull_request) Failing after 7s
CI / Build Check (pull_request) Has been skipped
CI / Admin Web Tests (pull_request) Successful in 2m9s
CI / Deploy Edge Functions (pull_request) Has been skipped
- Add CountdownRing with real-time arc progress on lock screen
- Replace generic dots with phase-specific SF Symbols (flame, snowflake, etc.)
- Remove horizontal progress bar in favor of round counter text
- Increase Dynamic Island expanded font sizes for better visibility
- Increase live activity sync frequency from 5s to 1s for smoother arc updates
- Add pause/resume button via TogglePauseIntent AppIntent
- Remove AlertConfiguration to silence notification sounds on updates
2026-05-17 00:43:01 +02:00
Millian Lamiaux
dc3ff15e81 feat: production-grade Live Activity with type-safe phases, decomposed views, previews, and alert transitions
- Replace raw string phase model with WorkoutPhase enum (Codable, Sendable, CaseIterable)
  with built-in .capitalized display name and SwiftUI .color per phase
- Decompose WorkoutLiveActivity into reusable view structs: PhasePill, CountdownText,
  WorkoutProgressBar, MusicInfoRow, HeartRateBadge, PhaseIndicatorDot, WorkoutLockScreenView,
  WorkoutSmallView — following CraftingSwift iOS 26 architecture patterns
- Add AlertConfiguration on work/rest/complete phase transitions so Dynamic Island
  expands and lights up at key moments
- Add 13 #Preview blocks across both widgets covering all presentation types:
  lock screen, expanded, compact, minimal — for instant Xcode Canvas feedback
- Add stale state handling (context.isStale shows 'Last updated' indicator)
- MusicLiveActivity: 5 new #Preview blocks for playing/paused/expanded/compact/minimal
2026-05-16 15:28:45 +02:00
Millian Lamiaux
95f34e6471 feat: Dynamic Island pause state, Apple-aligned spacing, and UI polish
- Add isPaused to WorkoutActivityAttributes.ContentState
- Show PAUSED badge, freeze timer to static text, dim content when paused
- Prevent stale spinner on pause by extending staleDate to 1 hour
- Add 6s timer warning color, progress bar, compact heavy timer
- Pulsing compact indicator during WORK phase
- Lock Screen margins aligned to Apple's 14pt HIG spec
2026-05-15 23:52:01 +02:00
Millian Lamiaux
057fbb3c9a fix: add 6s timeout to MusicService Supabase fetch for offline fallback
When airplane mode is active, the Supabase client hung indefinitely
waiting for a network response, blocking the mock track fallback.
Now races the query against a 6-second Task.sleep so mock tracks
load immediately after timeout.
2026-05-15 23:51:51 +02:00
Millian Lamiaux
918e663dbf chore: update Xcode project for widget target and watch team
- Add widget target PBXBuildFile, PBXFileReference, PBXGroup, PBXNativeTarget entries
- Reorder PBXCopyFilesBuildPhase and XCBuildConfiguration sections
- Add DEVELOPMENT_TEAM to watch complication and watch app configs
2026-05-15 22:41:35 +02:00
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
71de3c0aa7 fix: Live Activity concurrency and state observation
- Add Sendable conformance to MusicActivityAttributes.ContentState
- Remove @preconcurrency on ActivityKit import
- Use nonisolated(unsafe) guards for Activity refs in task closures
- Add observeActivityState() to handle stale/ended/dismissed activity states
- Set staleDate (120s) instead of nil for push notification support
2026-05-15 22:41:04 +02:00
Millian Lamiaux
349a96379e add mock programs fallback for offline development 2026-05-10 19:20:30 +01: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
Millian Lamiaux
9943dce82d fix: add HealthKit entitlement and regenerate Xcode project to resolve NSInvalidArgumentException
Some checks failed
CI / TypeScript (pull_request) Failing after 5s
CI / ESLint (pull_request) Failing after 4s
CI / Tests (pull_request) Failing after 5s
CI / Build Check (pull_request) Has been skipped
CI / Admin Web Tests (pull_request) Successful in 2m8s
CI / Deploy Edge Functions (pull_request) Has been skipped
The iOS app target was missing the com.apple.developer.healthkit entitlement
and the Xcode project was out of sync with project.yml, causing a crash when
HealthKitService requested write authorization for active energy, workouts, and
heart rate types.
2026-04-23 22:34:54 +02:00
Millian Lamiaux
cf096f2068 Redesign Activity tab with animated rings, monthly calendar, and global stats
Some checks failed
CI / TypeScript (push) Failing after 7s
CI / ESLint (push) Failing after 3s
CI / Tests (push) Failing after 7s
CI / Build Check (push) Has been skipped
CI / Admin Web Tests (push) Successful in 2m6s
CI / Deploy Edge Functions (push) Has been skipped
2026-04-22 01:18:42 +02:00
Millian Lamiaux
d74c47b1a8 Move level badge to top-left and free badge to top-right in FeaturedProgramCard 2026-04-22 00:54:42 +02:00
Millian Lamiaux
0f5b7b9e18 feat(i18n): complete internationalization for iOS + watchOS across all views
Migrate every hardcoded Text("...") string to the L10n / LocalizedStringResource
type-safe key system with full en/fr/de/es translations (4 languages).

iOS changes (TabataGo target):
- Strings.swift: ~90 new L10n keys across 13 groups (action, tab, home, zone,
  level, programs, programDetail, player, profile, settings, policy, paywall,
  health, complete, activity, onboarding, goal)
- Localizable.xcstrings: 145 → 245+ keys with fr/de/es translations
- Model enums: FitnessLevel.label & FitnessGoal.label changed from String to
  LocalizedStringResource, backed by L10n.level/goal keys
- Component param types changed to LocalizedStringResource: StatBadge,
  SectionHeader, ProfileRow, PolicySection, CompletionStat, FeatureRow,
  OnboardingHeader, PrimaryButton, SelectionCard
- All 18 view files updated: HomeTab, ActivityTab, ProgramsTab, ProfileTab,
  MainTabView, SettingsView, PolicyViews, CompletionView, BodyZoneView,
  ProgramDetailView, PaywallView, OnboardingView, PlayerView

Watch changes (TabataGoWatch target):
- New Localizable.xcstrings: 23 keys with en/fr/de/es (phase labels, idle
  state, activity rings, complication strings)
- New WatchL10n.swift: type-safe enum (needs manual Xcode target membership)
- Updated: WatchPlayerView, WatchIdleView, WatchActivityView,
  TabataGoComplication (inline LocalizedStringResource for widget target)

Both iOS and watchOS targets build with zero errors.
2026-04-22 00:41:19 +02:00
Millian Lamiaux
e28bebea79 Redesign FeaturedProgramCard and reorder Home sections
- Replace external pill badges (level + FREE) with immersive overlay card:
  level tag pinned top-right with ultraThinMaterial blur, FREE shown inline
  with checkmark.seal.fill icon, dark scrim for text legibility
- Remove card drop shadow
- Move Browse by Zone section above Featured in Home tab scroll order
2026-04-21 23:18:15 +02:00
Millian Lamiaux
9f15ae2d79 fix: dismiss paywall and sync premium state after successful purchase
- Add purchaseSucceeded flag to PurchaseViewModel, set on purchase success
- PaywallView observes the flag and dismisses itself automatically
- ProfileTab.syncSubscription() writes PurchaseService.currentPlan back
  to UserProfile.subscriptionRaw via SwiftData on sheet dismiss
2026-04-21 23:00:45 +02:00
Millian Lamiaux
877f836f19 Replace double chip filters with segmented control + dropdown menu in ProgramsTab
Swap the two horizontal FilterChip scroll rows with a native segmented
Picker for body zone and a toolbar Menu for difficulty level. Fix zone
values to match Supabase enum (upper-body, lower-body, full-body).
Remove unused FilterChip struct.
2026-04-21 22:47:47 +02:00
Millian Lamiaux
2413bc0356 Remove 'All Workouts' section from HomeTab 2026-04-21 22:04:56 +02:00
Millian Lamiaux
89cca25e22 remove Expo project and all related files
Remove the entire Expo/React Native application: routes (app/), source
code (src/), assets, iOS native build, config plugins, StoreKit config,
npm dependencies, TypeScript/ESLint/Vitest configs, and Expo-specific
documentation. The repository now contains only: admin-web, supabase,
youtube-worker, tabatago-swift, docs, scripts, and CI/tooling configs.
2026-04-21 21:55:00 +02:00