@inherits LayoutComponentBase
@implements IDisposable
@inject ServiceLivresApi Api
@inject EtatReseau Reseau
@* Mise en page pensée mobile d'abord : un bandeau, une colonne, rien à gauche.
Le PC hérite de la même colonne, simplement centrée et limitée en largeur. *@
MaBibli
@if (!Reseau.EnLigne)
{
@* Une pastille dans le bandeau, visible sur tous les écrans : l'état du réseau change
l'usage de l'application, il ne doit pas se découvrir au premier clic qui échoue. *@
Hors ligne
}
@if (_utilisateur?.Identifiant is not null)
{
@_utilisateur.Affichage@(_utilisateur.Simule ? " (dev)" : "")
}
@if (!Reseau.EnLigne)
{
@*
Dire d'où viennent les données et de quand elles datent. Sans cette phrase, une
bibliothèque affichée hors-ligne est indiscernable d'une bibliothèque à jour — et un
livre ajouté depuis un autre appareil manquerait sans explication.
*@
Hors ligne.
@(Reseau.DerniereSynchro is { } synchro
? $" Données enregistrées {Quand(synchro)}. "
: " Aucune donnée n'a encore pu être enregistrée sur cet appareil. ")
Consultation et recherche fonctionnent ; les modifications sont impossibles.
}
@Body
@code {
private UtilisateurCourant? _utilisateur;
private bool _etaitEnLigne = true;
private bool _synchroEnCours;
protected override async Task OnInitializedAsync()
{
Reseau.Change += SurChangementReseau;
Reseau.SynchroChange += SurSynchro;
// Écoute des bascules online/offline avant tout appel : un démarrage hors-ligne doit
// aller directement au cache, sans attendre l'échec d'une requête.
await Reseau.DemarrerAsync();
_etaitEnLigne = Reseau.EnLigne;
_utilisateur = await Api.ObtenirUtilisateurAsync();
// Rafraîchit tout le fonds, pas seulement l'écran ouvert : c'est ce qui rend la
// bibliothèque entière consultable et cherchable après la coupure.
await SynchroniserAsync();
}
///
/// Au retour du réseau, on recharge : la bibliothèque a pu changer depuis un autre appareil,
/// et les actions d'écriture redeviennent disponibles dans la foulée.
///
private void SurChangementReseau()
{
var revenu = Reseau.EnLigne && !_etaitEnLigne;
_etaitEnLigne = Reseau.EnLigne;
_ = InvokeAsync(async () =>
{
StateHasChanged();
if (revenu)
{
_utilisateur = await Api.ObtenirUtilisateurAsync();
await SynchroniserAsync();
StateHasChanged();
}
});
}
private async Task SynchroniserAsync()
{
if (_synchroEnCours)
{
return;
}
_synchroEnCours = true;
try
{
await Api.SynchroniserAsync();
}
finally
{
_synchroEnCours = false;
}
}
/// Date de synchronisation en clair : l'heure suffit le jour même.
private static string Quand(DateTimeOffset instant)
{
var local = instant.ToLocalTime();
return local.Date == DateTimeOffset.Now.Date
? $"aujourd'hui à {local:HH:mm}"
: $"le {local:dd/MM/yyyy} à {local:HH:mm}";
}
/// La date affichée vient de changer : rien à recharger, juste à redessiner.
private void SurSynchro() => _ = InvokeAsync(StateHasChanged);
public void Dispose()
{
Reseau.Change -= SurChangementReseau;
Reseau.SynchroChange -= SurSynchro;
}
}