Le widget ne passait pas minuit : personne ne lisait updatedAt
Signale par Sylvain le 2026-08-18 : passe minuit, l'hydratation, les calories et la seance de la veille restaient affichees comme celles du jour. Il fallait ouvrir l'app pour les voir retomber a zero. Le widget n'a AUCUN acces reseau : il rend ce que l'app lui a pousse la derniere fois qu'elle a tourne. Le snapshot portait deja `updatedAt` — rien ne le lisait. CoachQuickWidget programmait meme un reveil a minuit, avec le bon commentaire, mais relisait ensuite les memes chiffres sans regarder leur date : le rechargement avait lieu, la remise a zero non. - `CoachWidgetSnapshot.asOf(_:)` : vide ce qui decrit UNE journee (eau, cafes, kcal, routine, activite faite, score de forme) quand le snapshot date d'un autre jour. Conserve ce qui traverse les jours — objectifs et zones cardiaques. Les compteurs repartent a 0 et non a nil : au matin, « rien bu » est vrai, « je ne sais pas » ne l'est pas. - Les trois providers emettent une 2e entree datee de minuit. WidgetKit bascule seul, sans reveiller l'app. - `tomorrow` : la seance du lendemain, poussee par le pont web. Sans elle, vider la seance ferait annoncer JOUR OFF chaque nuit — l'agacement d'un incident precedent, repete tous les jours. ⚠️ Deuxieme occurrence trouvee en verifiant, et pire : CoachQuickSync.credit() ajoutait au total EXISTANT puis rehorodatait a maintenant. Sur un snapshot de la veille, 200 ml bus le matin donnaient « 2 200 ml » estampilles du jour — un total faux, que la peremption ne pouvait plus rattraper. ⚠️ NON COMPILE : pas de toolchain Swift ici. Build Xcode requis sur le Mac mini pour que le correctif arrive sur l'iPhone et la Watch.
This commit is contained in:
@@ -77,27 +77,37 @@ struct CoachQuickProvider: TimelineProvider {
|
||||
completion(current())
|
||||
}
|
||||
|
||||
/// ⚠️ **Le rendez-vous de minuit existait déjà — et ne servait à rien.**
|
||||
///
|
||||
/// La version précédente programmait bien `.after(midnight)`, avec le bon
|
||||
/// commentaire (« pour que les totaux du jour repartent de zéro »). Mais au
|
||||
/// réveil, `current()` relisait `snapshot.waterMlToday` **sans regarder de
|
||||
/// quand datait ce snapshot** : elle réaffichait donc exactement les mêmes
|
||||
/// chiffres. Le rechargement avait lieu, la remise à zéro non.
|
||||
///
|
||||
/// C'est `asOf(_:)` qui manquait, pas le réveil.
|
||||
func getTimeline(in context: Context, completion: @escaping (Timeline<CoachQuickEntryTimeline>) -> Void) {
|
||||
// Une seule entrée, rechargée à chaque saisie par `reloadAllTimelines`.
|
||||
// Rafraîchissement de sécurité au prochain minuit, pour que les totaux
|
||||
// du jour repartent de zéro même sans interaction.
|
||||
let midnight = Calendar.current.nextDate(
|
||||
after: Date(), matching: DateComponents(hour: 0, minute: 0), matchingPolicy: .nextTime
|
||||
) ?? Date().addingTimeInterval(3600)
|
||||
completion(Timeline(entries: [current()], policy: .after(midnight)))
|
||||
let now = Date()
|
||||
let midnight = CoachWidgetSnapshot.nextMidnight(after: now)
|
||||
completion(Timeline(entries: [current(at: now), current(at: midnight)],
|
||||
policy: .after(midnight.addingTimeInterval(60))))
|
||||
}
|
||||
|
||||
private func current() -> CoachQuickEntryTimeline {
|
||||
let snapshot = CoachWidgetStore.load()
|
||||
private func current(at day: Date = Date()) -> CoachQuickEntryTimeline {
|
||||
// `asOf` vide les totaux du jour quand le snapshot date d'un autre jour.
|
||||
let snapshot = CoachWidgetStore.load()?.asOf(day)
|
||||
// Le serveur connaît peut-être déjà des boissons du jour : le snapshot
|
||||
// les porte. On y ajoute ce qui attend dans la file.
|
||||
let syncedWater = snapshot?.waterMlToday ?? 0
|
||||
let syncedCoffee = snapshot?.coffeeCountToday ?? 0
|
||||
return CoachQuickEntryTimeline(
|
||||
date: Date(),
|
||||
waterMl: CoachQuickLog.todayTotal(kind: "water", syncedMl: syncedWater),
|
||||
date: day,
|
||||
waterMl: CoachQuickLog.todayTotal(kind: "water", syncedMl: syncedWater, day: day),
|
||||
waterGoal: snapshot?.waterGoalMl,
|
||||
coffeeCount: CoachQuickLog.todayCount(kind: "coffee", syncedCount: syncedCoffee),
|
||||
coffeeCount: CoachQuickLog.todayCount(kind: "coffee", syncedCount: syncedCoffee, day: day),
|
||||
// ⚠️ `pending` compte ce qui attend d'être envoyé, TOUS jours
|
||||
// confondus : une saisie d'hier non synchronisée doit encore partir,
|
||||
// et le badge doit continuer à le signaler.
|
||||
pending: CoachQuickLog.pending().count,
|
||||
kcal: snapshot?.kcalToday,
|
||||
kcalGoal: snapshot?.kcalGoal
|
||||
|
||||
Reference in New Issue
Block a user