diff --git a/src/Aspire.Dashboard/Components/Controls/AspireMenuButton.razor b/src/Aspire.Dashboard/Components/Controls/AspireMenuButton.razor index 0fab2d93cb..26d80cd94c 100644 --- a/src/Aspire.Dashboard/Components/Controls/AspireMenuButton.razor +++ b/src/Aspire.Dashboard/Components/Controls/AspireMenuButton.razor @@ -13,40 +13,17 @@ }; } -@if (IconEnd is not null) -{ - -} -else -{ - - @if (string.IsNullOrWhiteSpace(Text)) - { - - } - else if (!string.IsNullOrWhiteSpace(Text)) - { - @Text - - - } - -} + + @if (string.IsNullOrWhiteSpace(Text)) + { + + } + else + { + @Text + + } + @foreach (var item in Items) diff --git a/src/Aspire.Dashboard/Components/Controls/AspireMenuButton.razor.cs b/src/Aspire.Dashboard/Components/Controls/AspireMenuButton.razor.cs index cb944ac19c..52e38fac71 100644 --- a/src/Aspire.Dashboard/Components/Controls/AspireMenuButton.razor.cs +++ b/src/Aspire.Dashboard/Components/Controls/AspireMenuButton.razor.cs @@ -22,9 +22,6 @@ public partial class AspireMenuButton : FluentComponentBase [Parameter] public Icon? IconStart { get; set; } - [Parameter] - public Icon? IconEnd { get; set; } - [Parameter] public Icon? Icon { get; set; } @@ -43,8 +40,7 @@ public partial class AspireMenuButton : FluentComponentBase [Parameter] public string? Title { get; set; } - [Parameter] - public string MenuButtonId { get; set; } = Identifier.NewId(); + public string MenuButtonId { get; } = Identifier.NewId(); protected override void OnParametersSet() { diff --git a/src/Aspire.Dashboard/Components/Pages/Resources.razor b/src/Aspire.Dashboard/Components/Pages/Resources.razor index 48bd238922..5fd10474ba 100644 --- a/src/Aspire.Dashboard/Components/Pages/Resources.razor +++ b/src/Aspire.Dashboard/Components/Pages/Resources.razor @@ -43,7 +43,7 @@ @if (HasAnyChildResources()) { @@ -53,7 +53,7 @@ { @if (HasAnyChildResources()) { - var showExpandAllButton = _collapsedResourceNames.Count > 0; + var showExpandAllButton = HasCollapsedResources(); diff --git a/src/Aspire.Dashboard/Components/Pages/Resources.razor.cs b/src/Aspire.Dashboard/Components/Pages/Resources.razor.cs index 28296dfd05..8534e9b738 100644 --- a/src/Aspire.Dashboard/Components/Pages/Resources.razor.cs +++ b/src/Aspire.Dashboard/Components/Pages/Resources.razor.cs @@ -45,7 +45,7 @@ public partial class Resources : ComponentBase, IAsyncDisposable [Inject] public required IJSRuntime JS { get; init; } [Inject] - public required ILocalStorage LocalStorage { get; init; } + public required ISessionStorage SessionStorage { get; init; } [CascadingParameter] public required ViewportInformation ViewportInformation { get; set; } @@ -71,7 +71,6 @@ public partial class Resources : ComponentBase, IAsyncDisposable private readonly CancellationTokenSource _watchTaskCancellationTokenSource = new(); private readonly ConcurrentDictionary _resourceByName = new(StringComparers.ResourceName); private readonly HashSet _collapsedResourceNames = new(StringComparers.ResourceName); - private readonly HashSet _initialCollapsedResourceNames = []; private string _filter = ""; private bool _isFilterPopupVisible; private Task? _resourceSubscriptionTask; @@ -153,12 +152,12 @@ protected override async Task OnInitializedAsync() if (DashboardClient.IsEnabled) { - var settings = await LocalStorage.GetUnprotectedAsync(BrowserStorageKeys.ResourcesPageSettings); - if (settings.Value is not null) + var collapsedResult = await SessionStorage.GetAsync>(BrowserStorageKeys.ResourcesCollapsedResourceNames); + if (collapsedResult.Success) { - foreach (var resourceName in settings.Value.CollapsedResourceNames) + foreach (var resourceName in collapsedResult.Value) { - _initialCollapsedResourceNames.Add(resourceName); + _collapsedResourceNames.Add(resourceName); } } @@ -265,11 +264,6 @@ bool UpdateFromResource(ResourceViewModel resource, Func resourceT ResourceStatesToVisibility.TryAdd(resource.State ?? string.Empty, stateVisible(resource.State ?? string.Empty)); ResourceHealthStatusesToVisibility.TryAdd(resource.HealthStatus?.Humanize() ?? string.Empty, healthStatusVisible(resource.HealthStatus?.Humanize() ?? string.Empty)); - if (_initialCollapsedResourceNames.Contains(resource.Name)) - { - _collapsedResourceNames.Add(resource.Name); - } - UpdateMenuButtons(); return added; @@ -311,7 +305,7 @@ private void UpdateMenuButtons() { _resourcesMenuItems.Clear(); - if (_collapsedResourceNames.Count > 0) + if (HasCollapsedResources()) { _resourcesMenuItems.Add(new MenuButtonItem { @@ -333,6 +327,11 @@ private void UpdateMenuButtons() } } + private bool HasCollapsedResources() + { + return _resourceByName.Any(r => !r.Value.IsHiddenState() && _collapsedResourceNames.Contains(r.Key)); + } + private void UpdateMaxHighlightedCount() { var maxHighlightedCount = 0; @@ -505,26 +504,34 @@ private async Task OnToggleCollapse(ResourceGridViewModel viewModel) _collapsedResourceNames.Remove(viewModel.Resource.Name); } - await LocalStorage.SetUnprotectedAsync(BrowserStorageKeys.ResourcesPageSettings, new ResourcesPageSettings(CollapsedResourceNames: _collapsedResourceNames)); + await SessionStorage.SetAsync(BrowserStorageKeys.ResourcesCollapsedResourceNames, _collapsedResourceNames.ToList()); await _dataGrid.SafeRefreshDataAsync(); UpdateMenuButtons(); } private async Task OnToggleCollapseAll() { - if (_collapsedResourceNames.Count > 0) + var resourcesWithChildren = _resourceByName.Values + .Where(r => !r.IsHiddenState()) + .Where(r => _resourceByName.Values.Any(nested => nested.GetResourcePropertyValue(KnownProperties.Resource.ParentName) == r.Name)) + .ToList(); + + if (HasCollapsedResources()) { - _collapsedResourceNames.Clear(); + foreach (var resource in resourcesWithChildren) + { + _collapsedResourceNames.Remove(resource.Name); + } } else { - foreach (var resourceName in _resourceByName.Keys) + foreach (var resource in resourcesWithChildren) { - _collapsedResourceNames.Add(resourceName); + _collapsedResourceNames.Add(resource.Name); } } - await LocalStorage.SetUnprotectedAsync(BrowserStorageKeys.ResourcesPageSettings, new ResourcesPageSettings(CollapsedResourceNames: _collapsedResourceNames)); + await SessionStorage.SetAsync(BrowserStorageKeys.ResourcesCollapsedResourceNames, _collapsedResourceNames.ToList()); await _dataGrid.SafeRefreshDataAsync(); UpdateMenuButtons(); } @@ -547,6 +554,4 @@ public async ValueTask DisposeAsync() await TaskHelpers.WaitIgnoreCancelAsync(_resourceSubscriptionTask); } - - public record ResourcesPageSettings(HashSet CollapsedResourceNames); } diff --git a/src/Aspire.Dashboard/Model/BrowserStorage/StorageResult.cs b/src/Aspire.Dashboard/Model/BrowserStorage/StorageResult.cs index 23a7849922..9cc9d3411f 100644 --- a/src/Aspire.Dashboard/Model/BrowserStorage/StorageResult.cs +++ b/src/Aspire.Dashboard/Model/BrowserStorage/StorageResult.cs @@ -1,6 +1,20 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; + namespace Aspire.Dashboard.Model.BrowserStorage; -public readonly record struct StorageResult(bool Success, TValue? Value); +public readonly struct StorageResult +{ + [MemberNotNullWhen(true, nameof(Value))] + public bool Success { get; } + + public TValue? Value { get; } + + public StorageResult(bool success, TValue? value) + { + Success = success; + Value = value; + } +} diff --git a/src/Aspire.Dashboard/Resources/Resources.Designer.cs b/src/Aspire.Dashboard/Resources/Resources.Designer.cs index 6b5af112f9..adfcb3160b 100644 --- a/src/Aspire.Dashboard/Resources/Resources.Designer.cs +++ b/src/Aspire.Dashboard/Resources/Resources.Designer.cs @@ -1,6 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. +// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -384,7 +385,7 @@ public static string ResourcesEnvironmentVariablesHeader { } /// - /// Looks up a localized string similar to Update filters. + /// Looks up a localized string similar to Has filters. /// public static string ResourcesFiltered { get { @@ -420,7 +421,7 @@ public static string ResourcesNoResources { } /// - /// Looks up a localized string similar to Add filters. + /// Looks up a localized string similar to No filters. /// public static string ResourcesNotFiltered { get { diff --git a/src/Aspire.Dashboard/Resources/Resources.resx b/src/Aspire.Dashboard/Resources/Resources.resx index 3985d5bd9f..4627b62367 100644 --- a/src/Aspire.Dashboard/Resources/Resources.resx +++ b/src/Aspire.Dashboard/Resources/Resources.resx @@ -1,17 +1,17 @@ - @@ -125,10 +125,10 @@ Resources - Add filters + No filters - Update filters + Has filters Resource types @@ -274,4 +274,4 @@ Expand child resources - + \ No newline at end of file diff --git a/src/Aspire.Dashboard/Utils/BrowserStorageKeys.cs b/src/Aspire.Dashboard/Utils/BrowserStorageKeys.cs index b3460c328a..f41dec6052 100644 --- a/src/Aspire.Dashboard/Utils/BrowserStorageKeys.cs +++ b/src/Aspire.Dashboard/Utils/BrowserStorageKeys.cs @@ -14,7 +14,7 @@ internal static class BrowserStorageKeys public const string MetricsPageState = "Aspire_PageState_Metrics"; public const string ConsoleLogsPageState = "Aspire_PageState_ConsoleLogs"; public const string ConsoleLogConsoleSettings = "Aspire_ConsoleLog_ConsoleSettings"; - public const string ResourcesPageSettings = "Aspire_Resources_ResourcesPageSettings"; + public const string ResourcesCollapsedResourceNames = "Aspire_Resources_CollapsedResourceNames"; public static string SplitterOrientationKey(string viewKey) { diff --git a/tests/Aspire.Dashboard.Components.Tests/Shared/TestLocalStorage.cs b/tests/Aspire.Dashboard.Components.Tests/Shared/TestLocalStorage.cs index 96b29fc590..eab5a6ac7d 100644 --- a/tests/Aspire.Dashboard.Components.Tests/Shared/TestLocalStorage.cs +++ b/tests/Aspire.Dashboard.Components.Tests/Shared/TestLocalStorage.cs @@ -12,7 +12,7 @@ public sealed class TestLocalStorage : ILocalStorage public Task> GetAsync(string key) { - return Task.FromResult(new StorageResult(Success: false, Value: default)); + return Task.FromResult(new StorageResult(success: false, value: default)); } public Task> GetUnprotectedAsync(string key) @@ -20,9 +20,9 @@ public Task> GetUnprotectedAsync(string key) if (OnGetUnprotectedAsync is { } callback) { var (success, value) = callback(key); - return Task.FromResult(new StorageResult(Success: success, Value: (T)(value ?? default(T))!)); + return Task.FromResult(new StorageResult(success: success, value: (T)(value ?? default(T))!)); } - return Task.FromResult(new StorageResult(Success: false, Value: default)); + return Task.FromResult(new StorageResult(success: false, value: default)); } public Task SetAsync(string key, T value) diff --git a/tests/Aspire.Dashboard.Components.Tests/Shared/TestSessionStorage.cs b/tests/Aspire.Dashboard.Components.Tests/Shared/TestSessionStorage.cs index 9f01691d77..678c64ea57 100644 --- a/tests/Aspire.Dashboard.Components.Tests/Shared/TestSessionStorage.cs +++ b/tests/Aspire.Dashboard.Components.Tests/Shared/TestSessionStorage.cs @@ -15,10 +15,10 @@ public Task> GetAsync(string key) if (OnGetAsync is { } callback) { var (success, value) = callback(key); - return Task.FromResult(new StorageResult(Success: success, Value: (T)(value ?? default(T))!)); + return Task.FromResult(new StorageResult(success: success, value: (T)(value ?? default(T))!)); } - return Task.FromResult>(new StorageResult(Success: false, Value: default)); + return Task.FromResult>(new StorageResult(success: false, value: default)); } public Task SetAsync(string key, T value)