| File: Components\Layout\ResourceServiceConnectionProvider.cs | Web Access |
| Project: src\src\Aspire.Dashboard\Aspire.Dashboard.csproj (Aspire.Dashboard) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; namespace Aspire.Dashboard.Components.Layout; public sealed class ResourceServiceConnectionProvider : ComponentBase, IAsyncDisposable { private const int MaxAttemptsBeforeShowingRetry = 5; private DotNetObjectReference<ResourceServiceConnectionProvider>? _dotNetRef; private int _disconnectedCount; [Inject] public required IDashboardClient DashboardClient { get; init; } [Inject] public required IJSRuntime JS { get; init; } protected override void OnInitialized() { if (!DashboardClient.IsEnabled) { return; } DashboardClient.ConnectionStateChanged += OnConnectionStateChanged; } protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender && DashboardClient.IsEnabled) { // Register the .NET object reference so JS can call back for retry. _dotNetRef = DotNetObjectReference.Create(this); await JS.InvokeVoidAsync("registerResourceServiceConnectionProvider", _dotNetRef); // Report the current state immediately in case we're already disconnected or unsupported. // Don't report Connecting — that's normal startup. if (DashboardClient.ConnectionState is DashboardConnectionState.Disconnected or DashboardConnectionState.Unsupported) { await UpdateModalStateAsync(DashboardClient.ConnectionState); } } } private async void OnConnectionStateChanged(DashboardConnectionState state) { try { await InvokeAsync(() => UpdateModalStateAsync(state)); } catch (ObjectDisposedException) { // Component disposed, ignore. } catch (JSDisconnectedException) { // Circuit disconnected, JS interop is no longer available. } } private async Task UpdateModalStateAsync(DashboardConnectionState state) { if (state is DashboardConnectionState.Connected) { _disconnectedCount = 0; await JS.InvokeVoidAsync("updateResourceServiceConnectionState", "connected", false); } else if (state is DashboardConnectionState.Unsupported) { _disconnectedCount = 0; await JS.InvokeVoidAsync("updateResourceServiceConnectionState", "unsupported", false, DashboardClient.ApplicationName, DashboardClient.MinRequiredVersion); } else if (state is DashboardConnectionState.Disconnected) { _disconnectedCount++; var showRetry = _disconnectedCount >= MaxAttemptsBeforeShowingRetry; await JS.InvokeVoidAsync("updateResourceServiceConnectionState", "disconnected", showRetry); } // Connecting state — don't update modal. } [JSInvokable] public async Task ReconnectFromJs() { await DashboardClient.ReconnectAsync(); } public ValueTask DisposeAsync() { DashboardClient.ConnectionStateChanged -= OnConnectionStateChanged; _dotNetRef?.Dispose(); return ValueTask.CompletedTask; } }