28 septembre 2022

Blazor – gérer les déconnexions

J’ai une application Blazor Server et si par exemple on laisse ouvert l’onglet sans utiliser l’application ou alors que je mets à jour mon application, elle est déconnectée. Blazor gère tout seul ces états. C’est très bien, seulement je trouve que l’expérience utilisateur n’est pas super. Voici ce qui apparait :

Message de déconnexions Blazor server

Blazor s’occupe donc tout seul d’ajouter une div avec l’id components-reconnect-modal et de gérer son contenu selon le type de déconnexion (server ne répondant plus, connexion rejetée, problème réseau..) :

˂div id="components-reconnect-modal">
...
˂/div>

Mais si nous l’ajoutons nous même à notre projet, et bien Blazor s’en servira sans l’ajouter !

Pourquoi pas personnaliser le style

Connaissant l’id utilisé par Blazor et le contenu de ce qu’il ajoute on peut déjà personnaliser ce qu’il affiche. Ca sera déjà plus sympa. Il suffit par exemple d’ajouter ces styles CSS au fichier site.css du projet :

#components-reconnect-modal {
    display: flex !important;
    opacity: 1 !important;
    background-color: rgb(255 255 255 / 80%) !important;
}
 
    #components-reconnect-modal::before {
        content: '';
        width: 300px;
        height: 65px;
        background: #BDCBFF;
        position: fixed;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        display: block;
    }
 
    #components-reconnect-modal h5 {
        margin-top: 50px !important;
        padding: .75rem 1.25rem;
        width: 300px;
        position: fixed;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        z-index: 1050;
        color: #003494;
        background-color: #BDCBFF;
        box-shadow: 0px 10px 10px 0px hsl(0deg 0% 0% / 20%);
    }
 
    #components-reconnect-modal div {
        border-color: rgb(0 52 148) rgb(89 127 222) rgb(161 180 235) !important;
        margin: auto;
        z-index: 1050;
    }
 
    #components-reconnect-modal h5 a {
        color: #092464 !important;
    }

#components-reconnect-modal button {
    color:#515151;
    padding: 4px 8px;
    font-size: .875rem;
    min-width: 64px;
    box-sizing: border-box;
    transition: background-color 250ms cubic-bezier(.4,0,.2,1) 0ms,box-shadow 250ms cubic-bezier(.4,0,.2,1) 0ms,border 250ms cubic-bezier(.4,0,.2,1) 0ms;
    font-weight: 500;
    line-height: 1;
    letter-spacing: .02857em;
    text-transform: uppercase;
    margin: auto !important;
    background:rgb(46 121 232 / 28%);
    z-index: 1500;
    border:0;
}

Le résultat est sans appel, c’est nettement mieux :

Changer les tentatives de reconnections

Tant que nous y sommes, nous pouvons aussi modifier ces tentatives de reconnexion pr une petit script à ajouter dans la page _Layout.cshtml. Le code parlera de lui-même, il faut juste penser à ajouter l’attribut autostart= »false » à blazor.server.js :

<script src="_framework/blazor.server.js" autostart="false"></script>
<script>
      Blazor.start({
        reconnectionOptions: {
          maxRetries: 10,
          retryIntervalMilliseconds: 8000
        }
      });
˂/script>

Une autre solution avec un composant Razor

