Commit Graph

20 Commits

Author SHA1 Message Date
Sylvain
120adbed26 chore(ios): GoogleSignIn SPM 2026-06-05 08:38:47 +02:00
Sylvain Bettinelli
947d76714e feat(ios): plugin natif Sign in with Google (CoachGoogleAuth)
- CoachGoogleAuth.swift : plugin Capacitor (SDK GoogleSignIn) → signIn() renvoie
  { idToken, email?, givenName? }, POSTé sur /auth/google/web (aud = client iOS).
  Garde #if canImport(GoogleSignIn) → le repo build même avant l'ajout du SDK.
- MainViewController : enregistre CoachGoogleAuthPlugin.
- AppDelegate : GIDSignIn.sharedInstance.handle(url) dans application(_:open:).
- project.pbxproj : CoachGoogleAuth.swift dans la target App.

⚠️ Xcode (Mac) : ajouter le SDK GoogleSignIn-iOS via SPM + GIDClientID/URL
scheme (reversed client id) dans Info.plist (cf. HANDOFF). Sans le SDK, le
bouton ne s'affiche pas (signIn rejette proprement).
2026-06-05 06:15:17 +00:00
Sylvain Bettinelli
bf975ce8e6 feat(ios): plugin natif Sign in with Apple (CoachAppleAuth)
- CoachAppleAuth.swift : plugin Capacitor (AuthenticationServices) → signIn()
  renvoie { identityToken, user, email?, givenName? } au JS, POSTé sur
  /auth/apple/web (aud = bundle id ch.hypnotruck.coach).
- App.entitlements : capability com.apple.developer.applesignin (Default).
- project.pbxproj : enregistre CoachAppleAuth.swift dans la target App.
- AppDelegate : l'injection du cookie bypass n'écrase plus une session
  existante (sinon le login natif serait remplacé à chaque lancement).

Côté Xcode : ajouter la capability 'Sign in with Apple' puis build device.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 19:05:52 +00:00
Sylvain
a5795bf83f feat(live): Live Activity / Dynamic Island (WidgetKit extension iOS 16.2+/17+)
Nouvelle target "CoachLiveActivity" (Widget Extension, bundle
ch.hypnotruck.coach.LiveActivity, iOS 17.0) embarquée dans App via copy phase
"Embed App Extensions" (dstSubfolderSpec=13). API ActivityKit + WidgetKit
vérifiées sur SDK iOS 26.5 swiftinterface (Activity.request(content:),
ActivityConfiguration, DynamicIsland(expanded:compactLeading:trailing:minimal:)).

Architecture :
- CoachLiveActivityAttributes (modèle partagé App ↔ extension)
- CoachLiveActivityWidget (Lock Screen + Dynamic Island UI)
- CoachLiveActivityBundle (@main WidgetBundle)
- LiveActivityManager (App side, wrap Activity.request/update/end)
- LiveStore hook : start sur 1er sample, update à chaque ingest, end après
  60s "lost". Tout guardé @available(iOS 16.2, *) pour préserver deployment iOS 15.
- Info.plist App : NSSupportsLiveActivities = YES.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 19:51:57 +02:00
Sylvain
f65d96e908 feat(live): vue native SwiftUI avec vrai Liquid Glass iOS 26
- LiveStore.swift : bus singleton main-actor, alimenté par CoachLiveBridge,
  observable par SwiftUI. Watchdog 8s pour status .lost.
- CoachLiveView.swift : vue SwiftUI @available(iOS 26.0,*) avec
  .glassEffect()/GlassEffectContainer (vrai Liquid Glass), aurora background,
  4 cartes métriques teintées, pulse heartbeat sur FC en direct.
- CoachLiveBridge : nouvelle méthode plugin openNativeLive() qui présente
  CoachLiveView en plein écran (fallback web pour iOS < 26). emit() alimente
  aussi LiveStore.shared.
- pbxproj : registration des 2 nouveaux .swift dans la target App.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 18:05:21 +02:00
Sylvain
3c0e266cdc chore(ios): recommended build settings (Xcode) + WKRunsIndependentlyOfCompanionApp=false
- "Update to recommended settings" d'Xcode (CLANG_WARN_*, ENABLE_USER_SCRIPT_SANDBOXING,
  STRING_CATALOG_GENERATE_SYMBOLS, BuildIndependentTargetsInParallel, LastUpgradeCheck)
