fix(widget): le total retombait à zéro après synchronisation
Le widget affiche « snapshot + file d'attente ». La synchronisation vidait la file sans créditer le snapshot : le total repassait donc à zéro juste après que la saisie ait été enregistrée pour de bon. Le pire moment pour douter de son journal. `flush()` reporte désormais dans le snapshot ce qu'il vient d'envoyer, AVANT de vider la file. Seules les entrées du jour sont créditées — le snapshot porte des totaux quotidiens, y verser une saisie d'hier les fausserait. Le pont web repoussera un snapshot exact au prochain chargement de page ; ce report garde l'affichage juste entre-temps. Les optionnels du snapshot reçoivent `= nil` explicitement : sans valeur par défaut, l'init memberwise de Swift les aurait exigés à l'appel. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -54,14 +54,39 @@ enum CoachQuickSync {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !sent.isEmpty {
|
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)
|
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()
|
WidgetCenter.shared.reloadAllTimelines()
|
||||||
}
|
}
|
||||||
return sent.count
|
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 {
|
private static func post(_ entry: CoachQuickEntry) async -> Bool {
|
||||||
var request = URLRequest(url: base.appendingPathComponent("api/drinks"))
|
var request = URLRequest(url: base.appendingPathComponent("api/drinks"))
|
||||||
request.httpMethod = "POST"
|
request.httpMethod = "POST"
|
||||||
|
|||||||
@@ -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.
|
/// le widget n'a pas d'accès réseau, tout vient de ce snapshot poussé par l'app.
|
||||||
struct CoachWidgetSnapshot: Codable {
|
struct CoachWidgetSnapshot: Codable {
|
||||||
var updatedAt: Date
|
var updatedAt: Date
|
||||||
var today: TodaySession?
|
var today: TodaySession? = nil
|
||||||
var forme: FormeScore?
|
var forme: FormeScore? = nil
|
||||||
|
|
||||||
/// Boissons du jour DÉJÀ connues du serveur, en millilitres et en nombre de
|
/// 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
|
/// 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
|
/// « 1,5 L » tant que l'app n'a pas resynchronisé, et l'utilisateur
|
||||||
/// douterait de saisies pourtant enregistrées.
|
/// douterait de saisies pourtant enregistrées.
|
||||||
/// Optionnels : un snapshot écrit par une version antérieure reste lisible.
|
/// Optionnels : un snapshot écrit par une version antérieure reste lisible.
|
||||||
var waterMlToday: Double?
|
var waterMlToday: Double? = nil
|
||||||
var coffeeCountToday: Int?
|
var coffeeCountToday: Int? = nil
|
||||||
|
|
||||||
/// Énergie du jour et objectif, poussés par l'app depuis le journal.
|
/// É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) :
|
/// `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.
|
/// il n'est pas écrit en dur ici, pour qu'un changement d'objectif suive.
|
||||||
var kcalToday: Double?
|
var kcalToday: Double? = nil
|
||||||
var kcalGoal: Double?
|
var kcalGoal: Double? = nil
|
||||||
|
|
||||||
|
|
||||||
/// Séance planifiée du jour (nil = jour OFF / pas de séance).
|
/// Séance planifiée du jour (nil = jour OFF / pas de séance).
|
||||||
|
|||||||
Reference in New Issue
Block a user