@using System.Security.Claims @implements IAsyncDisposable @inject AuthenticationStateProvider AuthenticationStateProvider @inject NavigationManager Navigation @inject IJSRuntime JS @code { private static readonly string[] HomePaths = ["", "index.html"]; private static readonly string[] ApplicationPaths = ["application", "application.html"]; private static readonly string[] RulesPaths = ["reglement", "reglement.html"]; private DotNetObjectReference? _dotNetReference; private bool _listenerRegistered; private bool _authStateSubscribed; private string? AuthModalSource; private string AuthModalTitle = "Authentification"; private bool ShowAuthModal; private bool IsAuthenticated; private string DisplayName = "Utilisateur connecte"; private string DisplayMeta = "Session active"; private string LoginHref => BuildAuthHref("login", EffectiveReturnUrl); private string RegisterHref => BuildAuthHref("register", EffectiveReturnUrl); private string LoginModalSrc => BuildAuthHref("login", EffectiveReturnUrl, embedded: true); private string RegisterModalSrc => BuildAuthHref("register", EffectiveReturnUrl, embedded: true); private string LogoutHref => BuildAuthHref("logout", "/"); private string CurrentPath { get { var absolutePath = new Uri(Navigation.Uri).AbsolutePath; return absolutePath.Trim('/'); } } private string EffectiveReturnUrl { get { var absolutePath = new Uri(Navigation.Uri).AbsolutePath; if (absolutePath.StartsWith("/authentication/", StringComparison.OrdinalIgnoreCase)) { return "/"; } return string.IsNullOrWhiteSpace(absolutePath) ? "/" : absolutePath; } } private string BuildNavLinkClass(string[] paths) => IsCurrentPage(paths) ? "site-menu-link is-active" : "site-menu-link"; private string? BuildAriaCurrent(string[] paths) => IsCurrentPage(paths) ? "page" : null; private bool IsCurrentPage(string[] paths) => paths.Any(path => string.Equals(CurrentPath, path, StringComparison.OrdinalIgnoreCase)); private static string BuildAuthHref(string action, string returnUrl, bool embedded = false) { var query = $"returnUrl={Uri.EscapeDataString(returnUrl)}"; if (embedded) { query += "&embedded=1"; } return $"authentication/{action}?{query}"; } private static string BuildDisplayName(ClaimsPrincipal user) => user.Identity?.Name ?? user.FindFirst("name")?.Value ?? user.FindFirst("preferred_username")?.Value ?? "Utilisateur connecte"; private static string BuildMeta(ClaimsPrincipal user) => user.FindFirst("email")?.Value ?? "Session active"; protected override async Task OnInitializedAsync() { AuthenticationStateProvider.AuthenticationStateChanged += HandleAuthenticationStateChanged; _authStateSubscribed = true; await RefreshAuthenticationStateAsync(); } private async Task OpenLoginModal() => await OpenAuthModalAsync("Se connecter", LoginModalSrc); private async Task OpenRegisterModal() => await OpenAuthModalAsync("Creer un compte", RegisterModalSrc); private async Task OpenAuthModalAsync(string title, string source) { await EnsureAuthListenerAsync(); AuthModalTitle = title; AuthModalSource = source; ShowAuthModal = true; } private void CloseAuthModal() { ShowAuthModal = false; AuthModalSource = null; } private async Task EnsureAuthListenerAsync() { if (_listenerRegistered) { return; } _dotNetReference ??= DotNetObjectReference.Create(this); try { await JS.InvokeVoidAsync("chesscubingAuthModal.registerListener", _dotNetReference); _listenerRegistered = true; } catch { // Si le navigateur garde encore un ancien script en cache, // on laisse l'application fonctionner et la modal reste utilisable sans auto-fermeture. } } [JSInvokable] public Task HandleAuthModalMessage(string status) { if (!string.Equals(status, "login-succeeded", StringComparison.OrdinalIgnoreCase) && !string.Equals(status, "logout-succeeded", StringComparison.OrdinalIgnoreCase)) { return Task.CompletedTask; } CloseAuthModal(); StateHasChanged(); Navigation.NavigateTo(Navigation.Uri, forceLoad: true); return Task.CompletedTask; } private void HandleAuthenticationStateChanged(Task authenticationStateTask) => _ = InvokeAsync(RefreshAuthenticationStateAsync); private async Task RefreshAuthenticationStateAsync() { try { var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); var user = authState.User; if (user.Identity?.IsAuthenticated == true) { IsAuthenticated = true; DisplayName = BuildDisplayName(user); DisplayMeta = BuildMeta(user); } else { ResetAuthenticationDisplay(); } } catch { ResetAuthenticationDisplay(); } } private void ResetAuthenticationDisplay() { IsAuthenticated = false; DisplayName = "Utilisateur connecte"; DisplayMeta = "Session active"; } public async ValueTask DisposeAsync() { if (_authStateSubscribed) { AuthenticationStateProvider.AuthenticationStateChanged -= HandleAuthenticationStateChanged; } if (_listenerRegistered) { try { await JS.InvokeVoidAsync("chesscubingAuthModal.unregisterListener"); } catch { } } _dotNetReference?.Dispose(); } private static string BoolString(bool value) => value ? "true" : "false"; }