Jauges d'hydratation et de calories dans le widget

Deux barres sous le titre : eau bue sur objectif, énergie consommée sur objectif
calorique. La barre des calories vire à l'orange au-dessus de la cible.

La jauge n'apparaît QUE si l'objectif correspondant existe. Aucun objectif
d'hydratation n'est défini dans les objectifs nutritionnels : tant qu'il vaut
nil, le widget affiche le volume bu sans barre. Une jauge sur un objectif
inventé donnerait un pourcentage qui ne veut rien dire — et il aurait l'air
d'un réglage validé.

Les trois champs du snapshot sont optionnels : un instantané écrit par une
version antérieure reste lisible.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Sylvain Bettinelli
2026-08-06 13:44:40 +00:00
parent e4d317f781
commit 5eafa14b99
2 changed files with 61 additions and 5 deletions

View File

@@ -60,11 +60,17 @@ struct CoachQuickEntryTimeline: TimelineEntry {
let waterMl: Double
let coffeeCount: Int
let pending: Int
let kcal: Double?
let kcalGoal: Double?
let waterGoal: Double?
}
struct CoachQuickProvider: TimelineProvider {
func placeholder(in context: Context) -> CoachQuickEntryTimeline {
CoachQuickEntryTimeline(date: Date(), waterMl: 1250, coffeeCount: 2, pending: 0)
CoachQuickEntryTimeline(
date: Date(), waterMl: 1250, coffeeCount: 2, pending: 0,
kcal: 1420, kcalGoal: 1950, waterGoal: 2000
)
}
func getSnapshot(in context: Context, completion: @escaping (CoachQuickEntryTimeline) -> Void) {
@@ -91,7 +97,10 @@ struct CoachQuickProvider: TimelineProvider {
date: Date(),
waterMl: CoachQuickLog.todayTotal(kind: "water", syncedMl: syncedWater),
coffeeCount: CoachQuickLog.todayCount(kind: "coffee", syncedCount: syncedCoffee),
pending: CoachQuickLog.pending().count
pending: CoachQuickLog.pending().count,
kcal: snapshot?.kcalToday,
kcalGoal: snapshot?.kcalGoal,
waterGoal: snapshot?.waterGoalMl
)
}
}
@@ -117,10 +126,44 @@ struct CoachQuickWidgetView: View {
.lineLimit(1)
.minimumScaleFactor(0.8)
// Deux jauges : hydratation et énergie. La barre n'apparaît que si
// un objectif existe une jauge sur un objectif inventé donnerait
// un pourcentage qui ne veut rien dire.
VStack(alignment: .leading, spacing: 3) {
HStack(spacing: 6) {
Text("\(litres) L")
.font(.system(.subheadline, design: .rounded).weight(.semibold))
.monospacedDigit()
if let goal = entry.waterGoal, goal > 0 {
ProgressView(value: min(entry.waterMl / goal, 1))
.tint(.blue)
Text("\(Int(goal / 1000)) L")
.font(.caption2).foregroundStyle(.tertiary).monospacedDigit()
} else {
Text("bue aujourd'hui")
.font(.caption2).foregroundStyle(.secondary)
Spacer()
}
}
if let kcal = entry.kcal {
HStack(spacing: 6) {
Text("\(Int(kcal)) kcal")
.font(.system(.subheadline, design: .rounded).weight(.semibold))
.monospacedDigit()
if let goal = entry.kcalGoal, goal > 0 {
ProgressView(value: min(kcal / goal, 1))
.tint(kcal > goal ? .orange : .green)
Text("\(Int(goal))")
.font(.caption2).foregroundStyle(.tertiary).monospacedDigit()
} else {
Spacer()
}
}
}
}
HStack(spacing: 6) {
Text("\(litres) L")
.font(.system(.title3, design: .rounded).weight(.semibold))
.monospacedDigit()
Text("· \(entry.coffeeCount) café\(entry.coffeeCount > 1 ? "s" : "")")
.font(.caption)
.foregroundStyle(.secondary)