Skip to content

Recap hebdomadaire : prévenir le parent que la progression suivie a bougé - #73

Open
isc wants to merge 1 commit into
mainfrom
feat/recap-hebdo
Open

Recap hebdomadaire : prévenir le parent que la progression suivie a bougé#73
isc wants to merge 1 commit into
mainfrom
feat/recap-hebdo

Conversation

@isc

@isc isc commented Jul 26, 2026

Copy link
Copy Markdown
Owner

Suite de #72. Le miroir chiffré permet au parent de consulter la progression de son enfant — encore faut-il qu'il y pense. Ce toggle facultatif envoie une notification le dimanche soir, au plus une par semaine.

⚠ Ordre de déploiement

La DDL doit être appliquée AVANT que le client ne soit déployé.

psql "$SUPABASE_DB_URL" -f supabase/push_subscriptions.sql

Le sens inverse casse l'existant : un nouveau client face à une base sans upsert_push_prefs / read_push_prefs reçoit des 404, affiche donc ses deux toggles à OFF alors que le rappel quotidien tourne, et la moindre action de l'utilisateur pour « le remettre » échoue. Dans le bon sens, les clients encore en cache continuent d'appeler upsert_push_subscription, conservée intacte, et rien ne change pour eux.

Le contenu de la notification

Aucun chiffre, et c'est le point. L'instantané suivi est chiffré de bout en bout : le serveur qui envoie la notification ne connaît ni le prénom de l'enfant ni sa progression. Le message annonce qu'un recap est prêt ; le clic ouvre l'espace parent, qui déchiffre localement et affiche le contenu réel.

C'est ce qui permet d'avoir un signal poussé sans rien concéder sur la confidentialité — là où un e-mail aurait imposé de composer le contenu côté serveur, donc de l'y exposer en clair.

Deux notifications, deux personnes

Le rappel quotidien pousse l'enfant à faire sa séance, sur son appareil. Le recap prévient le parent, sur le sien. Vouloir l'un sans l'autre est le cas normal, d'où deux préférences indépendantes portées par la même souscription push. Quand les deux tombent le même soir, le recap prime : un appareil ne reçoit jamais deux notifications dans la même soirée.

Points de conception

  • upsert_push_prefs porte un nom distinct de upsert_push_subscription plutôt que d'en élargir la signature. Ajouter des paramètres en aurait fait une surcharge Postgres : les appels à 4 arguments des clients encore en cache seraient devenus ambigus (function is not unique). Et supprimer l'ancienne aurait été pire — un client périmé aurait créé sa ligne avec le rappel quotidien à false, silencieusement.
  • Mise à jour partielle (coalesce) : les deux toggles vivent dans des composants différents, un read-modify-write côté client les ferait s'écraser.
  • Le RPC renvoie l'état résultant. La règle « plus rien d'actif → on désabonne » découle ainsi d'une valeur lue dans la même transaction, et non d'une relecture qui, en échouant hors-ligne, aurait supprimé un abonnement bien vivant.
  • Miroir localStorage des préférences : l'affichage des toggles ne doit pas dépendre du réseau. Une PWA s'ouvre souvent hors-ligne, et afficher OFF pendant que les notifications arrivent est un mensonge — pire, ça rend le toggle inopérant pour désactiver.
  • Rattrapage le lundi : les workflows planifiés GitHub sont régulièrement décalés, parfois sautés. Sans filet, une soirée manquée perdait le recap de la semaine entière (le rappel quotidien, lui, revient le lendemain).

Corrections dans le Service Worker

  • un tag par type — le recap effaçait sinon un rappel de séance non lu, et inversement ;
  • navigation avant focus, avec catchnotificationclick faisait c.focus() sans naviguer, donc une app déjà ouverte ignorait le fragment et déposait le parent sur l'écran précédent. La navigation est réservée au recap (rediriger un enfant en pleine séance lui ferait perdre sa séance) et le catch est indispensable : navigate() rejette sur un client non contrôlé, et matchAll en inclut volontairement.

Vérifications

  • 287 tests (21 sur la seule fonction de décision du cron : fuseaux, fenêtre horaire, dédoublonnage, rattrapage, rétrocompatibilité des lignes écrites avant les colonnes)
  • le cron lancé avant la DDL est strictement iso-comportement : select=* ne 400 pas, daily_reminder absent ne vaut pas false, la branche weekly est inatteignable — verrouillé par un test
  • tsc -b, lint et build prod OK

🤖 Generated with Claude Code

Un miroir consultable ne sert que si le parent pense à le regarder. Un toggle
facultatif, dans la section « Suivi à distance », envoie une notification le
dimanche soir sur l'appareil du parent — au plus une par semaine, avec
rattrapage le lundi si le cron a sauté la soirée.

Le message ne contient AUCUN chiffre : l'instantané suivi étant chiffré de bout
en bout, le serveur qui envoie la notification ne connaît ni le prénom de
l'enfant ni sa progression. Le clic ouvre l'espace parent, qui déchiffre
localement. C'est ce qui permet un signal poussé sans rien concéder sur la
confidentialité, là où un e-mail aurait imposé de composer le contenu côté
serveur, donc de l'y exposer en clair.

Le rappel quotidien et le recap ne s'adressent pas à la même personne (l'enfant
sur son appareil, le parent sur le sien) : ils deviennent deux préférences
indépendantes portées par la même souscription push, avec mise à jour partielle
côté SQL pour que les deux toggles ne s'écrasent pas.

- `upsert_push_prefs` porte un NOM DISTINCT de `upsert_push_subscription`
  (conservée intacte) : élargir la signature en aurait fait une surcharge
  Postgres, rendant ambigus les appels des clients encore en cache
- le RPC renvoie l'état résultant, pour que « plus rien d'actif → on désabonne »
  découle d'une valeur lue et jamais d'une lecture qui a échoué
- miroir localStorage des préférences : l'affichage des toggles ne doit pas
  dépendre du réseau (une PWA s'ouvre souvent hors-ligne)
- sw.js : un tag par type de notification, et navigation avant focus pour le
  seul recap — avec catch, navigate() rejetant sur un client non contrôlé
- cesser de suivre le dernier enfant éteint le recap, dont le toggle disparaît
@github-actions

Copy link
Copy Markdown
Contributor

Preview déployée pour cette PR :

Commit : 8dad1817431fe12d9a4f850489121bf681da7f1b
Le preview est mis à jour à chaque push et supprimé à la fermeture de la PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant