Consulter la bibliotheque hors-ligne depuis un cache IndexedDB

Le piege que CLAUDE.md signale : en Blazor WebAssembly le code tourne dans le
navigateur alors que SQLite vit sur le serveur, et le service worker ne met en
cache que les assets. Sans travail explicite, l'application demarre hors-ligne
et affiche une bibliotheque vide.

- js/cache-hors-ligne.js : instantanes JSON dans IndexedDB, et surveillance des
  bascules online/offline. Aucune logique metier.
- CacheHorsLigne / EtatReseau : lecture-ecriture des instantanes, et etat reseau
  combinant navigator.onLine (fiable seulement par sa negation) avec le sort
  reel des appels HTTP.
- ServiceLivresApi : les lectures retombent sur le cache, les ecritures sont
  refusees. Le catalogue entier est memorise, pas les reponses filtrees : c'est
  ce qui rend la recherche hors-ligne possible sur tout le fonds.
- FiltreLivresLocal : pendant navigateur de FiltreLivres, avec un test qui
  confronte les deux implementations sur les memes donnees.
- Interface : pastille et bandeau d'etat avec la date de synchronisation, et
  actions d'ecriture desactivees avec leur raison plutot que boutons morts.
- js/mise-a-jour.js : les empreintes WASM etant desactivees, le service worker
  est le seul cache-busting du projet. L'enregistrement journalise desormais ses
  echecs, et un bandeau propose la nouvelle version sans attendre la fermeture
  de tous les onglets.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
mathieu
2026-08-18 13:59:19 +02:00
co-authored by Claude Opus 5
parent 3a5a3a7763
commit ebc5f95d49
21 changed files with 1420 additions and 82 deletions
+29 -2
View File
@@ -1,5 +1,6 @@
@page "/"
@inject ServiceLivresApi Api
@inject EtatReseau Reseau
@implements IDisposable
<PageTitle>MaBibli — catalogue</PageTitle>
@@ -121,9 +122,22 @@ else if (_livres is not null)
</ul>
}
@* Hors-ligne, les deux entrées d'ajout deviennent des boutons éteints plutôt que des liens qui
mèneraient à un formulaire invalidable. La consultation, elle, reste entière : recherche,
filtres et tri portent sur toute la bibliothèque, depuis l'instantané local. *@
<nav class="actions-flottantes">
<a class="bouton bouton-principal" href="ajout/isbn">Ajouter par ISBN</a>
<a class="bouton bouton-discret" href="ajout/manuel">Saisie manuelle</a>
@if (Reseau.EnLigne)
{
<a class="bouton bouton-principal" href="ajout/isbn">Ajouter par ISBN</a>
<a class="bouton bouton-discret" href="ajout/manuel">Saisie manuelle</a>
}
else
{
<button type="button" class="bouton bouton-principal" disabled
title="@EtatReseau.MotifHorsLigne">Ajouter par ISBN</button>
<button type="button" class="bouton bouton-discret" disabled
title="@EtatReseau.MotifHorsLigne">Saisie manuelle</button>
}
<a class="bouton bouton-discret" href="prets">Prêts</a>
<a class="bouton bouton-discret" href="auteurs">Auteurs</a>
</nav>
@@ -149,6 +163,18 @@ else if (_livres is not null)
!string.IsNullOrWhiteSpace(_recherche) || _format is not null || _statut is not null
|| AuteurId is not null;
/// <summary>
/// La liste se relit à chaque bascule du réseau : au retour, pour reprendre les données du
/// serveur ; à la coupure, pour basculer sur l'instantané local sans laisser un écran vide.
/// </summary>
protected override void OnInitialized() => Reseau.Change += SurChangementReseau;
private void SurChangementReseau() => _ = InvokeAsync(async () =>
{
await ChargerAsync();
StateHasChanged();
});
protected override async Task OnParametersSetAsync()
{
if (_auteurCharge != AuteurId)
@@ -219,6 +245,7 @@ else if (_livres is not null)
public void Dispose()
{
Reseau.Change -= SurChangementReseau;
_cts.Cancel();
_cts.Dispose();
}