Skip to content

Commit

Permalink
Changes and fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesNK committed Feb 8, 2025
1 parent 9eb65df commit 0c0fa99
Show file tree
Hide file tree
Showing 10 changed files with 93 additions and 100 deletions.
45 changes: 11 additions & 34 deletions src/Aspire.Dashboard/Components/Controls/AspireMenuButton.razor
Original file line number Diff line number Diff line change
Expand Up @@ -13,40 +13,17 @@
};
}

@if (IconEnd is not null)
{
<FluentButton Id="@MenuButtonId"
Title="@Title"
Appearance="@ButtonAppearance"
IconStart="@IconStart"
IconEnd="@IconEnd"
@onclick="ToggleMenu"
Disabled="@(!Items.Where(i => !i.IsDivider).Any())"
AdditionalAttributes="@additionalButtonAttributes"
Class="@ButtonClass" />
}
else
{
<FluentButton Id="@MenuButtonId"
Title="@Title"
Appearance="@ButtonAppearance"
IconStart="@IconStart"
@onclick="ToggleMenu"
Disabled="@(!Items.Where(i => !i.IsDivider).Any())"
AdditionalAttributes="@additionalButtonAttributes"
Class="@ButtonClass">
@if (string.IsNullOrWhiteSpace(Text))
{
<FluentIcon Value="@_icon" Color="@IconColor" />
}
else if (!string.IsNullOrWhiteSpace(Text))
{
@Text

<FluentIcon Value="@_icon" Color="@IconColor" Slot="end" />
}
</FluentButton>
}
<FluentButton Id="@MenuButtonId" Title="@Title" Appearance="@ButtonAppearance" IconStart="@IconStart" @onclick="ToggleMenu" Disabled="@(!Items.Where(i => !i.IsDivider).Any())" AdditionalAttributes="@additionalButtonAttributes" Class="@ButtonClass">
@if (string.IsNullOrWhiteSpace(Text))
{
<FluentIcon Value="@_icon" Color="@IconColor" />
}
else
{
@Text
<FluentIcon Value="@_icon" Color="@IconColor" Slot="end" />
}
</FluentButton>

<FluentMenu Anchor="@MenuButtonId" aria-labelledby="button" @bind-Open="@_visible" VerticalThreshold="200">
@foreach (var item in Items)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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; }