C’est ce que je préfère. Dans un nouveau composant Razor, par exemple ReconnectDialog.razor, j’ajoute ce code :

 <div id="components-reconnect-modal" class="components-reconnect-hide">
        <div class="hide">
            <div class="myDialog">
            
                <div class="components-reconnect-top">
                    <div class="oi oi-transfer p10"></div> <h5>Connected</h5>
                </div>
            </div>
        </div>
        <div class="show">
            <div class="myDialog">
                 <div class="components-reconnect-top">
                     <div class="oi oi-warning p10"></div> <h5>Attempting to connect to server</h5>
                </div>
                <div class="components-reconnect-bottom">
                    <a href="javascript:window.location.reload()" class="p5">Reload</a> the page to restore functionality.                          
                </div>  
            </div>
           
        </div>
        <div class="failed">    
            <div class="myDialog">
                <div class="components-reconnect-top">
                    <div class="oi oi-wrench p10"></div> <h5>Failing to connect</h5>
                </div>    
                <div class="components-reconnect-bottom">
                    Reconnection failed, probably due to a network failure. <a href="javascript:window.Blazor.reconnect()" class="p5">Click here</a> to attempt reconnection.                          
                </div>  
            </div>
        </div>
        <div class="rejected">  
            <div class="myDialog">
                <div class="components-reconnect-top">
                    <div class="oi oi-ban p10"></div> <h5>Connection rejected</h5>
                </div> 
                <div class="components-reconnect-bottom">
                    <a href="javascript:window.location.reload()" class="p5">Reload</a> the page to restore functionality.                          
                </div>   
            </div>
        </div>
    </div>

Et dans la page CSS :

	.components-reconnect-show > div,
	.components-reconnect-failed > div,
	.components-reconnect-rejected > div {
		display: none;
	}

	.components-reconnect-hide > .hide,
	.components-reconnect-show > .show,
	.components-reconnect-failed > .failed,
	.components-reconnect-rejected > .rejected {
		display: block;
	}

    
     
    .p5 {
        padding-right:5px;
    }
    .p10 {
        padding-right:10px;
    }

    .myDialog {
        position: fixed;
        top: 0;
        bottom: 0;
        left: 0;
        right: 0;
        z-index:1050;

        background-color: #fefefe;
        margin: auto;
        border: 1px solid #888;
        height: 150px;
        width: 400px;
        border-radius: 6px;
        box-shadow: 0 10px 20px rgb(0 0 0 / 10%), 0 6px 6px rgb(0 0 0 / 16%);
    }

    .components-reconnect-top {
        background: #6422EB;
        display: flex;
        flex-direction: row;
        height: 60px;
        transition: height 300ms cubic-bezier(0.4, 0.0, 0.2, 1);
        padding: 20px;
        color:white;
        width:100%;
    }

    .components-reconnect-bottom {
        display: flex;
        flex-direction: row;
        padding: 20px;
    }

Vous ajoutez maintenant ce composant en haut de votre MainLayout.razor :

&lt;ReconnectDialog>&lt;/ReconnectDialog>

Et voici ce que ça donne. Bien évidemment la partie <div class= »hide »> est à retirée ^^

Oui mais…. repensons à l’expérience utilisateur !

Si c’est déconnecté, plus de liaisons, l’application est seule au monde. Et comme nos utilisateurs sont pas de « informaticien » 😑, autant trouver une astuce pour que le navitateur tente lui même une reconnexion. La bonne blague serait d’ajouter <meta http-equiv= »refresh » content= »30″> 🤪 .

Supprimer tout ce qu’on a créé (Style CSS, div et composant) et laissons alors la navigateur gérer lui-même. Ajouter simplement ce script à la page _Layout.cshtml :

&lt;script src="_framework/blazor.server.js">&lt;/script>
    &lt;script>
        // Wait until a &#039;reload&#039; button appears
        new MutationObserver((mutations, observer) => {
            if (document.querySelector(&#039;#components-reconnect-modal h5 a&#039;)) {
                // Now every 10 seconds, see if the server appears to be back, and if so, reload
                async function attemptReload() {
                    await fetch(&#039;&#039;); // Check the server really is back
                    location.reload();
                }
                observer.disconnect();
                attemptReload();
                setInterval(attemptReload, 2000);
            }
        }).observe(document.body, { childList: true, subtree: true });
    &lt;/script>

Ce petit script qui va tourner dans le navigateur de l’utilisateur va surveiller la présence de la séquence parent/enfants components-reconnect-modal h5 a dans le DOM et s’il la trouve, relancer lui-même la connexion.

⚠ Si vous souhaitez concerver votre propre composant, il va falloir adapter le querySelector

Lien associée Microsoft Docs – ASP.NET Core Blazor SignalR guidance

Laisser un commentaire