Files
deepdrft/DeepDrftManager/Components/Pages/Tracks/BatchTrackList.razor
T
daniel-c-harvey c9c6286571 Fix large CMS upload timeout with idle heartbeat and add per-file progress meter
Replace the 100s default HttpClient timeout (set Timeout=Infinite) with an idle/heartbeat
deadline driven by a ProgressStreamContent wrapper that reports bytes-on-the-wire. Each tick
resets the idle window and advances a MudProgressLinear per upload row. Idle window is
configurable via Upload:IdleTimeoutSeconds (default 90s).
2026-06-17 11:07:19 -04:00

92 lines
4.6 KiB
Plaintext

@using Microsoft.AspNetCore.Components.Forms
<MudPaper Class="pa-4" Elevation="2">
<MudText Typo="Typo.h6" GutterBottom="true">Tracks</MudText>
@if (AllowNewTracks)
{
<InputFile OnChange="HandleWavFilesSelected" accept=".wav,audio/wav,audio/x-wav" multiple disabled="@Disabled" />
}
@if (Tracks.Count == 0)
{
<MudText Typo="Typo.body2" Color="Color.Default" Class="mt-3">No tracks added yet.</MudText>
}
else
{
<MudList T="BatchRowModel" Class="mt-3">
@for (var i = 0; i < Tracks.Count; i++)
{
var index = i;
var row = Tracks[index];
<div style="@RowStyle(index)" @onclick="() => SelectRow(index)">
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1" Class="pa-2">
<MudText Typo="Typo.body2" Style="min-width: 1.5rem;">@(index + 1).</MudText>
<MudText Typo="Typo.body2" Style="flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">@row.TrackName</MudText>
@StatusChip(row)
<MudIconButton Icon="@Icons.Material.Filled.ArrowUpward"
Size="Size.Small"
Disabled="@(index == 0 || Disabled)"
OnClick="@(() => OnMoveUp.InvokeAsync(index))"
aria-label="Move track up" />
<MudIconButton Icon="@Icons.Material.Filled.ArrowDownward"
Size="Size.Small"
Disabled="@(index == Tracks.Count - 1 || Disabled)"
OnClick="@(() => OnMoveDown.InvokeAsync(index))"
aria-label="Move track down" />
<MudIconButton Icon="@Icons.Material.Filled.Delete"
Color="Color.Error"
Size="Size.Small"
Disabled="@Disabled"
OnClick="@(() => OnRemove.InvokeAsync(index))"
aria-label="Remove track" />
</MudStack>
@if (row.Status == BatchRowStatus.Uploading)
{
<MudProgressLinear Color="Color.Info"
Value="@row.UploadPercent"
Class="mx-2 mb-2"
aria-label="@($"Uploading {row.TrackName}")" />
}
</div>
}
</MudList>
}
</MudPaper>
@code {
[Parameter] public List<BatchRowModel> Tracks { get; set; } = new();
[Parameter] public int SelectedIndex { get; set; }
[Parameter] public EventCallback<int> SelectedIndexChanged { get; set; }
[Parameter] public bool Disabled { get; set; }
[Parameter] public bool AllowNewTracks { get; set; } = true;
[Parameter] public EventCallback<IReadOnlyList<IBrowserFile>> OnWavFilesSelected { get; set; }
[Parameter] public EventCallback<int> OnMoveUp { get; set; }
[Parameter] public EventCallback<int> OnMoveDown { get; set; }
[Parameter] public EventCallback<int> OnRemove { get; set; }
private const int MaxFilesPerPick = 50;
private Task SelectRow(int index) => SelectedIndexChanged.InvokeAsync(index);
private Task HandleWavFilesSelected(InputFileChangeEventArgs e) =>
OnWavFilesSelected.InvokeAsync(e.GetMultipleFiles(MaxFilesPerPick));
private string RowStyle(int index)
{
const string baseStyle = "cursor: pointer; border-radius: 4px;";
return index == SelectedIndex
? $"{baseStyle} background-color: var(--mud-palette-action-default-hover);"
: baseStyle;
}
private RenderFragment StatusChip(BatchRowModel row) => row.Status switch
{
BatchRowStatus.Uploading => @<MudChip T="string" Size="Size.Small" Color="Color.Info" Variant="Variant.Text">
<MudProgressCircular Indeterminate="true" Size="Size.Small" Class="mr-1" />Uploading</MudChip>,
BatchRowStatus.Done => @<MudChip T="string" Size="Size.Small" Color="Color.Success" Variant="Variant.Text" Icon="@Icons.Material.Filled.CheckCircle">Done</MudChip>,
BatchRowStatus.Failed => @<MudChip T="string" Size="Size.Small" Color="Color.Error" Variant="Variant.Text" Icon="@Icons.Material.Filled.Error">Failed</MudChip>,
_ => @<MudChip T="string" Size="Size.Small" Color="Color.Default" Variant="Variant.Text">Queued</MudChip>
};
}