Expand All @@ -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()
{
Expand Down
4 changes: 2 additions & 2 deletions src/Aspire.Dashboard/Components/Pages/Resources.razor
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
@if (HasAnyChildResources())
{
<AspireMenuButton ButtonAppearance="Appearance.Stealth"
IconEnd="@(new Icons.Regular.Size20.Settings())"
Icon="@(new Icons.Regular.Size20.Settings())"
Items="@_resourcesMenuItems"
Title="@Loc[nameof(Dashboard.Resources.ConsoleLogs.ConsoleLogsSettings)]"
slot="end" />
Expand All @@ -53,7 +53,7 @@
{
@if (HasAnyChildResources())
{
var showExpandAllButton = _collapsedResourceNames.Count > 0;
var showExpandAllButton = HasCollapsedResources();

<FluentButton IconStart="@(showExpandAllButton ? new Icons.Regular.Size16.Eye() : new Icons.Regular.Size16.EyeOff())"
OnClick="@(async () => { await OnToggleCollapseAll(); _contentLayout?.CloseMobileToolbarAsync(); })">
Expand Down
45 changes: 25 additions & 20 deletions src/Aspire.Dashboard/Components/Pages/Resources.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand All @@ -71,7 +71,6 @@ public partial class Resources : ComponentBase, IAsyncDisposable
private readonly CancellationTokenSource _watchTaskCancellationTokenSource = new();
private readonly ConcurrentDictionary<string, ResourceViewModel> _resourceByName = new(StringComparers.ResourceName);
private readonly HashSet<string> _collapsedResourceNames = new(StringComparers.ResourceName);
private readonly HashSet<string> _initialCollapsedResourceNames = [];
private string _filter = "";
private bool _isFilterPopupVisible;
private Task? _resourceSubscriptionTask;
Expand Down Expand Up @@ -153,12 +152,12 @@ protected override async Task OnInitializedAsync()

if (DashboardClient.IsEnabled)
{
var settings = await LocalStorage.GetUnprotectedAsync<ResourcesPageSettings>(BrowserStorageKeys.ResourcesPageSettings);
if (settings.Value is not null)
var collapsedResult = await SessionStorage.GetAsync<List<string>>(BrowserStorageKeys.ResourcesCollapsedResourceNames);
if (collapsedResult.Success)
{
foreach (var resourceName in settings.Value.CollapsedResourceNames)
foreach (var resourceName in collapsedResult.Value)
{
_initialCollapsedResourceNames.Add(resourceName);
_collapsedResourceNames.Add(resourceName);
}
}

Expand Down Expand Up @@ -265,11 +264,6 @@ bool UpdateFromResource(ResourceViewModel resource, Func<string, bool> 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;
Expand Down Expand Up @@ -311,7 +305,7 @@ private void UpdateMenuButtons()
{
_resourcesMenuItems.Clear();

if (_collapsedResourceNames.Count > 0)
if (HasCollapsedResources())
{
_resourcesMenuItems.Add(new MenuButtonItem
{
Expand All @@ -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;
Expand Down Expand Up @@ -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();
}
Expand All @@ -547,6 +554,4 @@ public async ValueTask DisposeAsync()

await TaskHelpers.WaitIgnoreCancelAsync(_resourceSubscriptionTask);
}

public record ResourcesPageSettings(HashSet<string> CollapsedResourceNames);
}
16 changes: 15 additions & 1 deletion src/Aspire.Dashboard/Model/BrowserStorage/StorageResult.cs
Original file line number Diff line number Diff line change
@@ -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<TValue>(bool Success, TValue? Value);
public readonly struct StorageResult<TValue>
{
[MemberNotNullWhen(true, nameof(Value))]
public bool Success { get; }

public TValue? Value { get; }

public StorageResult(bool success, TValue? value)
{
Success = success;
Value = value;
}
}
5 changes: 3 additions & 2 deletions src/Aspire.Dashboard/Resources/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

60 changes: 30 additions & 30 deletions src/Aspire.Dashboard/Resources/Resources.resx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
Expand All @@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
Expand Down Expand Up @@ -125,10 +125,10 @@
<value>Resources</value>
</data>
<data name="ResourcesNotFiltered" xml:space="preserve">
<value>Add filters</value>
<value>No filters</value>
</data>
<data name="ResourcesFiltered" xml:space="preserve">
<value>Update filters</value>
<value>Has filters</value>
</data>
<data name="ResourcesResourceTypesHeader" xml:space="preserve">
<value>Resource types</value>
Expand Down Expand Up @@ -274,4 +274,4 @@
<data name="ResourceExpandAllChildren" xml:space="preserve">
<value>Expand child resources</value>
</data>
</root>
</root>
2 changes: 1 addition & 1 deletion src/Aspire.Dashboard/Utils/BrowserStorageKeys.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ public sealed class TestLocalStorage : ILocalStorage

public Task<StorageResult<T>> GetAsync<T>(string key)
{
return Task.FromResult(new StorageResult<T>(Success: false, Value: default));
return Task.FromResult(new StorageResult<T>(success: false, value: default));
}

public Task<StorageResult<T>> GetUnprotectedAsync<T>(string key)
{
if (OnGetUnprotectedAsync is { } callback)
{
var (success, value) = callback(key);
return Task.FromResult(new StorageResult<T>(Success: success, Value: (T)(value ?? default(T))!));
return Task.FromResult(new StorageResult<T>(success: success, value: (T)(value ?? default(T))!));
}
return Task.FromResult(new StorageResult<T>(Success: false, Value: default));
return Task.FromResult(new StorageResult<T>(success: false, value: default));
}

public Task SetAsync<T>(string key, T value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ public Task<StorageResult<T>> GetAsync<T>(string key)
if (OnGetAsync is { } callback)
{
var (success, value) = callback(key);
return Task.FromResult(new StorageResult<T>(Success: success, Value: (T)(value ?? default(T))!));
return Task.FromResult(new StorageResult<T>(success: success, value: (T)(value ?? default(T))!));
}

return Task.FromResult<StorageResult<T>>(new StorageResult<T>(Success: false, Value: default));
return Task.FromResult<StorageResult<T>>(new StorageResult<T>(success: false, value: default));
}

public Task SetAsync<T>(string key, T value)
Expand Down

0 comments on commit 0c0fa99

Please sign in to comment.