- Info.plist watch : WKRunsIndependentlyOfCompanionApp=false (app watch dépendante du compagnon)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 21:36:09 +02:00
Sylvain
ef69240af2 chore(watchos): supprime le warning AccentColor manquant
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME vidé sur la target CoachWatch
(pas de couleur d'accent custom, l'UI utilise des couleurs explicites). Build
Release device + Debug sim : 0 warning.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 19:15:09 +02:00
Sylvain
ae5ac7ec25 feat(watchos): AppIcon CoachWatch — prérequis TestFlight
Asset catalog mono-taille 1024 (source resources/icon.png, comme l'app iOS),
ASSETCATALOG_COMPILER_APPICON_NAME=AppIcon. Vérifié: Assets.car + CFBundleIconName
dans l'Info.plist final. Sans icône, l'upload TestFlight est refusé.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 19:09:23 +02:00
Sylvain
b2e9836b95 feat(ios): plugin Capacitor CoachLiveBridge (Phase 3) — relais WCSession -> /live
CAPPlugin + CAPBridgedPlugin (jsName CoachLiveBridge). Active WCSession au load(),
reçoit didReceiveMessage (temps réel) + didReceiveUserInfo (lot bufferisé) et
relaie au JS via notifyListeners("liveSample"). Enregistré dans
MainViewController.capacitorDidLoad(). Méthode getStatus pour debug JS. Build OK.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 16:27:15 +02:00
Sylvain
c6f5a7ffc9 feat(watchos): WorkoutManager (HKLiveWorkoutBuilder) — séance live + push iPhone
HKWorkoutSession(healthStore:configuration:) + associatedWorkoutBuilder +
HKLiveWorkoutDataSource. Collecte FC (mostRecent), calories/distance (sum) via
statistics(for:), publication @Published sur main, envoi throttlé (2s) vers
ConnectivityManager. Clôture async endCollection(at:)/finishWorkout(). Build OK.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 16:20:11 +02:00
Sylvain
5864cfcbef feat(watchos): ConnectivityManager (WCSession) — envoi des samples vers l'iPhone
sendMessage si iPhone joignable, sinon buffer + transferUserInfo (livraison
différée). Délégué WCSession hors-main, hop vers main pour @Published isReachable,
buffer sérialisé sur file dédiée. Build watchsimulator OK.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 16:11:58 +02:00
Sylvain
5d60a4049e feat(watchos): target CoachWatch (Phase 1) — app watchOS embarquée
- nouvelle target watchOS app CoachWatch (ch.hypnotruck.coach.watchkitapp), SwiftUI, SDK watchos
- entitlement HealthKit + Info.plist FR (NSHealthShare/UpdateUsageDescription) + WKBackgroundModes workout-processing
- "Embed Watch Content" + dépendance App -> CoachWatch (CoachWatch.app dans App.app/Watch)
- schemes partagés App + CoachWatch
- CoachWatchApp.swift + ContentView.swift minimaux

Build validé: target watchsimulator + embed dans App.app (iphonesimulator).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 16:09:37 +02:00
Sylvain
ec26ae0ca7 chore(ios): healthkit background-delivery + bump version 1.2
- entitlement com.apple.developer.healthkit.background-delivery (CoachWorkoutObserver)
- registration CoachWorkoutObserver.swift dans la target App
- LSApplicationCategoryType = healthcare-fitness
- MARKETING_VERSION 1.1 -> 1.2, CURRENT_PROJECT_VERSION 2 -> 22

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 15:50:25 +02:00
Sylvain Bettinelli
8d011610be feat(ios): plugin natif CoachHealthRoute (HKWorkoutRouteQuery)
@capgo/capacitor-health n'expose pas les routes GPS des workouts. Ce
plugin Swift custom appelle HKSampleQuery + HKWorkoutRouteQuery pour
récupérer la liste des CLLocation associées à un workout (par UUID).

API JS :
  CoachHealthRoute.getRoute({workoutUUID})
    → {available: bool, route: [{latitude, longitude, altitude, timestamp, speed}], count}

Format payload aligné sur HAE pour réutiliser le parser backend
existant (tools/health_parser.py).
2026-05-11 14:25:37 +00:00
Sylvain Bettinelli
a251d369df Bump v1.1 (build 2) + ITSAppUsesNonExemptEncryption=false pour TestFlight
Préparation upload TestFlight v1.1 avec :
- 12 phases d'évolution depuis v1.0 du 7 mai (bilan IA hebdo, /calendar
  refonte vue semaine, /workouts page, /profile, plan additions custom,
  WorkoutKit Apple Watch 6 commits, builder chips blocs interactif)
- MARKETING_VERSION 1.0 → 1.1 (visible côté user TestFlight)
- CURRENT_PROJECT_VERSION 1 → 2 (build number, doit être strictement
  croissant côté Apple)
