feat(ui): spinner global près du titre Festipod + log du délai des requêtes

Chaque useShapeQuery s'enregistre dans un store module-level pendingQueries au
début de son cycle et se résout à son premier résultat (isPending→isSuccess|isError,
équivalent readPromise). HomeScreen affiche un Spinner à côté du titre "Festipod"
tant qu'au moins une requête est en attente ; il ne s'arrête que quand TOUTES ont
reçu leur premier résultat. Toute future useShapeQuery y contribue automatiquement.

À la 1re résolution, chaque cycle logge son délai :
  [FestipodData] <shape>/<scope> premier résultat en <N>ms (n=<len>)
→ le délai d'obtention des événements (Event/public) est visible nommément.

Store idempotent (Set d'ids, sûr sous StrictMode) ; cycleId mémoïsé sur
[shapeKey, scope] → re-begin sur switch d'identité, cleanup résout au démontage
(spinner jamais bloqué). Spinner = Loader2 lucide + @keyframes app-spin dans index.css.

Tests: pendingQueries.test.ts (6, dont "off seulement quand toutes résolues").
Doctrine: data-layer/knowledge_context-internals.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sylvain Duchesne
2026-07-13 13:51:36 +02:00
parent c7e924abe7
commit 39b67feea0
8 changed files with 231 additions and 4 deletions
+11
View File
@@ -333,6 +333,17 @@ body {
min-height: 0;
}
/* Global data-query spinner (near the "Festipod" title) */
@keyframes app-spin {
to { transform: rotate(360deg); }
}
.app-spinner {
animation: app-spin 0.8s linear infinite;
flex-shrink: 0;
vertical-align: middle;
}
/* Online indicator on avatar */
.app-avatar .online-dot {
position: absolute;