Rassemble au catalogue les tomes qui racontent la même histoire
Les livres d'une même série se suivaient au hasard de l'alphabet, et rien ne disait qu'ils allaient ensemble. Ils tiennent maintenant sous le nom de leur série, à la place qu'occupait le premier d'entre eux — l'ordre général ne bouge donc pas — et ce nom mène à la série, seul écran qui montre aussi les tomes qui manquent. Le regroupement ne cache ni ne duplique rien : une bascule le défait, et un livre rattaché à deux séries ne paraît qu'une fois, toujours sous la même. La règle est une fonction pure, avec ses tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -110,6 +110,22 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
@*
|
||||
⚠️ Grouper ne CACHE rien — c'est pourquoi cette bascule n'entre pas dans le compteur
|
||||
du bouton « Filtrer », qui ne compte que ce qui rendrait des livres invisibles. Elle
|
||||
se voit d'ailleurs dans la liste elle-même : avec ou sans blocs de série.
|
||||
|
||||
Décochée, on retrouve exactement le catalogue d'avant : tous les livres à plat.
|
||||
*@
|
||||
@if (_series is { Count: > 0 })
|
||||
{
|
||||
<label class="bascule">
|
||||
<input type="checkbox" checked="@_grouper"
|
||||
@onchange="e => _grouper = (bool?)e.Value ?? true" />
|
||||
<span>Grouper les livres d'une même série</span>
|
||||
</label>
|
||||
}
|
||||
|
||||
<p class="champ-aide">
|
||||
Le statut de lecture est <strong>le vôtre</strong> : filtrer dessus montre votre
|
||||
lecture, pas celle du foyer. Le prêt, lui, est commun.
|
||||
@@ -136,12 +152,41 @@ else if (_livres is not null)
|
||||
{
|
||||
<p class="compteur">@_livres.Count livre@(_livres.Count > 1 ? "s" : "")</p>
|
||||
|
||||
@*
|
||||
Les tomes d'une série tiennent sous son nom, à la place qu'occuperait le premier d'entre
|
||||
eux : l'ordre général du catalogue ne bouge pas, on ne perd donc pas un livre qu'on
|
||||
cherchait à sa lettre. Le nom du bloc MÈNE À LA SÉRIE, où figurent aussi les tomes qu'on
|
||||
ne possède pas — ce que le catalogue, lui, ne peut pas montrer.
|
||||
*@
|
||||
<ul class="liste-livres">
|
||||
@foreach (var livre in _livres)
|
||||
@foreach (var entree in Entrees)
|
||||
{
|
||||
<li @key="livre.Id">
|
||||
<CarteLivre Livre="livre" />
|
||||
</li>
|
||||
@if (entree.Serie is { } serie)
|
||||
{
|
||||
<li class="groupe-serie" @key="serie">
|
||||
<a class="groupe-serie-titre" href="@($"series/{serie.Id}")">
|
||||
<span class="groupe-serie-nom">@serie.Titre</span>
|
||||
<span class="groupe-serie-compte">
|
||||
@entree.Livres.Count tome@(entree.Livres.Count > 1 ? "s" : "")
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<ul class="liste-livres">
|
||||
@foreach (var livre in entree.Livres)
|
||||
{
|
||||
<li @key="livre.Id">
|
||||
<CarteLivre Livre="livre" />
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</li>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li @key="entree.Livres[0].Id">
|
||||
<CarteLivre Livre="entree.Livres[0]" />
|
||||
</li>
|
||||
}
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
@@ -175,6 +220,25 @@ else if (_livres is not null)
|
||||
public int? AuteurId { get; set; }
|
||||
|
||||
private IReadOnlyList<LivreDto>? _livres;
|
||||
|
||||
/// <summary>
|
||||
/// Les séries du foyer, pour regrouper leurs tomes. <c>null</c> = pas encore lues.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// ⚠️ Elles ne sont lues <b>qu'une fois</b> par visite (et à chaque bascule du réseau), pas à
|
||||
/// chaque chargement du catalogue : celui-ci se relit à chaque frappe dans la recherche, et
|
||||
/// une requête de séries par caractère serait du gâchis pour une liste qui ne bouge pas
|
||||
/// pendant qu'on tape. Le prix est qu'un tome rattaché depuis un autre écran n'apparaît
|
||||
/// groupé qu'au prochain passage ici.
|
||||
/// </remarks>
|
||||
private IReadOnlyList<SerieDto>? _series;
|
||||
|
||||
/// <summary>Regroupement actif — l'utilisateur peut le défaire, rien ne disparaît alors.</summary>
|
||||
private bool _grouper = true;
|
||||
|
||||
/// <summary>Le catalogue tel qu'il s'affiche : livres seuls et blocs de série mêlés.</summary>
|
||||
private IReadOnlyList<EntreeCatalogue> Entrees =>
|
||||
GroupementCatalogue.Grouper(_livres ?? [], _grouper ? _series : null);
|
||||
private AuteurDto? _auteur;
|
||||
private int? _auteurCharge;
|
||||
private string _recherche = string.Empty;
|
||||
@@ -238,14 +302,40 @@ else if (_livres is not null)
|
||||
/// </summary>
|
||||
protected override void OnInitialized() => Reseau.Change += SurChangementReseau;
|
||||
|
||||
/// <summary>
|
||||
/// Lit les séries, et se contente de ne pas grouper si elles manquent.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// ⚠️ Une lecture ratée ne doit rien coûter au catalogue : sans série, la liste s'affiche à
|
||||
/// plat, ce qui est exactement l'écran d'avant. Hors-ligne, la lecture retombe d'elle-même
|
||||
/// sur l'instantané « series ».
|
||||
/// </remarks>
|
||||
private async Task ChargerSeriesAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_series = await Api.ListerSeriesAsync();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_series = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void SurChangementReseau() => _ = InvokeAsync(async () =>
|
||||
{
|
||||
await ChargerSeriesAsync();
|
||||
await ChargerAsync();
|
||||
StateHasChanged();
|
||||
});
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
if (_series is null)
|
||||
{
|
||||
await ChargerSeriesAsync();
|
||||
}
|
||||
|
||||
if (_auteurCharge != AuteurId)
|
||||
{
|
||||
_auteurCharge = AuteurId;
|
||||
|
||||
Reference in New Issue
Block a user