Sert les couvertures depuis IndexedDB aussi en ligne, et libère les blob:
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
@using MaBibli.Client.Services
|
||||
@inject CacheHorsLigne Cache
|
||||
@inject EtatReseau Reseau
|
||||
@implements IAsyncDisposable
|
||||
@*
|
||||
Couverture d'un livre, avec substitut.
|
||||
|
||||
@@ -20,10 +21,19 @@
|
||||
seulement l'image. Sans ça, cliquer une vignette cassée agrandissait quand même une image
|
||||
cassée (A4, IDEES.md).
|
||||
|
||||
Cache hors-ligne (A5, IDEES.md) : EN LIGNE, l'affichage utilise l'URL réseau telle quelle —
|
||||
rien ne doit ralentir la voie rapide — et une mise en cache démarre en tâche de fond,
|
||||
sans attendre son résultat, pour que la même couverture reste visible hors-ligne. HORS
|
||||
LIGNE, on tente d'abord le cache : l'URL réseau échouerait de toute façon.
|
||||
Cache des couvertures : le cache IndexedDB est consulté D'ABORD, en ligne comme hors ligne.
|
||||
Une image déjà rangée sur l'appareil s'affiche sans aucune requête réseau — c'est ce qui rend
|
||||
un rechargement instantané sur téléphone, là où covers.openlibrary.org impose deux
|
||||
redirections avant d'aboutir sur archive.org. Le réseau n'est sollicité QUE si rien n'est en
|
||||
cache, et la mise en cache démarre alors en tâche de fond.
|
||||
|
||||
⚠️ Ceci REMPLACE la règle d'origine (A5) « en ligne, rien ne doit ralentir la voie rapide »,
|
||||
qui réservait le cache au hors-ligne. La lecture IndexedDB précède désormais l'affichage :
|
||||
elle est locale et sans réseau, donc largement plus rapide que ce qu'elle remplace.
|
||||
|
||||
⚠️ Corollaire : l'URL d'objet doit être LIBÉRÉE quand le composant disparaît ou change
|
||||
d'image (Dispose / changement d'URL). Un blob: retient son Blob en mémoire jusque-là, et il y
|
||||
a maintenant une vignette par carte sur tous les écrans.
|
||||
⚠️ L'allègement (redimensionner, convertir en WebP) n'est PAS fait ici : à mesurer avant
|
||||
de s'y engager, comme pour l'AOT — voir IDEES.md.
|
||||
*@
|
||||
@@ -94,11 +104,11 @@
|
||||
private bool ADesPixels => !string.IsNullOrWhiteSpace(Url);
|
||||
|
||||
/// <summary>
|
||||
/// URL réellement posée en <c>src</c> : l'URL réseau en ligne (voie rapide, inchangée), le
|
||||
/// blob mis en cache hors-ligne quand il existe, sinon l'URL réseau telle quelle (qui échoue
|
||||
/// simplement, comme avant cette fonctionnalité).
|
||||
/// URL réellement posée en <c>src</c> : le blob mis en cache dès qu'il existe — en ligne
|
||||
/// comme hors ligne —, sinon l'URL réseau telle quelle (qui échoue simplement hors-ligne,
|
||||
/// exactement comme avant cette fonctionnalité).
|
||||
/// </summary>
|
||||
private string? UrlAffichee => !Reseau.EnLigne && _urlCachee is not null ? _urlCachee : Url;
|
||||
private string? UrlAffichee => _urlCachee ?? Url;
|
||||
|
||||
private string Initiale =>
|
||||
string.IsNullOrWhiteSpace(Titre) ? "?" : Titre.Trim()[..1].ToUpperInvariant();
|
||||
@@ -131,21 +141,38 @@
|
||||
}
|
||||
|
||||
_urlResoluePour = jeton;
|
||||
_urlCachee = null;
|
||||
await LibererAsync();
|
||||
|
||||
if (Reseau.EnLigne)
|
||||
// Le cache d'abord, EN LIGNE COMME HORS LIGNE : une image déjà sur l'appareil s'affiche
|
||||
// sans requête, sans redirection, et sans réveiller la radio du téléphone.
|
||||
_urlCachee = await Cache.LireCouvertureCacheeAsync(Url!);
|
||||
|
||||
if (_urlCachee is null && Reseau.EnLigne)
|
||||
{
|
||||
// Tâche de fond, jamais attendue : la voie d'affichage en ligne ne doit rien à
|
||||
// ce cache, elle reste aussi rapide qu'avant.
|
||||
// Rien en cache : le réseau sert l'image (UrlAffichee retombe sur Url), et la mise
|
||||
// en cache démarre en tâche de fond pour que le prochain chargement soit local.
|
||||
// ⚠️ Jamais attendue : elle ne doit pas retarder un affichage qui va avoir lieu.
|
||||
_ = Cache.MettreEnCacheCouvertureAsync(Url!);
|
||||
}
|
||||
else
|
||||
{
|
||||
_urlCachee = await Cache.LireCouvertureCacheeAsync(Url!);
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
/// <summary>Révoque l'URL d'objet en cours, s'il y en a une.</summary>
|
||||
private async Task LibererAsync()
|
||||
{
|
||||
if (_urlCachee is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var aLiberer = _urlCachee;
|
||||
_urlCachee = null;
|
||||
await Cache.LibererCouvertureAsync(aLiberer);
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync() => await LibererAsync();
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool premierRendu)
|
||||
{
|
||||
if (_aFocaliser)
|
||||
|
||||
@@ -191,6 +191,27 @@ public sealed class CacheHorsLigne(IJSRuntime js) : IAsyncDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Libère une URL d'objet obtenue par <see cref="LireCouvertureCacheeAsync"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// ⚠️ Nécessaire depuis que le cache sert aussi en ligne : une URL <c>blob:</c> retient son
|
||||
/// Blob en mémoire tant qu'elle n'est pas révoquée. Avec une vignette par carte sur tous les
|
||||
/// écrans, ne pas libérer ferait enfler la mémoire de l'onglet au fil de la navigation.
|
||||
/// </remarks>
|
||||
public async Task LibererCouvertureAsync(string url)
|
||||
{
|
||||
try
|
||||
{
|
||||
var module = await ModuleAsync();
|
||||
await module.InvokeVoidAsync("couvertureLiberer", url);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Voir MettreEnCacheCouvertureAsync : rien à propager.
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_module is null)
|
||||
|
||||
@@ -154,3 +154,15 @@ export async function couvertureLire(url) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Libère une URL d'objet rendue par `couvertureLire`. ⚠️ Indispensable depuis que le cache sert
|
||||
// AUSSI en ligne : chaque `createObjectURL` retient son Blob en mémoire jusqu'à ce qu'on le
|
||||
// révoque ou que la page se ferme. Hors-ligne seul, la fuite restait bornée à une consultation
|
||||
// ponctuelle ; sur toutes les vignettes de tous les écrans, elle ne l'est plus.
|
||||
export function couvertureLiberer(url) {
|
||||
try {
|
||||
if (url) URL.revokeObjectURL(url);
|
||||
} catch (e) {
|
||||
// Rien à faire : libérer est un agrément, jamais une condition de bon fonctionnement.
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user