Aller au contenu principal

PWA offline — reports « ne pas changer le produit »

Backlog à implémenter lors du chantier Context Engine. Ce document liste les améliorations offline volontairement reportées pendant la mise en place de la PWA (SW v5, août 2026), pour respecter la règle contractuelle de préservation de l'expérience utilisateur : un correctif offline ne doit jamais modifier le comportement produit en ligne.

Voir aussi : 14-pwa.md, 15-offline-strategy.md, 34-ranking-engine-synthesis.md.

Contexte

La PWA offline repose sur un service worker unique (dmv-public/public/sw.js, scope /, couvre aussi le workspace) qui met en cache : shell HTML, assets Next, assets public/ (dont logo.svg), images Supabase Storage, lectures API Laravel, et toutes les requêtes /rest/v1/publications sous une clé normalisée (retrait des filtres publie_le et or basés sur Date.now()).

Principe retenu (décision produit) : offline = ce qui a été consulté en ligne. Le SW cache chaque couche au fil de la navigation ; il ne préfetch rien de plus. Les points ci-dessous amélioreraient l'offline mais changent le comportement du mur (requêtes supplémentaires, persistance de feed, timing) → reportés.

Reports (à traiter avec le Context Engine)

1. Feed classé complet offline

  • Symptôme : offline, le mur ne montre que les publications de la fenêtre récente, par date et non selon le classement.
  • Cause : rankedPubs (dmv-public/app/components/wall/components/WallLayout.tsx) se calcule côté client en fusionnant fenêtre récente + archive (chargée au scroll) + featured + configs de scoring. Offline, seules les couches déjà chargées existent.
  • Action différée : persister le feed final classé (liste ordonnée d'IDs + données) en localStorage et le réhydrater offline, indépendamment du scroll.
  • Risque produit : touche le cœur du mur (WallContext/WallLayout) ; le classement est temporel → un feed offline sera toujours une photo du dernier passage. À recetter.

2. Préchargement des filtres actu / evenement

  • Symptôme : offline, cliquer un filtre (Actualités, Événements) ne renvoie rien s'il n'a pas été activé en ligne au préalable.
  • Cause : ces filtres ne sont pas du filtrage client — chaque filtre déclenche sa propre requête Supabase, et seulement au clic (dmv-public/app/components/wall/hooks/useWallTargetedPublications.ts, if (!communeId || pubFilter !== "actu") return;). Le SW les cache, mais seulement une fois fetchées.
  • Action différée : précharger actu et evenement au chargement du mur (déclencher les fetch même sans filtre actif) pour qu'ils soient en cache dès la première visite en ligne.
  • Risque produit : 2 requêtes de plus au chargement ; comportement visible en ligne inchangé, mais à valider (perf, quota).

3. Filtre suivis / Favoris offline

  • Cause : dépend de l'authentification (followedIds) et de requêtes personnelles ; pas de stratégie de cache offline dédiée.
  • Action différée : définir une stratégie de cache offline des favoris (localStorage des IDs suivis + publications associées), cohérente avec l'auth.

4. Fenêtre de cache des publications

  • Cause : PUBLICATION_CACHE_WINDOW_MS = 3 jours (dmv-public/lib/publicationCache.ts) → usePublications ne conserve que 3 jours en localStorage.
  • Action différée : allonger et/ou rendre configurable cette fenêtre (impact taille localStorage à mesurer). Reste « par date », à combiner avec le point 1 pour un vrai feed classé.

5. Cache uniforme de la couche archive

  • Cause : useArchivePubs / useRelationalArchivePubs / usePersonalEvents chargent au scroll et n'ont pas de cache localStorage propre (le SW les cache si elles ont été chargées en ligne).
  • Action différée : cache localStorage applicatif uniforme pour ces hooks, ou s'appuyer entièrement sur le SW + préchargement (cf. point 1).

6. Auto-reload du SW au premier chargement

  • Cause : dmv-public/app/components/pwa/ServiceWorkerManager.tsx recharge la page sur controllerchange (quand le SW prend le contrôle au 1er passage).
  • Action différée : limiter le rechargement aux vraies mises à jour de SW, pas à la prise de contrôle initiale, pour supprimer le reload initial.
  • Risque produit : un léger flash au 1er chargement ; sans impact fonctionnel.

7. Purge du cache au logout côté dmv-public (Mon Espace)

  • Cause : seul le workspace purge les caches dmv-data-* au logout (dmv-workspace/contexts/auth-context.tsx). Mon Espace (dmv-public) ne le fait pas.
  • Action différée : appliquer la même purge (CLEAR_DATA_CACHE + suppression directe des caches dmv-data-*) à la déconnexion de Mon Espace, pour éviter qu'un compte laisse des données lisibles au suivant sur appareil partagé.

Dette technique liée (hors « produit »)

Non lié à la préservation produit, mais à traiter au même moment : des erreurs de lint pré-existantes (React Compiler / eslint 10) ont été neutralisées par des eslint-disable-next-line ciblés (commentés « Dette (pré-PWA) ») pour débloquer la CI, sans rien refactorer :

  • WallLayout.tsxreact-hooks/preserve-manual-memoization (useCallback/useMemo à dépendances membres) ;
  • WallPrimaryControlsContainer.tsxreact-hooks/set-state-in-effect (reset d'état dérivé dans un effet) ;
  • WallContext.tsxreact-hooks/purity (Date.now() pendant le rendu).

Ces trois points méritent un vrai refactor lors du chantier Context Engine (ils concernent exactement les composants du mur).