feat(watch): séance guidée au poignet

Pendant natif du lecteur web. Bouton Démarrer par bloc : un exercice à la fois,
décompte en anneau, passage auto, haptique à chaque transition, pause et
navigation. Pendant une routine au sol, la montre est bien plus pratique que
d'aller chercher le téléphone — c'est tout l'intérêt de la version watchOS.

Chaque exercice écoulé est coché via RoutineStore.toggle, donc renvoyé à
l'iPhone puis au serveur : à la fin le bloc est à jour sur les trois surfaces.

La durée par exercice est désormais transmise dans le snapshot (duration_sec,
optionnel pour rester compatible avec les snapshots déjà persistés) : la vue
guidée utilise la vraie durée au lieu de répartir celle du bloc.

RoutineGuidedView.swift est déjà référencé dans le pbxproj (target CoachWatch),
aucun Add Files à faire. Backup : project.pbxproj.bak-guided

NON COMPILÉ — à inclure au prochain build Xcode.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Sylvain Bettinelli
2026-08-03 16:32:29 +00:00
parent 21d819a52b
commit f032ee2126
9 changed files with 2749 additions and 2 deletions

View File

@@ -6,6 +6,7 @@ import WatchKit
/// l'état est donc partagé avec la page /routine et le widget de la home.
struct RoutineView: View {
@StateObject private var store = RoutineStore.shared
@State private var guided: RoutineBlock?
var body: some View {
Group {
@@ -16,6 +17,9 @@ struct RoutineView: View {
}
}
.navigationTitle("Routine")
.sheet(item: $guided) { block in
RoutineGuidedView(block: block)
}
}
private var emptyState: some View {
@@ -79,7 +83,8 @@ struct RoutineView: View {
.padding(.vertical, 4)
}
/// En-tête de bloc (tap = tout cocher d'un coup) puis ses exercices.
/// En-tête de bloc (tap = tout cocher d'un coup), bouton de séance guidée,
/// puis ses exercices.
private func blockSection(_ block: RoutineBlock) -> some View {
let complete = store.isComplete(block)
return VStack(spacing: 4) {
@@ -103,6 +108,15 @@ struct RoutineView: View {
.buttonStyle(.plain)
.padding(.top, 6)
Button {
guided = block
} label: {
Label("Démarrer", systemImage: "play.fill")
.font(.caption2)
.frame(maxWidth: .infinity)
}
.tint(.cyan)
ForEach(block.items) { item in
exerciseRow(item)
}