File: Components\Controls\DashboardRunMenu.razor.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 Aspire.Dashboard.Utils;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Localization;
using Microsoft.FluentUI.AspNetCore.Components;
using Icons = Microsoft.FluentUI.AspNetCore.Components.Icons;
using LayoutResources = Aspire.Dashboard.Resources.Layout;
 
namespace Aspire.Dashboard.Components.Controls;
 
/// <summary>
/// The run picker from before recordings had their own panel: a "Live run" dropdown that lists the runs in a popup
/// menu. It's shown instead of the recordings icon and panel when the "Recordings" setting is set to the menu.
/// </summary>
public partial class DashboardRunMenu : ComponentBase
{
    private const string DashboardRunsHelpUrl = "https://aka.ms/aspire/run-persistence";
 
    private static readonly Icon s_checkmarkIcon = new Icons.Regular.Size16.Checkmark();
    private static readonly Icon s_helpIcon = new Icons.Regular.Size16.QuestionCircle();
    private static readonly Icon s_pinIcon = new Icons.Regular.Size16.Pin();
    private static readonly Icon s_pinnedIcon = new Icons.Filled.Size16.Pin();
    private readonly string _runMenuItemIdPrefix = $"dashboard-run-{Guid.NewGuid():N}";
 
    private string RunSelectTitle => Loc[nameof(LayoutResources.DashboardRunMenuTitle)];
    private string RunSelectAccessibleLabel => Loc[nameof(LayoutResources.DashboardRunMenuAccessibleLabel), SelectedRunText];
    private string SelectedRunText => SelectedRunIsCurrent
        ? Loc[nameof(LayoutResources.DashboardRunMenuCurrent)]
        : FormatHelpers.FormatTimeWithOptionalDate(TimeProvider, SelectedRunStartedAtUtc.UtcDateTime);
 
    [Parameter, EditorRequired]
    public required string SelectedRunId { get; set; }
 
    [Parameter]
    public bool SelectedRunIsCurrent { get; set; }
 
    [Parameter]
    public DateTimeOffset SelectedRunStartedAtUtc { get; set; }
 
    [Parameter]
    public EventCallback<string?> SelectedRunIdChanged { get; set; }
 
    [Inject]
    public required IStringLocalizer<LayoutResources> Loc { get; init; }
 
    [Inject]
    public required BrowserTimeProvider TimeProvider { get; init; }
 
    [Inject]
    public required IDashboardRunStore RunStore { get; init; }
 
    [Inject]
    public required ILogger<DashboardRunMenu> Logger { get; init; }
 
    private IList<MenuButtonItem> LoadRuns()
    {
        var runs = DashboardRunSelect.GetSortedRuns(RunStore.GetRuns());
 
        var menuItems = new List<MenuButtonItem>();
        foreach (var run in runs)
        {
            var isCompatible = run.IsCompatible;
            var menuItem = new MenuButtonItem
            {
                Id = $"{_runMenuItemIdPrefix}-{Uri.EscapeDataString(run.RunId)}",
                RenderKey = run.RunId,
                Text = FormatRunOption(run),
                Role = MenuItemRole.Radio,
                Checked = string.Equals(run.RunId, SelectedRunId, StringComparison.Ordinal),
                Icon = s_checkmarkIcon,
                IsDisabled = !isCompatible,
                Tooltip = isCompatible ? null : Loc[nameof(LayoutResources.DashboardRunMenuIncompatibleTooltip)].Value,
                SecondaryActionIcon = run.IsPinned ? s_pinnedIcon : s_pinIcon,
                SecondaryActionAriaLabel = Loc[run.IsPinned
                    ? nameof(LayoutResources.DashboardRunMenuUnpin)
                    : nameof(LayoutResources.DashboardRunMenuPin)],
                IsSecondaryActionSelected = run.IsPinned,
                OnSecondaryActionClick = () =>
                {
                    SetRunPinned(run, !run.IsPinned);
                    return Task.CompletedTask;
                },
                OnClick = () => SelectedRunIdChanged.InvokeAsync(run.IsCurrent ? null : run.RunId)
            };
            menuItems.Add(menuItem);
 
            if (run.IsCurrent && runs.Count > 1)
            {
                menuItems.Add(new MenuButtonItem { IsDivider = true });
            }
        }
 
        menuItems.Add(new MenuButtonItem { IsDivider = true });
        menuItems.Add(MenuButtonItem.CreateExternalLink(
            Loc[nameof(LayoutResources.DashboardRunMenuHelp)],
            DashboardRunsHelpUrl,
            s_helpIcon,
            tooltip: Loc[nameof(LayoutResources.DashboardRunMenuHelpTooltip)]));
 
        return menuItems;
    }
 
    private void SetRunPinned(DashboardRunDescriptor run, bool isPinned)
    {
        try
        {
            RunStore.SetRunPinned(run, isPinned);
        }
        catch (Exception exception)
        {
            Logger.LogError(exception, "Failed to update the pinned state of dashboard run '{RunId}'.", run.RunId);
        }
    }
 
    private string FormatRunOption(DashboardRunDescriptor run)
    {
        if (run.IsCurrent)
        {
            return Loc[nameof(LayoutResources.DashboardRunMenuCurrent)];
        }
 
        return FormatHelpers.FormatTimeWithOptionalDate(TimeProvider, run.StartedAtUtc.UtcDateTime);
    }
}