Trois widgets d'ecran verrouille et une complication Routine

Les familles accessory* n'etaient declarees que cote Watch, alors que ce sont
exactement celles de l'ecran verrouille iOS 16+. Les vues existaient donc
deja : il ne manquait que la declaration cote iPhone.

Ajoutes sur l'ecran verrouille : Forme (circulaire/rectangulaire/inline),
Seance (rectangulaire/inline, avec la bascule doneToday du commit precedent)
et Routine du jour. Sur la Watch : complication Routine, et la seance gagne la
famille circulaire (icone du sport, verte quand elle est faite).

Le snapshot gagne RoutineProgress {done, total} au grain EXERCICE — celui de
la page web et de l'app Watch depuis le 03/08. `fraction` borne le ratio et
protege de la division par zero ; un total nul ne doit pas remplir la jauge.
Meme garde du jour que doneToday : la routine repart a zero au reveil, un
report aveugle afficherait 24/24 des le matin.

Vues dupliquees plutot que partagees entre les deux extensions : elles ne
partagent aucun fichier et l'App Group n'est pas commun entre appareils. Trois
petites vues coutent moins qu'un fichier de plus a cabler dans deux cibles —
et pour la meme raison tout est ajoute a CoachWidgets.swift, deja membre de sa
cible : AUCUN fichier nouveau, donc rien a cabler dans le pbxproj.

⚠️ NON COMPILE — a builder au prochain passage sur le Mac, avec le correctif
« JOUR OFF » (12b56a4).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sylvain Bettinelli
2026-08-11 18:08:13 +00:00
parent 12b56a4d39
commit 3202e03a63
5 changed files with 302 additions and 2 deletions

View File

@@ -146,7 +146,16 @@ struct CoachWatchSessionView: View {
let today = entry.snapshot?.today
Group {
if let s = today {
if family == .accessoryInline {
if family == .accessoryCircular {
// Le cadran ne tient pas un titre : l'icône du sport porte
// l'information, la pastille verte dit qu'elle est faite.
ZStack {
AccessoryWidgetBackground()
Image(systemName: watchSportSymbol(s.sport))
.font(.title3)
.foregroundStyle(s.done ? .green : .primary)
}
} else if family == .accessoryInline {
Label(s.title, systemImage: watchSportSymbol(s.sport))
} else { // accessoryRectangular
VStack(alignment: .leading, spacing: 2) {
@@ -206,7 +215,59 @@ struct CoachWatchSessionWidget: Widget {
}
.configurationDisplayName("Séance du jour")
.description("La séance prévue aujourd'hui.")
.supportedFamilies([.accessoryRectangular, .accessoryInline])
.supportedFamilies([.accessoryRectangular, .accessoryInline, .accessoryCircular])
}
}
// MARK: - Complication « Routine du jour » (circular / rectangular / inline)
/// Avancement au grain EXERCICE (14/24) le même que la page web et l'app
/// Watch depuis le 2026-08-03. Un grain « bloc » donnerait un second compte
/// du même geste, et les deux finiraient par diverger.
struct CoachWatchRoutineView: View {
@Environment(\.widgetFamily) private var family
var entry: WatchEntry
var body: some View {
let r = entry.snapshot?.routine
let text = r.map { "\($0.done)/\($0.total)" } ?? ""
Group {
switch family {
case .accessoryInline:
Label("Routine \(text)", systemImage: "figure.cooldown")
case .accessoryRectangular:
VStack(alignment: .leading, spacing: 2) {
Label("ROUTINE", systemImage: "figure.cooldown")
.font(.caption2.weight(.bold)).foregroundStyle(.secondary)
Text(text).font(.headline)
if let r, r.total > 0 {
ProgressView(value: r.fraction).progressViewStyle(.linear)
}
}
default: // accessoryCircular
Gauge(value: r?.fraction ?? 0, in: 0...1) {
Image(systemName: "figure.cooldown")
} currentValueLabel: {
Text(r.map { String($0.done) } ?? "")
}
.gaugeStyle(.accessoryCircular)
.tint(.mint)
}
}
.watchWidgetBackground()
.widgetURL(URL(string: "https://coach.hypnotruck.ch/routine"))
}
}
struct CoachWatchRoutineWidget: Widget {
let kind = "CoachWatchRoutineWidget"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: WatchProvider()) { entry in
CoachWatchRoutineView(entry: entry)
}
.configurationDisplayName("Routine")
.description("Exercices de la routine cochés aujourd'hui.")
.supportedFamilies([.accessoryCircular, .accessoryRectangular, .accessoryInline])
}
}
@@ -217,5 +278,6 @@ struct CoachWatchWidgetsBundle: WidgetBundle {
var body: some Widget {
CoachWatchFormeWidget()
CoachWatchSessionWidget()
CoachWatchRoutineWidget()
}
}