mirror of
https://github.com/lantean-code/qbtmud.git
synced 2025-10-23 04:52:22 +00:00
Add better error handling, update toolbars and simplify logging
This commit is contained in:
@@ -9,4 +9,4 @@
|
||||
<p role="alert">Sorry, there's nothing at this address.</p>
|
||||
</LayoutView>
|
||||
</NotFound>
|
||||
</Router>
|
||||
</Router>
|
@@ -6,7 +6,7 @@
|
||||
<ButtonTemplate>
|
||||
<MudButton HtmlTag="label"
|
||||
Variant="Variant.Filled"
|
||||
Color="Color.Secondary"
|
||||
Color="Color.Primary"
|
||||
StartIcon="@Icons.Material.Filled.CloudUpload"
|
||||
for="@context.Id">
|
||||
Choose files
|
||||
@@ -14,8 +14,8 @@
|
||||
</ButtonTemplate>
|
||||
</MudFileUpload>
|
||||
</MudItem>
|
||||
<AddTorrentOptions @ref="TorrentOptions" ShowCookieOption />
|
||||
</MudGrid>
|
||||
<AddTorrentOptions @ref="TorrentOptions" ShowCookieOption="true" />
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<MudButton OnClick="Cancel">Close</MudButton>
|
||||
|
@@ -4,8 +4,8 @@
|
||||
<MudItem xs="12">
|
||||
<MudTextField Label="Urls" Lines="10" @bind-Value="Urls" Variant="Variant.Outlined" />
|
||||
</MudItem>
|
||||
<AddTorrentOptions @ref="TorrentOptions" ShowCookieOption />
|
||||
</MudGrid>
|
||||
<AddTorrentOptions @ref="TorrentOptions" ShowCookieOption="false" />
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<MudButton OnClick="Cancel">Close</MudButton>
|
||||
|
@@ -1,62 +1,68 @@
|
||||
<MudItem xs="12">
|
||||
<MudSelect Label="Torrent Management Mode" @bind-Value="TorrentManagementMode" Variant="Variant.Outlined">
|
||||
<MudSelectItem Value="false">Manual</MudSelectItem>
|
||||
<MudSelectItem Value="true">Automatic</MudSelectItem>
|
||||
</MudSelect>
|
||||
</MudItem>
|
||||
<MudItem xs="12">
|
||||
<MudTextField Label="Save files to location" @bind-Value="SavePath" Variant="Variant.Outlined"></MudTextField>
|
||||
</MudItem>
|
||||
@if (ShowCookieOption)
|
||||
{
|
||||
<MudGrid>
|
||||
<MudItem xs="12">
|
||||
<MudTextField Label="Cookie" @bind-Value="Cookie" Variant="Variant.Outlined"></MudTextField>
|
||||
<MudSwitch Label="Additional Options" @bind-Value="Expanded" LabelPosition="LabelPosition.End" />
|
||||
</MudItem>
|
||||
}
|
||||
<MudItem xs="12">
|
||||
<MudTextField Label="Rename" @bind-Value="RenameTorrent" Variant="Variant.Outlined"></MudTextField>
|
||||
</MudItem>
|
||||
<MudItem xs="12">
|
||||
<MudSelect Label="Category" @bind-Value="Category" Variant="Variant.Outlined">
|
||||
@foreach (var category in Categories)
|
||||
</MudGrid>
|
||||
<MudCollapse Expanded="Expanded">
|
||||
<MudGrid>
|
||||
<MudItem xs="12">
|
||||
<MudSelect Label="Torrent Management Mode" @bind-Value="TorrentManagementMode" Variant="Variant.Outlined">
|
||||
<MudSelectItem Value="false">Manual</MudSelectItem>
|
||||
<MudSelectItem Value="true">Automatic</MudSelectItem>
|
||||
</MudSelect>
|
||||
</MudItem>
|
||||
<MudItem xs="12">
|
||||
<MudTextField Label="Save files to location" @bind-Value="SavePath" Variant="Variant.Outlined"></MudTextField>
|
||||
</MudItem>
|
||||
@if (ShowCookieOption)
|
||||
{
|
||||
<MudSelectItem Value="category">@category</MudSelectItem>
|
||||
<MudItem xs="12">
|
||||
<MudTextField Label="Cookie" @bind-Value="Cookie" Variant="Variant.Outlined"></MudTextField>
|
||||
</MudItem>
|
||||
}
|
||||
</MudSelect>
|
||||
</MudItem>
|
||||
<MudItem xs="12">
|
||||
<MudFieldSwitch Label="Start torrent" @bind-Value="StartTorrent" />
|
||||
</MudItem>
|
||||
<MudItem xs="12">
|
||||
<MudFieldSwitch Label="Add to top of queue" @bind-Value="AddToTopOfQueue" />
|
||||
</MudItem>
|
||||
<MudItem xs="12">
|
||||
<MudSelect Label="Stop condition" @bind-Value="StopCondition" Variant="Variant.Outlined">
|
||||
<MudSelectItem Value="@("None")">None</MudSelectItem>
|
||||
<MudSelectItem Value="@("MetadataReceived")">Metadata received</MudSelectItem>
|
||||
<MudSelectItem Value="@("FilesChecked")">Files checked</MudSelectItem>
|
||||
</MudSelect>
|
||||
</MudItem>
|
||||
<MudItem xs="12">
|
||||
<MudFieldSwitch Label="Add to top of queue" @bind-Value="AddToTopOfQueue" />
|
||||
</MudItem>
|
||||
<MudItem xs="12">
|
||||
<MudFieldSwitch Label="Skip hash check" @bind-Value="SkipHashCheck" />
|
||||
</MudItem>
|
||||
<MudSelect Label="Content layout" @bind-Value="ContentLayout" Variant="Variant.Outlined">
|
||||
<MudSelectItem Value="@("Original")">Original</MudSelectItem>
|
||||
<MudSelectItem Value="@("Subfolder")">Create subfolder</MudSelectItem>
|
||||
<MudSelectItem Value="@("NoSubfolder")">Don't create subfolder'</MudSelectItem>
|
||||
</MudSelect>
|
||||
<MudItem xs="12">
|
||||
<MudFieldSwitch Label="Download in sequentual order" @bind-Value="DownloadInSequentialOrder" />
|
||||
</MudItem>
|
||||
<MudItem xs="12">
|
||||
<MudFieldSwitch Label="Download first and last pieces first" @bind-Value="DownloadFirstAndLastPiecesFirst" />
|
||||
</MudItem>
|
||||
<MudItem xs="12">
|
||||
<MudNumericField Label="Limit download rate" @bind-Value="DownloadLimit" Variant="Variant.Outlined" Min="0" />
|
||||
</MudItem>
|
||||
<MudItem xs="12">
|
||||
<MudNumericField Label="Limit upload rate" @bind-Value="UploadLimit" Variant="Variant.Outlined" Min="0" />
|
||||
</MudItem>
|
||||
<MudItem xs="12">
|
||||
<MudTextField Label="Rename" @bind-Value="RenameTorrent" Variant="Variant.Outlined"></MudTextField>
|
||||
</MudItem>
|
||||
<MudItem xs="12">
|
||||
<MudSelect Label="Category" @bind-Value="Category" Variant="Variant.Outlined">
|
||||
@foreach (var category in Categories)
|
||||
{
|
||||
<MudSelectItem Value="category">@category</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
</MudItem>
|
||||
<MudItem xs="12">
|
||||
<MudFieldSwitch Label="Start torrent" @bind-Value="StartTorrent" />
|
||||
</MudItem>
|
||||
<MudItem xs="12">
|
||||
<MudFieldSwitch Label="Add to top of queue" @bind-Value="AddToTopOfQueue" />
|
||||
</MudItem>
|
||||
<MudItem xs="12">
|
||||
<MudSelect Label="Stop condition" @bind-Value="StopCondition" Variant="Variant.Outlined">
|
||||
<MudSelectItem Value="@("None")">None</MudSelectItem>
|
||||
<MudSelectItem Value="@("MetadataReceived")">Metadata received</MudSelectItem>
|
||||
<MudSelectItem Value="@("FilesChecked")">Files checked</MudSelectItem>
|
||||
</MudSelect>
|
||||
</MudItem>
|
||||
<MudItem xs="12">
|
||||
<MudFieldSwitch Label="Skip hash check" @bind-Value="SkipHashCheck" />
|
||||
</MudItem>
|
||||
<MudSelect Label="Content layout" @bind-Value="ContentLayout" Variant="Variant.Outlined">
|
||||
<MudSelectItem Value="@("Original")">Original</MudSelectItem>
|
||||
<MudSelectItem Value="@("Subfolder")">Create subfolder</MudSelectItem>
|
||||
<MudSelectItem Value="@("NoSubfolder")">Don't create subfolder'</MudSelectItem>
|
||||
</MudSelect>
|
||||
<MudItem xs="12">
|
||||
<MudFieldSwitch Label="Download in sequentual order" @bind-Value="DownloadInSequentialOrder" />
|
||||
</MudItem>
|
||||
<MudItem xs="12">
|
||||
<MudFieldSwitch Label="Download first and last pieces first" @bind-Value="DownloadFirstAndLastPiecesFirst" />
|
||||
</MudItem>
|
||||
<MudItem xs="12">
|
||||
<MudNumericField Label="Limit download rate" @bind-Value="DownloadLimit" Variant="Variant.Outlined" Min="0" />
|
||||
</MudItem>
|
||||
<MudItem xs="12">
|
||||
<MudNumericField Label="Limit upload rate" @bind-Value="UploadLimit" Variant="Variant.Outlined" Min="0" />
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
</MudCollapse>
|
||||
|
@@ -12,6 +12,8 @@ namespace Lantean.QBTMudBlade.Components.Dialogs
|
||||
[Parameter]
|
||||
public bool ShowCookieOption { get; set; }
|
||||
|
||||
protected bool Expanded { get; set; }
|
||||
|
||||
protected bool TorrentManagementMode { get; set; }
|
||||
|
||||
protected string SavePath { get; set; } = default!;
|
||||
|
34
Lantean.QBTMudBlade/Components/Dialogs/ExceptionDialog.razor
Normal file
34
Lantean.QBTMudBlade/Components/Dialogs/ExceptionDialog.razor
Normal file
@@ -0,0 +1,34 @@
|
||||
<MudDialog>
|
||||
<DialogContent>
|
||||
<MudGrid>
|
||||
@if (Exception is null)
|
||||
{
|
||||
<MudItem xs="12">
|
||||
<MudAlert Severity="Severity.Error">
|
||||
Missing error information.
|
||||
</MudAlert>
|
||||
</MudItem>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudItem xs="12">
|
||||
<MudField Label="Message">@Exception.Message</MudField>
|
||||
</MudItem>
|
||||
<MudItem xs="12">
|
||||
<MudField Label="Source">@Exception.Source</MudField>
|
||||
</MudItem>
|
||||
<MudItem xs="12">
|
||||
<MudField Label="Stack Trace">
|
||||
<pre class="overflow">
|
||||
@Exception.StackTrace
|
||||
</pre>
|
||||
</MudField>
|
||||
</MudItem>
|
||||
}
|
||||
</MudGrid>
|
||||
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<MudButton OnClick="Close">Close</MudButton>
|
||||
</DialogActions>
|
||||
</MudDialog>
|
@@ -0,0 +1,20 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using MudBlazor;
|
||||
|
||||
namespace Lantean.QBTMudBlade.Components.Dialogs
|
||||
{
|
||||
public partial class ExceptionDialog
|
||||
{
|
||||
[CascadingParameter]
|
||||
public MudDialogInstance MudDialog { get; set; } = default!;
|
||||
|
||||
[Parameter]
|
||||
public Exception? Exception { get; set; }
|
||||
|
||||
protected void Close(MouseEventArgs args)
|
||||
{
|
||||
MudDialog.Cancel();
|
||||
}
|
||||
}
|
||||
}
|
73
Lantean.QBTMudBlade/Components/EnhancedErrorBoundary.cs
Normal file
73
Lantean.QBTMudBlade/Components/EnhancedErrorBoundary.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Rendering;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Lantean.QBTMudBlade.Components
|
||||
{
|
||||
public class EnhancedErrorBoundary : ErrorBoundaryBase
|
||||
{
|
||||
private readonly ObservableCollection<Exception> _exceptions = [];
|
||||
|
||||
public bool HasErrored => CurrentException != null;
|
||||
|
||||
[Parameter]
|
||||
public EventCallback OnClear { get; set; }
|
||||
|
||||
protected override Task OnErrorAsync(Exception exception)
|
||||
{
|
||||
_exceptions.Add(exception);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task RecoverAndClearErrors()
|
||||
{
|
||||
Recover();
|
||||
_exceptions.Clear();
|
||||
|
||||
await OnClear.InvokeAsync();
|
||||
}
|
||||
|
||||
public async Task ClearErrors()
|
||||
{
|
||||
_exceptions.Clear();
|
||||
await OnClear.InvokeAsync();
|
||||
}
|
||||
|
||||
public IReadOnlyList<Exception> Errors => _exceptions.AsReadOnly();
|
||||
|
||||
protected override void BuildRenderTree(RenderTreeBuilder builder)
|
||||
{
|
||||
builder.AddContent(0, ChildContent);
|
||||
//builder.OpenComponent<CascadingValue<EnhancedErrorBoundary>>(0);
|
||||
//builder.AddAttribute(1, "Value", this);
|
||||
//builder.AddContent(2, ChildContent);
|
||||
//builder.CloseComponent();
|
||||
// if (CurrentException is null)
|
||||
// {
|
||||
// builder.AddContent(0, ChildContent);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// if (ErrorContent is not null)
|
||||
// {
|
||||
// builder.AddContent(1, ErrorContent(CurrentException));
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// builder.OpenElement(2, "div");
|
||||
// builder.AddContent(3, "Blazor School Custom Error Boundary.");
|
||||
// builder.AddContent(4, __innerBuilder =>
|
||||
// {
|
||||
// __innerBuilder.OpenElement(5, "button");
|
||||
// __innerBuilder.AddAttribute(6, "type", "button");
|
||||
// __innerBuilder.AddAttribute(7, "onclick", RecoverAndClearErrors);
|
||||
// __innerBuilder.AddContent(8, "Continue");
|
||||
// __innerBuilder.CloseElement();
|
||||
// });
|
||||
// builder.CloseElement();
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
9
Lantean.QBTMudBlade/Components/ErrorDisplay.razor
Normal file
9
Lantean.QBTMudBlade/Components/ErrorDisplay.razor
Normal file
@@ -0,0 +1,9 @@
|
||||
<MudList Clickable="true">
|
||||
<MudListItem OnClick="ClearErrors">Clear Errors</MudListItem>
|
||||
<MudListItem OnClick="ClearErrorsAndResumeAsync">Clear Errors and Resume</MudListItem>
|
||||
<MudDivider />
|
||||
@foreach (var error in Errors)
|
||||
{
|
||||
<MudListItem OnClick="@(e => ShowException(error))">@error.Message</MudListItem>
|
||||
}
|
||||
</MudList>
|
38
Lantean.QBTMudBlade/Components/ErrorDisplay.razor.cs
Normal file
38
Lantean.QBTMudBlade/Components/ErrorDisplay.razor.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using Lantean.QBTMudBlade.Components.Dialogs;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MudBlazor;
|
||||
|
||||
namespace Lantean.QBTMudBlade.Components
|
||||
{
|
||||
public partial class ErrorDisplay
|
||||
{
|
||||
[Inject]
|
||||
protected IDialogService DialogService { get; set; } = default!;
|
||||
|
||||
[Parameter]
|
||||
[EditorRequired]
|
||||
public EnhancedErrorBoundary ErrorBoundary { get; set; } = default!;
|
||||
|
||||
protected IEnumerable<Exception> Errors => ErrorBoundary.Errors;
|
||||
|
||||
protected async Task ShowException(Exception exception)
|
||||
{
|
||||
var parameters = new DialogParameters
|
||||
{
|
||||
{ nameof(ExceptionDialog.Exception), exception }
|
||||
};
|
||||
|
||||
await DialogService.ShowAsync<ExceptionDialog>("Error Details", parameters, DialogHelper.FormDialogOptions);
|
||||
}
|
||||
|
||||
protected async Task ClearErrors()
|
||||
{
|
||||
await ErrorBoundary.ClearErrors();
|
||||
}
|
||||
|
||||
protected async Task ClearErrorsAndResumeAsync()
|
||||
{
|
||||
await ErrorBoundary.RecoverAndClearErrors();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,4 +1,24 @@
|
||||
<MudTable T="ContentItem" Hover="true" FixedHeader="true" HeaderClass="table-head-bordered" Breakpoint="Breakpoint.None" Bordered="false"
|
||||
<MudToolBar DisableGutters="true" Dense="true">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.DriveFileRenameOutline" OnClick="RenameFile" Title="Rename" />
|
||||
<MudDivider Vertical="true" />
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.ViewColumn" Color="Color.Inherit" OnClick="ColumnOptions" Title="Choose Columns" />
|
||||
<MudDivider Vertical="true" />
|
||||
<MudMenu Icon="@Icons.Material.Outlined.FileDownloadOff" Label="Do Not Download" AnchorOrigin="Origin.BottomLeft" TransformOrigin="Origin.TopLeft" title="Do Not Download">
|
||||
<MudMenuItem OnClick="DoNotDownloadLessThan100PercentAvailability" OnTouch="DoNotDownloadLessThan100PercentAvailability">Less Than 100% Availability</MudMenuItem>
|
||||
<MudMenuItem OnClick="DoNotDownloadLessThan80PercentAvailability" OnTouch="DoNotDownloadLessThan80PercentAvailability">Less than 80% Availability</MudMenuItem>
|
||||
<MudMenuItem OnClick="DoNotDownloadCurrentlyFilteredFiles" OnTouch="NormalPriorityCurrentlyFilteredFiles">Currently Filtered Files</MudMenuItem>
|
||||
</MudMenu>
|
||||
<MudMenu Icon="@Icons.Material.Outlined.FileDownload" Label="Normal Priority" AnchorOrigin="Origin.BottomLeft" TransformOrigin="Origin.TopLeft" title="Download">
|
||||
<MudMenuItem OnClick="NormalPriorityLessThan100PercentAvailability" OnTouch="NormalPriorityLessThan100PercentAvailability">Less Than 100% Availability</MudMenuItem>
|
||||
<MudMenuItem OnClick="NormalPriorityLessThan80PercentAvailability" OnTouch="NormalPriorityLessThan80PercentAvailability">Less than 80% Availability</MudMenuItem>
|
||||
<MudMenuItem OnClick="NormalPriorityCurrentlyFilteredFiles" OnTouch="NormalPriorityCurrentlyFilteredFiles">Currently Filtered Files</MudMenuItem>
|
||||
</MudMenu>
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.FilterList" OnClick="ShowFilterDialog" Title="Filter" />
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.FilterListOff" OnClick="RemoveFilter" Title="Remove Filter" />
|
||||
<MudSpacer />
|
||||
<MudTextField T="string" Value="SearchText" ValueChanged="SearchTextChanged" Immediate="true" DebounceInterval="500" Placeholder="Filter file list" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0"></MudTextField>
|
||||
</MudToolBar>
|
||||
<MudTable T="ContentItem" Hover="true" FixedHeader="true" HeaderClass="table-head-bordered" Breakpoint="Breakpoint.None" Bordered="false"
|
||||
MultiSelection="true" Dense="true" SelectOnRowClick="false"
|
||||
Items="Files"
|
||||
SelectedItems="SelectedItems"
|
||||
@@ -7,28 +27,6 @@
|
||||
RowStyleFunc="RowStyle"
|
||||
RowClassFunc="RowClass"
|
||||
AllowUnsorted="false">
|
||||
<ToolBarContent>
|
||||
<MudToolBar DisableGutters="true" Dense="true">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.DriveFileRenameOutline" OnClick="RenameFile" Title="Rename" />
|
||||
<MudDivider Vertical="true" />
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.ViewColumn" Color="Color.Inherit" OnClick="ColumnOptions" Title="Choose Columns" />
|
||||
<MudDivider Vertical="true" />
|
||||
<MudMenu Icon="@Icons.Material.Outlined.FileDownloadOff" Label="Do Not Download" AnchorOrigin="Origin.BottomLeft" TransformOrigin="Origin.TopLeft" title="Do Not Download">
|
||||
<MudMenuItem OnClick="DoNotDownloadLessThan100PercentAvailability" OnTouch="DoNotDownloadLessThan100PercentAvailability">Less Than 100% Availability</MudMenuItem>
|
||||
<MudMenuItem OnClick="DoNotDownloadLessThan80PercentAvailability" OnTouch="DoNotDownloadLessThan80PercentAvailability">Less than 80% Availability</MudMenuItem>
|
||||
<MudMenuItem OnClick="DoNotDownloadCurrentlyFilteredFiles" OnTouch="NormalPriorityCurrentlyFilteredFiles">Currently Filtered Files</MudMenuItem>
|
||||
</MudMenu>
|
||||
<MudMenu Icon="@Icons.Material.Outlined.FileDownload" Label="Normal Priority" AnchorOrigin="Origin.BottomLeft" TransformOrigin="Origin.TopLeft" title="Download">
|
||||
<MudMenuItem OnClick="NormalPriorityLessThan100PercentAvailability" OnTouch="NormalPriorityLessThan100PercentAvailability">Less Than 100% Availability</MudMenuItem>
|
||||
<MudMenuItem OnClick="NormalPriorityLessThan80PercentAvailability" OnTouch="NormalPriorityLessThan80PercentAvailability">Less than 80% Availability</MudMenuItem>
|
||||
<MudMenuItem OnClick="NormalPriorityCurrentlyFilteredFiles" OnTouch="NormalPriorityCurrentlyFilteredFiles">Currently Filtered Files</MudMenuItem>
|
||||
</MudMenu>
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.FilterList" OnClick="ShowFilterDialog" Title="Filter" />
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.FilterListOff" OnClick="RemoveFilter" Title="Remove Filter" />
|
||||
</MudToolBar>
|
||||
<MudSpacer />
|
||||
<MudTextField T="string" Value="SearchText" ValueChanged="SearchTextChanged" Immediate="true" DebounceInterval="500" Placeholder="Filter file list" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0"></MudTextField>
|
||||
</ToolBarContent>
|
||||
<ColGroup>
|
||||
<col style="width: 30px" />
|
||||
@foreach (var column in GetColumns())
|
||||
@@ -73,7 +71,7 @@
|
||||
@if (context.Data.IsFolder)
|
||||
{
|
||||
<MudIconButton ButtonType="ButtonType.Button" Icon="@Icons.Material.Filled.ExpandLess" Class="@("pa-0 " + (ExpandedNodes.Contains(context.Data.Name) ? "rotate-180" : "rotate-90"))" OnClick="@(c => ToggleNode(context.Data, c))"></MudIconButton>
|
||||
<MudIcon Icon="@Icons.Material.Filled.Folder" Class="pt-2" Style="margin-right: 2px" />
|
||||
<MudIcon Icon="@Icons.Material.Filled.Folder" Class="pt-2" Style="margin-right: 4px; position: relative; top: 3px" />
|
||||
}
|
||||
@context.Data.DisplayName
|
||||
</div>;
|
||||
|
@@ -531,7 +531,7 @@ namespace Lantean.QBTMudBlade.Components
|
||||
return;
|
||||
}
|
||||
|
||||
var files = FileList.Values.Where(f => f.Availability < value).Select(f => f.Index);
|
||||
var files = FileList.Values.Where(f => !f.IsFolder && f.Availability < value).Select(f => f.Index);
|
||||
|
||||
if (!files.Any())
|
||||
{
|
||||
@@ -548,7 +548,7 @@ namespace Lantean.QBTMudBlade.Components
|
||||
return;
|
||||
}
|
||||
|
||||
var files = GetFiles().Select(f => f.Index);
|
||||
var files = GetFiles().Where(f => !f.IsFolder).Select(f => f.Index);
|
||||
|
||||
if (!files.Any())
|
||||
{
|
||||
|
@@ -1,24 +1,24 @@
|
||||
@if (Type == ParentType.Toolbar)
|
||||
@if (Type == RenderType.Toolbar)
|
||||
{
|
||||
<MudToolBar Dense="true" DisableGutters="true" WrapContent="true">
|
||||
@ToolbarContent
|
||||
</MudToolBar>
|
||||
}
|
||||
else if (Type == ParentType.ToolbarContents)
|
||||
else if (Type == RenderType.ToolbarContents)
|
||||
{
|
||||
@ToolbarContent
|
||||
}
|
||||
else if (Type == ParentType.MixedToolbar)
|
||||
else if (Type == RenderType.MixedToolbar)
|
||||
{
|
||||
<MudToolBar Dense="true" DisableGutters="true" WrapContent="true">
|
||||
@MixedToolbarContent
|
||||
</MudToolBar>
|
||||
}
|
||||
else if (Type == ParentType.MixedToolbarContents)
|
||||
else if (Type == RenderType.MixedToolbarContents)
|
||||
{
|
||||
@MixedToolbarContent
|
||||
}
|
||||
else if (Type == ParentType.InitialIconsOnly)
|
||||
else if (Type == RenderType.InitialIconsOnly)
|
||||
{
|
||||
@foreach (var option in GetOptions().Take(5))
|
||||
{
|
||||
@@ -28,151 +28,143 @@ else if (Type == ParentType.InitialIconsOnly)
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudIconButton Title="@option.Name" Icon="@option.Icon" Color="option.Color" OnClick="option.Callback" />
|
||||
<MudIconButton Title="@option.Name" Icon="@option.Icon" Color="option.Color" OnClick="option.Callback" Disabled="Disabled" />
|
||||
}
|
||||
}
|
||||
|
||||
<MudMenu Dense="true" AnchorOrigin="Origin.BottomLeft" TransformOrigin="Origin.TopLeft" Label="Actions" EndIcon="@Icons.Material.Filled.ArrowDropDown" @ref="ActionsMenu">
|
||||
@foreach (var option in GetOptions().Skip(5))
|
||||
{
|
||||
@if (option is Divider)
|
||||
{
|
||||
<MudDivider />
|
||||
}
|
||||
else if (!option.Children.Any())
|
||||
{
|
||||
<MudMenuItem Icon="@option.Icon" IconColor="option.Color" OnClick="option.Callback" OnTouch="option.Callback">
|
||||
@option.Name
|
||||
</MudMenuItem>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudMenuItem Icon="@option.Icon" IconColor="option.Color">
|
||||
<MudMenu Dense="true" AnchorOrigin="Origin.TopRight" TransformOrigin="Origin.TopLeft" ActivationEvent="MouseEvent.MouseOver" Icon="@Icons.Material.Filled.ArrowDropDown" DisableElevation="true" DisableRipple="true" Class="sub-menu">
|
||||
<ActivatorContent>
|
||||
@option.Name
|
||||
</ActivatorContent>
|
||||
|
||||
<ChildContent>
|
||||
@foreach (var childItem in option.Children)
|
||||
{
|
||||
@ChildItem(childItem)
|
||||
}
|
||||
</ChildContent>
|
||||
</MudMenu>
|
||||
</MudMenuItem>
|
||||
}
|
||||
|
||||
}
|
||||
</MudMenu>
|
||||
@Menu(GetOptions().Skip(5));
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudMenu AnchorOrigin="Origin.BottomLeft" TransformOrigin="Origin.TopLeft">
|
||||
@foreach (var option in GetOptions())
|
||||
{
|
||||
<MudMenuItem Icon="@option.Icon" IconColor="option.Color" OnClick="option.Callback" OnTouch="option.Callback">
|
||||
@if (option is Divider)
|
||||
{
|
||||
<MudDivider />
|
||||
}
|
||||
else if (!option.Children.Any())
|
||||
{
|
||||
@option.Name
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudMenu Dense="true" AnchorOrigin="Origin.BottomLeft" TransformOrigin="Origin.TopLeft" ActivationEvent="MouseEvent.LeftClick" Icon="@Icons.Material.Filled.ArrowDropDown" DisableElevation="true" DisableRipple="true" Class="sub-menu">
|
||||
<ActivatorContent>
|
||||
@option.Name
|
||||
</ActivatorContent>
|
||||
@Menu(GetOptions());
|
||||
}
|
||||
|
||||
<ChildContent>
|
||||
@code {
|
||||
private RenderFragment ToolbarContent
|
||||
{
|
||||
get
|
||||
{
|
||||
return __builder =>
|
||||
{
|
||||
foreach (var option in GetOptions())
|
||||
{
|
||||
if (option is Divider)
|
||||
{
|
||||
<MudDivider Vertical="true" />
|
||||
}
|
||||
else if (!option.Children.Any())
|
||||
{
|
||||
if (option.Icon is null)
|
||||
{
|
||||
<MudButton Color="option.Color" OnClick="option.Callback">@option.Name</MudButton>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudIconButton Title="@option.Name" Icon="@option.Icon" Color="option.Color" OnClick="option.Callback" Disabled="Disabled" />
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudMenu Icon="@option.Icon" IconColor="@option.Color" Label="@option.Name" title="@option.Name" AnchorOrigin="Origin.BottomLeft" TransformOrigin="Origin.TopLeft">
|
||||
@foreach (var childItem in option.Children)
|
||||
{
|
||||
@ChildItem(childItem)
|
||||
}
|
||||
</ChildContent>
|
||||
</MudMenu>
|
||||
</MudMenu>
|
||||
}
|
||||
}
|
||||
</MudMenuItem>
|
||||
};
|
||||
}
|
||||
</MudMenu>
|
||||
}
|
||||
}
|
||||
|
||||
@code {
|
||||
private RenderFragment ToolbarContent =>
|
||||
@<NonRendering>
|
||||
@foreach (var option in GetOptions())
|
||||
private RenderFragment MixedToolbarContent
|
||||
{
|
||||
get
|
||||
{
|
||||
@if (option is Divider)
|
||||
return __builder =>
|
||||
{
|
||||
<MudDivider Vertical="true" />
|
||||
}
|
||||
else if (!option.Children.Any())
|
||||
foreach (var option in GetOptions())
|
||||
{
|
||||
if (option is Divider)
|
||||
{
|
||||
<MudDivider Vertical="true" />
|
||||
}
|
||||
else if (!option.Children.Any())
|
||||
{
|
||||
if (option.Icon is null)
|
||||
{
|
||||
<MudButton Color="option.Color" OnClick="option.Callback" Disabled="Disabled">@option.Name</MudButton>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudIconButton Title="@option.Name" Icon="@option.Icon" Color="option.Color" OnClick="option.Callback" Disabled="Disabled" />
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudMenu Label="@option.Name" title="@option.Name" AnchorOrigin="Origin.BottomLeft" TransformOrigin="Origin.TopLeft" EndIcon="@Icons.Material.Filled.ArrowDropDown">
|
||||
@foreach (var childItem in option.Children)
|
||||
{
|
||||
@ChildItem(childItem)
|
||||
}
|
||||
</MudMenu>
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private RenderFragment ChildItem(Action option)
|
||||
{
|
||||
return __builder =>
|
||||
{
|
||||
if (option is Divider)
|
||||
{
|
||||
if (option.Icon is null)
|
||||
{
|
||||
<MudButton Color="option.Color" OnClick="option.Callback">@option.Name</MudButton>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudIconButton Title="@option.Name" Icon="@option.Icon" Color="option.Color" OnClick="option.Callback" />
|
||||
}
|
||||
<MudDivider />
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudMenu Icon="@option.Icon" IconColor="@option.Color" Label="@option.Name" title="@option.Name" AnchorOrigin="Origin.BottomLeft" TransformOrigin="Origin.TopLeft">
|
||||
@foreach (var childItem in option.Children)
|
||||
{
|
||||
@ChildItem(childItem)
|
||||
}
|
||||
</MudMenu>
|
||||
<MudMenuItem Icon="@option.Icon" IconColor="option.Color" OnClick="option.Callback" OnTouch="option.Callback" Disabled="Disabled">@option.Name</MudMenuItem>
|
||||
}
|
||||
}
|
||||
</NonRendering>
|
||||
;
|
||||
};
|
||||
}
|
||||
|
||||
private RenderFragment MixedToolbarContent =>
|
||||
@<NonRendering>
|
||||
@foreach (var option in GetOptions())
|
||||
private RenderFragment Menu(IEnumerable<Action> actions)
|
||||
{
|
||||
return __builder =>
|
||||
{
|
||||
@if (option is Divider)
|
||||
{
|
||||
<MudDivider Vertical="true" />
|
||||
}
|
||||
else if (!option.Children.Any())
|
||||
{
|
||||
if (option.Icon is null)
|
||||
<MudMenu Dense="true" AnchorOrigin="Origin.BottomLeft" TransformOrigin="Origin.TopLeft" Label="Actions" EndIcon="@Icons.Material.Filled.ArrowDropDown" @ref="ActionsMenu" Disabled="@(!Hashes.Any())">
|
||||
@foreach (var option in actions)
|
||||
{
|
||||
<MudButton Color="option.Color" OnClick="option.Callback">@option.Name</MudButton>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudIconButton Title="@option.Name" Icon="@option.Icon" Color="option.Color" OnClick="option.Callback" />
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudMenu Label="@option.Name" title="@option.Name" AnchorOrigin="Origin.BottomLeft" TransformOrigin="Origin.TopLeft" EndIcon="@Icons.Material.Filled.ArrowDropDown">
|
||||
@foreach (var childItem in option.Children)
|
||||
@if (option is Divider)
|
||||
{
|
||||
@ChildItem(childItem)
|
||||
<MudDivider />
|
||||
}
|
||||
</MudMenu>
|
||||
}
|
||||
}
|
||||
</NonRendering>;
|
||||
else if (!option.Children.Any())
|
||||
{
|
||||
<MudMenuItem Icon="@option.Icon" IconColor="option.Color" OnClick="option.Callback" OnTouch="option.Callback" Disabled="Disabled">
|
||||
@option.Name
|
||||
</MudMenuItem>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudMenuItem Icon="@option.Icon" IconColor="option.Color">
|
||||
<MudMenu Dense="true" AnchorOrigin="Origin.TopRight" TransformOrigin="Origin.TopLeft" ActivationEvent="MouseEvent.MouseOver" Icon="@Icons.Material.Filled.ArrowDropDown" DisableElevation="true" DisableRipple="true" Class="sub-menu">
|
||||
<ActivatorContent>
|
||||
@option.Name
|
||||
</ActivatorContent>
|
||||
|
||||
private RenderFragment ChildItem(Action option) =>
|
||||
@<NonRendering>
|
||||
@if (option is Divider)
|
||||
{
|
||||
<MudDivider />
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudMenuItem Icon="@option.Icon" IconColor="option.Color" OnClick="option.Callback" OnTouch="option.Callback">@option.Name</MudMenuItem>
|
||||
}
|
||||
</NonRendering>;
|
||||
<ChildContent>
|
||||
@foreach (var childItem in option.Children)
|
||||
{
|
||||
@ChildItem(childItem)
|
||||
}
|
||||
</ChildContent>
|
||||
</MudMenu>
|
||||
</MudMenuItem>
|
||||
}
|
||||
}
|
||||
</MudMenu>
|
||||
};
|
||||
}
|
||||
}
|
@@ -39,7 +39,7 @@ namespace Lantean.QBTMudBlade.Components
|
||||
/// If true this component will render as a <see cref="MudToolBar"/> otherwise will render as a <see cref="MudMenu"/>.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public ParentType Type { get; set; }
|
||||
public RenderType Type { get; set; }
|
||||
|
||||
[CascadingParameter]
|
||||
public MainData MainData { get; set; } = default!;
|
||||
@@ -49,6 +49,8 @@ namespace Lantean.QBTMudBlade.Components
|
||||
|
||||
protected MudMenu? ActionsMenu { get; set; }
|
||||
|
||||
protected bool Disabled => !Hashes.Any();
|
||||
|
||||
protected async Task Pause()
|
||||
{
|
||||
await ApiClient.PauseTorrents(Hashes);
|
||||
@@ -73,7 +75,9 @@ namespace Lantean.QBTMudBlade.Components
|
||||
{
|
||||
savePath = torrent.SavePath;
|
||||
}
|
||||
await DialogService.ShowSingleFieldDialog("Set Location", "Location", savePath, v => ApiClient.SetTorrentLocation(v, null, Hashes.ToArray()));
|
||||
await Task.CompletedTask;
|
||||
throw new InvalidOperationException("BOoooo");
|
||||
//await DialogService.ShowSingleFieldDialog("Set Location", "Location", savePath, v => ApiClient.SetTorrentLocation(v, null, Hashes.ToArray()));
|
||||
}
|
||||
|
||||
protected async Task Rename()
|
||||
@@ -234,13 +238,12 @@ namespace Lantean.QBTMudBlade.Components
|
||||
|
||||
private IEnumerable<Action> GetOptions()
|
||||
{
|
||||
if (!Hashes.Any())
|
||||
Torrent? torrent = null;
|
||||
if (Hashes.Any())
|
||||
{
|
||||
return [];
|
||||
torrent = MainData.Torrents[Hashes.First()];
|
||||
}
|
||||
|
||||
var firstTorrent = MainData.Torrents[Hashes.First()];
|
||||
|
||||
var categories = new List<Action>
|
||||
{
|
||||
new Action("New", Icons.Material.Filled.Add, Color.Info, EventCallback.Factory.Create(this, AddCategory)),
|
||||
@@ -255,7 +258,7 @@ namespace Lantean.QBTMudBlade.Components
|
||||
new Action("Remove All", Icons.Material.Filled.Remove, Color.Error, EventCallback.Factory.Create(this, RemoveTags)),
|
||||
new Divider()
|
||||
};
|
||||
tags.AddRange(MainData.Tags.Select(t => new Action(t, firstTorrent.Tags.Contains(t) ? Icons.Material.Filled.CheckBox : Icons.Material.Filled.CheckBoxOutlineBlank, Color.Default, EventCallback.Factory.Create(this, () => ToggleTag(t)))));
|
||||
tags.AddRange(MainData.Tags.Select(t => new Action(t, (torrent?.Tags.Contains(t) == true) ? Icons.Material.Filled.CheckBox : Icons.Material.Filled.CheckBoxOutlineBlank, Color.Default, EventCallback.Factory.Create(this, () => ToggleTag(t)))));
|
||||
|
||||
var options = new List<Action>
|
||||
{
|
||||
@@ -268,11 +271,11 @@ namespace Lantean.QBTMudBlade.Components
|
||||
new Action("Rename", Icons.Material.Filled.DriveFileRenameOutline, Color.Info, EventCallback.Factory.Create(this, Rename)),
|
||||
new Action("Category", Icons.Material.Filled.List, Color.Info, categories, true),
|
||||
new Action("Tags", Icons.Material.Filled.Label, Color.Info, tags, true),
|
||||
new Action("Automatic Torrent Management", Icons.Material.Filled.Check, firstTorrent.AutomaticTorrentManagement ? Color.Info : Color.Transparent, EventCallback.Factory.Create(this, ToggleAutoTMM)),
|
||||
new Action("Automatic Torrent Management", Icons.Material.Filled.Check, (torrent?.AutomaticTorrentManagement == true) ? Color.Info : Color.Transparent, EventCallback.Factory.Create(this, ToggleAutoTMM)),
|
||||
new Divider(),
|
||||
new Action("Limit upload rate", Icons.Material.Filled.KeyboardDoubleArrowUp, Color.Info, EventCallback.Factory.Create(this, LimitUploadRate)),
|
||||
new Action("Limit share ratio", Icons.Material.Filled.Percent, Color.Warning, EventCallback.Factory.Create(this, LimitShareRatio)),
|
||||
new Action("Super seeding mode", Icons.Material.Filled.Check, firstTorrent.SuperSeeding ? Color.Info : Color.Transparent, EventCallback.Factory.Create(this, ToggleSuperSeeding)),
|
||||
new Action("Super seeding mode", Icons.Material.Filled.Check, (torrent?.SuperSeeding == true) ? Color.Info : Color.Transparent, EventCallback.Factory.Create(this, ToggleSuperSeeding)),
|
||||
new Divider(),
|
||||
new Action("Force recheck", Icons.Material.Filled.Loop, Color.Info, EventCallback.Factory.Create(this, ForceRecheck)),
|
||||
new Action("Force reannounce", Icons.Material.Filled.BroadcastOnHome, Color.Info, EventCallback.Factory.Create(this, ForceReannounce)),
|
||||
@@ -304,7 +307,7 @@ namespace Lantean.QBTMudBlade.Components
|
||||
}
|
||||
}
|
||||
|
||||
public enum ParentType
|
||||
public enum RenderType
|
||||
{
|
||||
/// <summary>
|
||||
/// Renders toolbar contents without the <see cref="MudToolBar"/> wrapper.
|
||||
|
@@ -1,22 +1,33 @@
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
<MudThemeProvider />
|
||||
<MudDialogProvider />
|
||||
<MudSnackbarProvider />
|
||||
<EnhancedErrorBoundary @ref="ErrorBoundary" OnClear="Cleared">
|
||||
<MudThemeProvider />
|
||||
<MudDialogProvider />
|
||||
<MudSnackbarProvider />
|
||||
|
||||
<PageTitle>qBittorrent Web UI</PageTitle>
|
||||
<PageTitle>qBittorrent Web UI</PageTitle>
|
||||
|
||||
<MudLayout>
|
||||
<MudAppBar Elevation="1">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Menu" Color="Color.Inherit" Edge="Edge.Start" OnClick="ToggleDrawer" />
|
||||
<MudText Typo="Typo.h5" Class="ml-3">qBittorrent Web UI</MudText>
|
||||
<MudSpacer />
|
||||
@if (ShowMenu)
|
||||
{
|
||||
<Menu />
|
||||
}
|
||||
</MudAppBar>
|
||||
<CascadingValue Value="DrawerOpen" Name="DrawerOpen">
|
||||
@Body
|
||||
</CascadingValue>
|
||||
</MudLayout>
|
||||
<MudLayout>
|
||||
<MudAppBar Elevation="1">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Menu" Color="Color.Inherit" Edge="Edge.Start" OnClick="ToggleDrawer" />
|
||||
<MudText Typo="Typo.h5" Class="ml-3">qBittorrent Web UI</MudText>
|
||||
<MudSpacer />
|
||||
@if (ErrorBoundary?.Errors.Count > 0)
|
||||
{
|
||||
<MudBadge Content="@(ErrorBoundary?.Errors.Count ?? 0)" Color="Color.Error" Overlap="true">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Error" Color="Color.Default" OnClick="ToggleErrorDrawer" />
|
||||
</MudBadge>
|
||||
}
|
||||
@if (ShowMenu)
|
||||
{
|
||||
<Menu />
|
||||
}
|
||||
</MudAppBar>
|
||||
<MudDrawer Open="ErrorDrawerOpen" ClipMode="DrawerClipMode.Docked" Elevation="2" Anchor="Anchor.Right">
|
||||
<ErrorDisplay ErrorBoundary="ErrorBoundary" />
|
||||
</MudDrawer>
|
||||
<CascadingValue Value="DrawerOpen" Name="DrawerOpen">
|
||||
@Body
|
||||
</CascadingValue>
|
||||
</MudLayout>
|
||||
</EnhancedErrorBoundary>
|
@@ -1,5 +1,7 @@
|
||||
using Lantean.QBitTorrentClient;
|
||||
using Lantean.QBTMudBlade.Components;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using MudBlazor;
|
||||
using MudBlazor.Services;
|
||||
|
||||
@@ -20,10 +22,14 @@ namespace Lantean.QBTMudBlade.Layout
|
||||
|
||||
protected bool DrawerOpen { get; set; } = true;
|
||||
|
||||
protected bool ErrorDrawerOpen { get; set; } = false;
|
||||
|
||||
protected bool ShowMenu { get; set; } = false;
|
||||
|
||||
public Guid Id => Guid.NewGuid();
|
||||
|
||||
protected EnhancedErrorBoundary? ErrorBoundary { get; set; }
|
||||
|
||||
ResizeOptions IBrowserViewportObserver.ResizeOptions { get; } = new()
|
||||
{
|
||||
ReportRate = 50,
|
||||
@@ -66,6 +72,16 @@ namespace Lantean.QBTMudBlade.Layout
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
protected void ToggleErrorDrawer()
|
||||
{
|
||||
ErrorDrawerOpen = !ErrorDrawerOpen;
|
||||
}
|
||||
|
||||
protected void Cleared()
|
||||
{
|
||||
ErrorDrawerOpen = false;
|
||||
}
|
||||
|
||||
protected virtual async Task DisposeAsync(bool disposing)
|
||||
{
|
||||
if (!_disposedValue)
|
||||
|
@@ -49,7 +49,7 @@ namespace Lantean.QBTMudBlade.Pages
|
||||
#if DEBUG
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await DoLogin("admin", "p22bMTJDK");
|
||||
await DoLogin("admin", "a8hbfvNP2");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@@ -1,6 +1,17 @@
|
||||
@page "/"
|
||||
@layout ListLayout
|
||||
|
||||
<MudToolBar DisableGutters="true" Dense="true">
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.AddLink" OnClick="AddTorrentLink" Title="Add torrent link" />
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.AddCircle" OnClick="AddTorrentFile" Title="Add torrent file" />
|
||||
<MudDivider Vertical="true" />
|
||||
<TorrentActions Type="RenderType.InitialIconsOnly" Hashes="GetSelectedTorrents()" />
|
||||
<MudDivider Vertical="true" />
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.Info" Color="Color.Inherit" Disabled="@(!ToolbarButtonsEnabled)" OnClick="ShowTorrent" Title="View torrent details" />
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.ViewColumn" Color="Color.Inherit" OnClick="ColumnOptions" Title="Choose Columns" />
|
||||
<MudSpacer />
|
||||
<MudTextField Value="SearchText" TextChanged="SearchTextChanged" Immediate="true" DebounceInterval="1000" Placeholder="Filter torrent list" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0"></MudTextField>
|
||||
</MudToolBar>
|
||||
<MudTable
|
||||
Items="OrderedTorrents"
|
||||
T="Torrent"
|
||||
@@ -23,24 +34,6 @@
|
||||
RowStyleFunc="RowStyle"
|
||||
Virtualize="true"
|
||||
Class="torrent-list">
|
||||
<ToolBarContent>
|
||||
<MudToolBar DisableGutters="true" Dense="true">
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.AddLink" OnClick="AddTorrentLink" Title="Add torrent link" />
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.AddCircle" OnClick="AddTorrentFile" Title="Add torrent file" />
|
||||
<MudDivider Vertical="true" />
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.Delete" Color="Color.Inherit" Disabled="@(!ToolbarButtonsEnabled)" OnClick="RemoveTorrents" Title="Remove selected torrents" />
|
||||
<MudDivider Vertical="true" />
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.PlayArrow" Disabled="@(!ToolbarButtonsEnabled)" OnClick="ResumeTorrents" Title="Resume selected torrents" />
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.Pause" Disabled="@(!ToolbarButtonsEnabled)" OnClick="PauseTorrents" Title="Pause selected torrents" />
|
||||
<MudDivider Vertical="true" />
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.Info" Color="Color.Inherit" Disabled="@(!ToolbarButtonsEnabled)" OnClick="ShowTorrent" Title="View torrent details" />
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.ViewColumn" Color="Color.Inherit" OnClick="ColumnOptions" Title="Choose Columns" />
|
||||
<MudDivider Vertical="true" />
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.Settings" Color="Color.Inherit" OnClick="Options" Title="View Application Options" />
|
||||
</MudToolBar>
|
||||
<MudSpacer />
|
||||
<MudTextField Value="SearchText" TextChanged="SearchTextChanged" Immediate="true" DebounceInterval="1000" Placeholder="Filter torrent list" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0"></MudTextField>
|
||||
</ToolBarContent>
|
||||
<ColGroup>
|
||||
<col style="width: 30px" />
|
||||
@foreach (var column in GetColumns())
|
||||
|
@@ -70,6 +70,11 @@ namespace Lantean.QBTMudBlade.Pages
|
||||
}
|
||||
}
|
||||
_sortSelector ??= _columns.First(c => c.Enabled).SortSelector;
|
||||
|
||||
if (SelectedTorrent is not null && Torrents is not null && !Torrents.Contains(SelectedTorrent))
|
||||
{
|
||||
SelectedTorrent = null;
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<Torrent>? GetOrderedTorrents()
|
||||
|
@@ -26,11 +26,15 @@ namespace Lantean.QBTMudBlade
|
||||
#endif
|
||||
|
||||
builder.Services.AddTransient<CookieHandler>();
|
||||
builder.Services.AddScoped<HttpLogger>();
|
||||
builder.Services
|
||||
.AddScoped(sp => sp
|
||||
.GetRequiredService<IHttpClientFactory>()
|
||||
.CreateClient("API"))
|
||||
.AddHttpClient("API", client => client.BaseAddress = new Uri(baseAddress, "/api/v2/")).AddHttpMessageHandler<CookieHandler>();
|
||||
.AddHttpClient("API", client => client.BaseAddress = new Uri(baseAddress, "/api/v2/"))
|
||||
.AddHttpMessageHandler<CookieHandler>()
|
||||
.RemoveAllLoggers()
|
||||
.AddLogger<HttpLogger>(wrapHandlersPipeline: true);
|
||||
|
||||
builder.Services.AddScoped<ApiClient>();
|
||||
builder.Services.AddScoped<IApiClient, ApiClient>();
|
||||
@@ -39,6 +43,12 @@ namespace Lantean.QBTMudBlade
|
||||
builder.Services.AddBlazoredLocalStorage();
|
||||
builder.Services.AddSingleton<IClipboardService, ClipboardService>();
|
||||
|
||||
#if DEBUG
|
||||
builder.Logging.SetMinimumLevel(LogLevel.Information);
|
||||
#else
|
||||
builder.Logging.SetMinimumLevel(LogLevel.Error);
|
||||
#endif
|
||||
|
||||
await builder.Build().RunAsync();
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
using Lantean.QBTMudBlade.Models;
|
||||
using MudBlazor;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Channels;
|
||||
|
||||
@@ -173,8 +174,8 @@ namespace Lantean.QBTMudBlade.Services
|
||||
{
|
||||
foreach (var hash in mainData.TorrentsRemoved)
|
||||
{
|
||||
torrentList.Torrents.Remove(hash);
|
||||
RemoveTorrentFromStates(torrentList, hash);
|
||||
torrentList.Torrents.Remove(hash);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,12 +280,20 @@ namespace Lantean.QBTMudBlade.Services
|
||||
torrentList.TagState[FilterHelper.TAG_UNTAGGED].AddIfTrueOrRemove(hash, FilterHelper.FilterTag(torrent, FilterHelper.TAG_UNTAGGED));
|
||||
foreach (var tag in torrentList.Tags)
|
||||
{
|
||||
if (!torrentList.TagState.ContainsKey(tag))
|
||||
{
|
||||
torrentList.TagState.Add(tag, []);
|
||||
}
|
||||
torrentList.TagState[tag].AddIfTrueOrRemove(hash, FilterHelper.FilterTag(torrent, tag));
|
||||
}
|
||||
|
||||
torrentList.CategoriesState[FilterHelper.CATEGORY_UNCATEGORIZED].AddIfTrueOrRemove(hash, FilterHelper.FilterCategory(torrent, FilterHelper.CATEGORY_UNCATEGORIZED, torrentList.ServerState.UseSubcategories));
|
||||
foreach (var category in torrentList.Categories.Keys)
|
||||
{
|
||||
if (!torrentList.CategoriesState.ContainsKey(category))
|
||||
{
|
||||
torrentList.CategoriesState.Add(category, []);
|
||||
}
|
||||
torrentList.CategoriesState[category].AddIfTrueOrRemove(hash, FilterHelper.FilterCategory(torrent, category, torrentList.ServerState.UseSubcategories));
|
||||
}
|
||||
|
||||
@@ -296,6 +305,10 @@ namespace Lantean.QBTMudBlade.Services
|
||||
torrentList.TrackersState[FilterHelper.TRACKER_TRACKERLESS].AddIfTrueOrRemove(hash, FilterHelper.FilterTracker(torrent, FilterHelper.TRACKER_TRACKERLESS));
|
||||
foreach (var tracker in torrentList.Trackers.Keys)
|
||||
{
|
||||
if (!torrentList.TrackersState.ContainsKey(tracker))
|
||||
{
|
||||
torrentList.TrackersState.Add(tracker, []);
|
||||
}
|
||||
torrentList.TrackersState[tracker].AddIfTrueOrRemove(hash, FilterHelper.FilterTracker(torrent, tracker));
|
||||
}
|
||||
}
|
||||
|
49
Lantean.QBTMudBlade/Services/HttpLogger.cs
Normal file
49
Lantean.QBTMudBlade/Services/HttpLogger.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using Microsoft.Extensions.Http.Logging;
|
||||
|
||||
namespace Lantean.QBTMudBlade.Services
|
||||
{
|
||||
public class HttpLogger : IHttpClientLogger
|
||||
{
|
||||
private readonly ILogger<HttpLogger> _logger;
|
||||
|
||||
public HttpLogger(ILogger<HttpLogger> logger)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public object? LogRequestStart(HttpRequestMessage request)
|
||||
{
|
||||
//_logger.LogInformation(
|
||||
// "Sending '{Request.Method}' to '{Request.Host}{Request.Path}'",
|
||||
// request.Method,
|
||||
// request.RequestUri?.GetComponents(UriComponents.SchemeAndServer, UriFormat.Unescaped),
|
||||
// request.RequestUri!.PathAndQuery);
|
||||
return null;
|
||||
}
|
||||
|
||||
public void LogRequestStop(
|
||||
object? context, HttpRequestMessage request, HttpResponseMessage response, TimeSpan elapsed)
|
||||
{
|
||||
//_logger.LogInformation(
|
||||
// "Received '{Response.StatusCodeInt} {Response.StatusCodeString}' after {Response.ElapsedMilliseconds}ms",
|
||||
// (int)response.StatusCode,
|
||||
// response.StatusCode,
|
||||
// elapsed.TotalMilliseconds.ToString("F1"));
|
||||
}
|
||||
|
||||
public void LogRequestFailed(
|
||||
object? context,
|
||||
HttpRequestMessage request,
|
||||
HttpResponseMessage? response,
|
||||
Exception exception,
|
||||
TimeSpan elapsed)
|
||||
{
|
||||
_logger.LogError(
|
||||
exception,
|
||||
"Request towards '{Request.Host}{Request.Path}' failed after {Response.ElapsedMilliseconds}ms",
|
||||
request.RequestUri?.GetComponents(UriComponents.SchemeAndServer, UriFormat.Unescaped),
|
||||
request.RequestUri!.PathAndQuery,
|
||||
elapsed.TotalMilliseconds.ToString("F1"));
|
||||
}
|
||||
}
|
||||
}
|
@@ -148,4 +148,10 @@ td.no-wrap {
|
||||
top: -2px;
|
||||
right: -5px;
|
||||
transform: rotate(270deg);
|
||||
}
|
||||
}
|
||||
|
||||
.overflow {
|
||||
overflow: auto;
|
||||
height: 200px;
|
||||
width: 100%;
|
||||
}
|
@@ -23,7 +23,7 @@
|
||||
<div class="loading-progress-text"></div>
|
||||
</div>
|
||||
|
||||
<div id="blazor-error-ui">
|
||||
<div id="blazor-error-ui" data-nosnippet>
|
||||
An unhandled error has occurred.
|
||||
<a href="" class="reload">Reload</a>
|
||||
<a class="dismiss">🗙</a>
|
||||
|
@@ -476,7 +476,7 @@ namespace Lantean.QBitTorrentClient
|
||||
{
|
||||
foreach (var (name, stream) in torrents)
|
||||
{
|
||||
content.Add(new StreamContent(stream), name);
|
||||
content.Add(new StreamContent(stream), "torrents", name);
|
||||
}
|
||||
}
|
||||
if (savePath is not null)
|
||||
|
Reference in New Issue
Block a user