- ITSAppUsesNonExemptEncryption=false : évite le questionnaire crypto
  au moment de l'upload (l'app n'utilise pas d'algorithmes hors crypto
  iOS standard)
2026-05-08 10:54:26 +00:00
Sylvain Bettinelli
83b5260bc1 Plugin discovery : MainViewController sous-classe CAPBridgeViewController
Capacitor 8 n'auto-découvre pas les plugins in-app Swift (qui ne sont pas
des npm packages). Les plugins définis dans le target App doivent être
enregistrés manuellement via une sous-classe de CAPBridgeViewController
qui override capacitorDidLoad().

Ajouts :
- ios/App/App/MainViewController.swift : sous-classe qui registers
  CoachWorkoutKitPlugin() dans capacitorDidLoad()
- Main.storyboard : customClass pointe sur MainViewController (module
  'App' target) au lieu de CAPBridgeViewController (module Capacitor)
- project.pbxproj : 4 entrées pour MainViewController.swift (BuildFile,
  FileReference, group, Sources phase)

Après rebuild, Capacitor.Plugins.CoachWorkoutKit sera dispo en JS.
2026-05-08 10:05:06 +00:00
Sylvain Bettinelli
2d3e149c0a Plugin natif WorkoutKit : pousse workouts intervalles vers Apple Watch
Plugin Capacitor Swift in-app (ios/App/App/CoachWorkoutKit.swift) qui utilise
le framework Apple WorkoutKit (iOS 17+ / watchOS 10+) pour construire un
CustomWorkout structuré (warmup + IntervalBlock work/rest répété + cooldown)
et présenter la sheet système iOS qui propose à l'user d'ajouter le workout
à ses workouts personnalisés Apple Watch.

API JS (côté WebView) :
  Capacitor.Plugins.CoachWorkoutKit.isAvailable()
  Capacitor.Plugins.CoachWorkoutKit.sendInterval({
    activity, displayName, warmupMin, work, rest, repeats, cooldownMin
  })

Activity supportées : running / cycling / walking / hiking
HR zones (1-5) optionnelles via HeartRateZoneAlert sur work + rest steps.

Workflow attendu user :
1. coach.hypnotruck.ch → /settings ou bouton 'Envoyer Apple Watch' depuis /calendar
2. JS appelle WK.sendInterval(...)
3. Plugin Swift construit CustomWorkout + IntervalBlock + alerts
4. WorkoutScheduler.shared.preview(plan) ouvre la sheet iOS native
5. User tape 'Ajouter aux workouts' → workout dispo dans Apple Watch
   → Exercice → Course → Workouts personnalisés → [displayName]

Build à faire sur le Mac (cap sync ios + Xcode build → install iPhone).

Phase 1 = MVP plugin + bouton test sur /settings de coach.hypnotruck.ch.
Phase 2 = bouton 'Envoyer Apple Watch' sur card du jour /calendar (convertit
la séance prévue en CustomWorkout).
Phase 3 = bibliothèque templates intervalles (10x400m, fartlek, etc.).

Project.pbxproj : ajouté 4 entrées (PBXBuildFile + PBXFileReference + group +
Sources phase) avec IDs D1BD5B1990CF3F2B9C5D000{1,2}.
2026-05-08 09:28:15 +00:00
Sylvain Bettinelli
04581915ac Generate Android icons + splash variants depuis resources/
@capacitor/assets v3.0.5 → 87 assets Android générés à partir des sources
1024x1024 (icon) et 2732x2732 (splash) déjà présentes :
- mipmap-{m,h,xh,xxh,xxxh}dpi : ic_launcher.png + _round + _foreground
  (foreground/background layers pour adaptive icons Android 8+)
- drawable-{land,port}-{l,m,h,xh,xxh,xxxh}dpi : splash.png
- mipmap-anydpi-v26 : ic_launcher.xml et ic_launcher_round.xml
  (XML adaptive icon refs vers les layers PNG)

iOS pbxproj : strip leading zero `0920` → `920` (effet de bord du tool, pas
de comportement Xcode modifié — juste format normalisé).

Reste assets marketing pour Play Console (Feature Graphic 1024x500,
screenshots phone) — à générer au moment du upload, pas dans le repo.
2026-05-07 16:30:35 +00:00
Sylvain Bettinelli
a7809992a9 Enregistre CoachAuth.swift dans project.pbxproj
Évite l'étape manuelle Add Files dans Xcode. Le fichier sera
automatiquement compilé dès qu'il existe dans ios/App/App/CoachAuth.swift.
2026-05-05 19:33:30 +00:00
Sylvain
6e3f74209b Init iOS project (cap add ios) avec privacy keys et entitlements 2026-05-05 21:29:17 +02:00