| File: Components\Layout\ResourceTerminalsAvailabilityProvider.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 Aspire.Dashboard.Model; using Microsoft.AspNetCore.Components; namespace Aspire.Dashboard.Components.Layout; public sealed class ResourceTerminalsAvailabilityProvider : ComponentBase, IAsyncDisposable { private readonly CancellationTokenSource _cts = new(); private readonly Dictionary<string, ResourceViewModel> _resources = new(StringComparers.ResourceName); private Task? _watchTask; private bool _available; private bool _disposed; [Inject] public required IDashboardClient DashboardClient { get; init; } [Inject] public required ILogger<ResourceTerminalsAvailabilityProvider> Logger { get; init; } [Parameter, EditorRequired] public required EventCallback<bool> AvailabilityChanged { get; set; } protected override void OnInitialized() { if (DashboardClient.IsEnabled && !DashboardClient.IsReadOnly) { _watchTask = WatchAsync(_cts.Token); } } private async Task WatchAsync(CancellationToken cancellationToken) { try { await DashboardClient.WhenConnected.WaitAsync(cancellationToken); var (snapshot, updates) = await DashboardClient.SubscribeResourcesAsync(cancellationToken); await InvokeAsync(async () => { foreach (var resource in snapshot) { _resources[resource.Name] = resource; } await ReportAvailabilityAsync(cancellationToken); }); await foreach (var changes in updates.WithCancellation(cancellationToken)) { await InvokeAsync(async () => { foreach (var (changeType, resource) in changes) { if (changeType == ResourceViewModelChangeType.Delete) { _resources.Remove(resource.Name); } else { _resources[resource.Name] = resource; } } await ReportAvailabilityAsync(cancellationToken); }); } } catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) { } catch (Exception ex) { Logger.LogError(ex, "Failed to watch resource terminal availability."); await DispatchExceptionAsync(ex); } } private async Task ReportAvailabilityAsync(CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return; } var available = _resources.Values.Any(ResourceSelectHelpers.HasUsableTerminal); if (_available != available) { _available = available; await AvailabilityChanged.InvokeAsync(available); } } public async ValueTask DisposeAsync() { if (_disposed) { return; } _disposed = true; await _cts.CancelAsync(); if (_watchTask is not null) { await _watchTask; } _cts.Dispose(); } }