diff --git a/ios/App/App/CoachQuickSync.swift b/ios/App/App/CoachQuickSync.swift index 4a31a62..e6a3cc3 100644 --- a/ios/App/App/CoachQuickSync.swift +++ b/ios/App/App/CoachQuickSync.swift @@ -54,14 +54,39 @@ enum CoachQuickSync { } if !sent.isEmpty { + /* Le snapshot AVANT de vider la file. Le widget affiche + « snapshot + file » : vider la file sans créditer le snapshot + ferait retomber son total à zéro alors que la saisie vient + justement d'être enregistrée. C'est exactement ce qui se passait. + + Le pont web repoussera un snapshot exact au prochain chargement + de page ; en attendant, ce report garde l'affichage juste. */ + credit(queue.filter { sent.contains($0.id) }) CoachQuickLog.remove(ids: sent) - // Le widget doit cesser d'afficher la flèche d'attente, et son - // total doit repasser sur la source serveur. WidgetCenter.shared.reloadAllTimelines() } return sent.count } + /// Reporte dans le snapshot ce qui vient de partir au serveur. + /// + /// Seules les entrées du JOUR comptent : le snapshot porte des totaux + /// quotidiens, et y verser une saisie d'hier les fausserait. + private static func credit(_ entries: [CoachQuickEntry]) { + let calendar = Calendar.current + let today = entries.filter { calendar.isDateInToday($0.at) } + guard !today.isEmpty else { return } + + var snapshot = CoachWidgetStore.load() + ?? CoachWidgetSnapshot(updatedAt: Date(), today: nil, forme: nil) + let water = today.filter { $0.ref == "water" }.reduce(0) { $0 + $1.amount } + let coffees = today.filter { $0.ref == "coffee" }.count + if water > 0 { snapshot.waterMlToday = (snapshot.waterMlToday ?? 0) + water } + if coffees > 0 { snapshot.coffeeCountToday = (snapshot.coffeeCountToday ?? 0) + coffees } + snapshot.updatedAt = Date() + CoachWidgetStore.save(snapshot) + } + private static func post(_ entry: CoachQuickEntry) async -> Bool { var request = URLRequest(url: base.appendingPathComponent("api/drinks")) request.httpMethod = "POST" diff --git a/ios/App/App/CoachWidgetSnapshot.swift b/ios/App/App/CoachWidgetSnapshot.swift index 4aeb78d..2be05cf 100644 --- a/ios/App/App/CoachWidgetSnapshot.swift +++ b/ios/App/App/CoachWidgetSnapshot.swift @@ -41,8 +41,8 @@ enum CoachWidgetStore { /// le widget n'a pas d'accès réseau, tout vient de ce snapshot poussé par l'app. struct CoachWidgetSnapshot: Codable { var updatedAt: Date - var today: TodaySession? - var forme: FormeScore? + var today: TodaySession? = nil + var forme: FormeScore? = nil /// Boissons du jour DÉJÀ connues du serveur, en millilitres et en nombre de /// cafés. Le widget de saisie rapide y ajoute ce qui attend encore dans la @@ -50,14 +50,14 @@ struct CoachWidgetSnapshot: Codable { /// « 1,5 L » tant que l'app n'a pas resynchronisé, et l'utilisateur /// douterait de saisies pourtant enregistrées. /// Optionnels : un snapshot écrit par une version antérieure reste lisible. - var waterMlToday: Double? - var coffeeCountToday: Int? + var waterMlToday: Double? = nil + var coffeeCountToday: Int? = nil /// Énergie du jour et objectif, poussés par l'app depuis le journal. /// `kcalGoal` vient des objectifs nutritionnels réels (1950 au 06/08/2026) : /// il n'est pas écrit en dur ici, pour qu'un changement d'objectif suive. - var kcalToday: Double? - var kcalGoal: Double? + var kcalToday: Double? = nil + var kcalGoal: Double? = nil /// Séance planifiée du jour (nil = jour OFF / pas de séance).