mirror of
https://github.com/lantean-code/qbtmud.git
synced 2025-11-02 13:03:23 +00:00
Upgrade to MudBlazor 7 and add log pages
This commit is contained in:
@@ -3,15 +3,13 @@
|
||||
<MudGrid>
|
||||
<MudItem xs="12">
|
||||
<MudFileUpload T="IReadOnlyList<IBrowserFile>" FilesChanged="UploadFiles" Accept=".torrent" MaximumFileCount="50" >
|
||||
<ButtonTemplate>
|
||||
<MudButton HtmlTag="label"
|
||||
Variant="Variant.Filled"
|
||||
<ActivatorContent>
|
||||
<MudButton Variant="Variant.Filled"
|
||||
Color="Color.Primary"
|
||||
StartIcon="@Icons.Material.Filled.CloudUpload"
|
||||
for="@context.Id">
|
||||
StartIcon="@Icons.Material.Filled.CloudUpload">
|
||||
Choose files
|
||||
</MudButton>
|
||||
</ButtonTemplate>
|
||||
</ActivatorContent>
|
||||
</MudFileUpload>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<DialogContent>
|
||||
<MudGrid>
|
||||
<MudItem xs="12">
|
||||
<MudList Clickable="true">
|
||||
<MudList T="string">
|
||||
<MudListItem Icon="@Icons.Material.Filled.Add" IconColor="Color.Info" OnClick="AddCategory">Add</MudListItem>
|
||||
<MudListItem Icon="@Icons.Material.Filled.Remove" IconColor="Color.Error" OnClick="RemoveCategory">Remove</MudListItem>
|
||||
<MudDivider />
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<DialogContent>
|
||||
<MudGrid>
|
||||
<MudItem xs="12">
|
||||
<MudList Clickable="true">
|
||||
<MudList T="string">
|
||||
<MudListItem Icon="@Icons.Material.Filled.Add" IconColor="Color.Info" OnClick="AddTag">Add</MudListItem>
|
||||
<MudListItem Icon="@Icons.Material.Filled.Remove" IconColor="Color.Error" OnClick="RemoveAllTags">Remove All</MudListItem>
|
||||
<MudDivider />
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using MudBlazor;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Lantean.QBTMudBlade.Components.Dialogs
|
||||
{
|
||||
public partial class SliderFieldDialog<T>
|
||||
public partial class SliderFieldDialog<T> where T : struct, INumber<T>
|
||||
{
|
||||
[CascadingParameter]
|
||||
public MudDialogInstance MudDialog { get; set; } = default!;
|
||||
@@ -13,13 +14,13 @@ namespace Lantean.QBTMudBlade.Components.Dialogs
|
||||
public string Label { get; set; } = default!;
|
||||
|
||||
[Parameter]
|
||||
public T? Value { get; set; }
|
||||
public T Value { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public T? Min { get; set; }
|
||||
public T Min { get; set; } = T.Zero;
|
||||
|
||||
[Parameter]
|
||||
public T? Max { get; set; }
|
||||
public T Max { get; set; } = T.One;
|
||||
|
||||
protected void Cancel(MouseEventArgs args)
|
||||
{
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
<MudTh Class="@className" Style="@(GetColumnStyle(column))">
|
||||
@if (column.SortSelector is not null)
|
||||
{
|
||||
<SortLabel SortDirectionChanged="@(c => SetSort(column.Id, c))" SortDirection="@(column.Id == _sortColumn ? _sortDirection : SortDirection.None)">@columnHeader</SortLabel>
|
||||
<SortLabel Class="column-header" SortDirectionChanged="@(c => SetSort(column.Id, c))" SortDirection="@(column.Id == _sortColumn ? _sortDirection : SortDirection.None)">@columnHeader</SortLabel>
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -190,6 +190,10 @@ namespace Lantean.QBTMudBlade.Components
|
||||
|
||||
protected async Task OnRowClickInternal(TableRowClickEventArgs<T> eventArgs)
|
||||
{
|
||||
if (eventArgs.Item is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (MultiSelection)
|
||||
{
|
||||
if (eventArgs.MouseEventArgs.CtrlKey)
|
||||
@@ -236,7 +240,7 @@ namespace Lantean.QBTMudBlade.Components
|
||||
//EqualityComparer<T>.Default.Equals(item, SelectedItem) ||
|
||||
if (SelectedItems.Contains(item))
|
||||
{
|
||||
style += " background-color: var(--mud-palette-grey-dark); color: var(--mud-palette-grey-light) !important;";
|
||||
style += " background-color: var(--mud-palette-gray-dark); color: var(--mud-palette-gray-light) !important;";
|
||||
}
|
||||
return style;
|
||||
}
|
||||
|
||||
@@ -17,8 +17,12 @@ namespace Lantean.QBTMudBlade.Components
|
||||
[Parameter]
|
||||
public bool Disabled { get; set; }
|
||||
|
||||
[Inject]
|
||||
public ILogger<EnhancedErrorBoundary> Logger { get; set; } = default!;
|
||||
|
||||
protected override Task OnErrorAsync(Exception exception)
|
||||
{
|
||||
Logger.LogError(exception, exception.Message);
|
||||
_exceptions.Add(exception);
|
||||
|
||||
if (Disabled)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<MudList Clickable="true">
|
||||
<MudList T="string">
|
||||
<MudListItem OnClick="ClearErrors">Clear Errors</MudListItem>
|
||||
<MudListItem OnClick="ClearErrorsAndResumeAsync">Clear Errors and Resume</MudListItem>
|
||||
<MudDivider />
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
<MudToolBar DisableGutters="true" Dense="true">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.DriveFileRenameOutline" OnClick="RenameFile" Title="Rename" />
|
||||
<MudToolBar Gutters="false" 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" />
|
||||
<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>
|
||||
<MudMenuItem OnClick="DoNotDownloadLessThan100PercentAvailability">Less Than 100% Availability</MudMenuItem>
|
||||
<MudMenuItem OnClick="DoNotDownloadLessThan80PercentAvailability">Less than 80% Availability</MudMenuItem>
|
||||
<MudMenuItem OnClick="DoNotDownloadCurrentlyFilteredFiles">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>
|
||||
<MudMenuItem OnClick="NormalPriorityLessThan100PercentAvailability">Less Than 100% Availability</MudMenuItem>
|
||||
<MudMenuItem OnClick="NormalPriorityLessThan80PercentAvailability">Less than 80% Availability</MudMenuItem>
|
||||
<MudMenuItem OnClick="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" />
|
||||
<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>
|
||||
|
||||
@@ -89,7 +89,7 @@ namespace Lantean.QBTMudBlade.Components
|
||||
var result = await DialogService.ShowAsync<FilterOptionsDialog<ContentItem>>("Filters", parameters, DialogHelper.FormDialogOptions);
|
||||
|
||||
var dialogResult = await result.Result;
|
||||
if (dialogResult.Canceled)
|
||||
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<MudMenu Icon="@Icons.Material.Filled.MoreVert" Color="Color.Inherit" Dense="true" AnchorOrigin="Origin.BottomLeft" TransformOrigin="Origin.TopLeft">
|
||||
<MudMenu Icon="@Icons.Material.Filled.MoreVert" Color="Color.Inherit" Dense="true" AnchorOrigin="Origin.BottomRight" TransformOrigin="Origin.TopLeft">
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.PieChart" Href="/statistics">Statistics</MudMenuItem>
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.Search" Href="/search">Search</MudMenuItem>
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.RssFeed" Href="/rss">RSS</MudMenuItem>
|
||||
@@ -6,8 +6,8 @@
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.DisabledByDefault" Href="/blocks">Blocked IPs</MudMenuItem>
|
||||
<MudDivider />
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.Settings" Href="/settings">Settings</MudMenuItem>
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.Undo" OnClick="ResetWebUI" OnTouch="ResetWebUI">Reset Web UI</MudMenuItem>
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.Undo" OnClick="ResetWebUI">Reset Web UI</MudMenuItem>
|
||||
<MudDivider />
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.Logout" OnClick="Logout" OnTouch="Logout">Logout</MudMenuItem>
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.ExitToApp" OnClick="Exit" OnTouch="Exit">Exit qBittorrent</MudMenuItem>
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.Logout" OnClick="Logout">Logout</MudMenuItem>
|
||||
<MudMenuItem Icon="@Icons.Material.Filled.ExitToApp" OnClick="Exit">Exit qBittorrent</MudMenuItem>
|
||||
</MudMenu>
|
||||
@@ -289,7 +289,7 @@
|
||||
</MudItem>
|
||||
<MudItem xs="12">
|
||||
<MudText>Supported parameters (case sensitive):</MudText>
|
||||
<MudList>
|
||||
<MudList T="string" ReadOnly="true">
|
||||
<MudListItem>%N: Torrent name</MudListItem>
|
||||
<MudListItem>%L: Category</MudListItem>
|
||||
<MudListItem>%G: Tags (separated by comma)</MudListItem>
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Lantean.QBTMudBlade.Components
|
||||
[Parameter]
|
||||
public bool Active { get; set; }
|
||||
|
||||
[CascadingParameter]
|
||||
[CascadingParameter(Name = "RefreshInterval")]
|
||||
public int RefreshInterval { get; set; }
|
||||
|
||||
[CascadingParameter]
|
||||
|
||||
@@ -51,9 +51,9 @@ namespace Lantean.QBTMudBlade.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
downloadingColor = Theme.Palette.Success.ToString(MudBlazor.Utilities.MudColorOutputFormats.RGBA);
|
||||
haveColor = Theme.Palette.Info.ToString(MudBlazor.Utilities.MudColorOutputFormats.RGBA);
|
||||
borderColor = Theme.Palette.Black.ToString(MudBlazor.Utilities.MudColorOutputFormats.RGBA);
|
||||
downloadingColor = Theme.PaletteLight.Success.ToString(MudBlazor.Utilities.MudColorOutputFormats.RGBA);
|
||||
haveColor = Theme.PaletteLight.Info.ToString(MudBlazor.Utilities.MudColorOutputFormats.RGBA);
|
||||
borderColor = Theme.PaletteLight.Black.ToString(MudBlazor.Utilities.MudColorOutputFormats.RGBA);
|
||||
}
|
||||
await JSRuntime.RenderPiecesBar("progress", Hash, Pieces.Select(s => (int)s).ToArray(), downloadingColor, haveColor, borderColor);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@if (RenderType == RenderType.Toolbar)
|
||||
{
|
||||
<MudToolBar Dense="true" DisableGutters="true" WrapContent="true">
|
||||
<MudToolBar Dense="true" Gutters="false" WrapContent="true">
|
||||
@ToolbarContent
|
||||
</MudToolBar>
|
||||
}
|
||||
@@ -10,7 +10,7 @@ else if (RenderType == RenderType.ToolbarContents)
|
||||
}
|
||||
else if (RenderType == RenderType.MixedToolbar)
|
||||
{
|
||||
<MudToolBar Dense="true" DisableGutters="true" WrapContent="true">
|
||||
<MudToolBar Dense="true" Gutters="false" WrapContent="true">
|
||||
@MixedToolbarContent
|
||||
</MudToolBar>
|
||||
}
|
||||
@@ -27,7 +27,7 @@ else if (RenderType == RenderType.InitialIconsOnly)
|
||||
<MudDivider Vertical="true" />
|
||||
}
|
||||
|
||||
<MudIconButton Title="@action.Text" Icon="@action.Icon" Color="action.Color" OnClick="action.Callback" Disabled="Disabled" />
|
||||
<MudIconButton title="@action.Text" Icon="@action.Icon" Color="action.Color" OnClick="action.Callback" Disabled="Disabled" />
|
||||
}
|
||||
|
||||
@Menu(Actions.Skip(5))
|
||||
@@ -37,7 +37,7 @@ else if (RenderType == RenderType.Children)
|
||||
var parent = Actions.FirstOrDefault(a => a.Text == ParentAction?.Text);
|
||||
if (parent is not null)
|
||||
{
|
||||
<MudList Clickable="true">
|
||||
<MudList T="string">
|
||||
@foreach (var action in parent.Children)
|
||||
{
|
||||
@if (action.SeparatorBefore)
|
||||
@@ -77,7 +77,7 @@ else
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudIconButton Title="@action.Text" Icon="@action.Icon" Color="action.Color" OnClick="action.Callback" Disabled="Disabled" />
|
||||
<MudIconButton title="@action.Text" Icon="@action.Icon" Color="action.Color" OnClick="action.Callback" Disabled="Disabled" />
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -115,7 +115,7 @@ else
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudIconButton Title="@action.Text" Icon="@action.Icon" Color="action.Color" OnClick="action.Callback" Disabled="Disabled" />
|
||||
<MudIconButton title="@action.Text" Icon="@action.Icon" Color="action.Color" OnClick="action.Callback" Disabled="Disabled" />
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -141,7 +141,7 @@ else
|
||||
<MudDivider />
|
||||
}
|
||||
|
||||
<MudMenuItem Icon="@action.Icon" IconColor="action.Color" OnClick="action.Callback" OnTouch="action.Callback" Disabled="Disabled">@action.Text</MudMenuItem>
|
||||
<MudMenuItem Icon="@action.Icon" IconColor="action.Color" OnClick="action.Callback" Disabled="Disabled">@action.Text</MudMenuItem>
|
||||
};
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ else
|
||||
{
|
||||
return __builder =>
|
||||
{
|
||||
<MudMenu Dense="true" AnchorOrigin="Origin.BottomLeft" TransformOrigin="Origin.TopLeft" Label="Actions" EndIcon="@Icons.Material.Filled.ArrowDropDown" @ref="ActionsMenu" Disabled="@(!Hashes.Any())">
|
||||
<MudMenu Dense="true" AnchorOrigin="Origin.BottomLeft" TransformOrigin="Origin.TopLeft" Label="Actions" EndIcon="@Icons.Material.Filled.ArrowDropDown" @ref="ActionsMenu" Disabled="@(!Hashes.Any())" ActivationEvent="MouseEvent.LeftClick">
|
||||
@foreach (var action in actions)
|
||||
{
|
||||
@if (action.SeparatorBefore)
|
||||
@@ -159,14 +159,14 @@ else
|
||||
|
||||
if (!action.Children.Any())
|
||||
{
|
||||
<MudMenuItem Icon="@action.Icon" IconColor="action.Color" OnClick="action.Callback" OnTouch="action.Callback" Disabled="Disabled">
|
||||
<MudMenuItem Icon="@action.Icon" IconColor="action.Color" OnClick="action.Callback" Disabled="Disabled">
|
||||
@action.Text
|
||||
</MudMenuItem>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudMenuItem Icon="@action.Icon" IconColor="action.Color" OnTouch="@(t => SubMenuTouch(action))" OnClick="@(t => SubMenuTouch(action))">
|
||||
<MudMenu Dense="true" AnchorOrigin="Origin.TopRight" TransformOrigin="Origin.TopLeft" ActivationEvent="MouseEvent.MouseOver" Icon="@Icons.Material.Filled.ArrowDropDown" DisableElevation="true" DisableRipple="true" Class="sub-menu">
|
||||
<MudMenuItem Icon="@action.Icon" IconColor="action.Color" OnClick="@(t => SubMenuTouch(action))">
|
||||
<MudMenu Dense="true" AnchorOrigin="Origin.TopRight" TransformOrigin="Origin.TopLeft" ActivationEvent="MouseEvent.MouseOver" Icon="@Icons.Material.Filled.ArrowDropDown" Ripple="false" Class="sub-menu">
|
||||
<ActivatorContent>
|
||||
@action.Text
|
||||
</ActivatorContent>
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace Lantean.QBTMudBlade.Components
|
||||
{
|
||||
new TorrentAction("start", "Start", Icons.Material.Filled.PlayArrow, Color.Success, CreateCallback(Resume)),
|
||||
new TorrentAction("pause", "Pause", Icons.Material.Filled.Pause, Color.Warning, CreateCallback(Pause)),
|
||||
new TorrentAction("forceStart", "Force start", Icons.Material.Filled.Pause, Color.Warning, CreateCallback(ForceStart)),
|
||||
new TorrentAction("forceStart", "Force start", Icons.Material.Filled.Forward, Color.Warning, CreateCallback(ForceStart)),
|
||||
new TorrentAction("delete", "Remove", Icons.Material.Filled.Delete, Color.Error, CreateCallback(Remove), separatorBefore: true),
|
||||
new TorrentAction("setLocation", "Set location", Icons.Material.Filled.MyLocation, Color.Info, CreateCallback(SetLocation), separatorBefore: true),
|
||||
new TorrentAction("rename", "Rename", Icons.Material.Filled.DriveFileRenameOutline, Color.Info, CreateCallback(Rename)),
|
||||
@@ -248,7 +248,10 @@ namespace Lantean.QBTMudBlade.Components
|
||||
protected async Task Copy(Func<Torrent, object?> selector)
|
||||
{
|
||||
await Copy(string.Join(Environment.NewLine, GetTorrents().Select(selector)));
|
||||
ActionsMenu?.CloseMenu();
|
||||
if (ActionsMenu is not null)
|
||||
{
|
||||
await ActionsMenu.CloseMenuAsync();
|
||||
}
|
||||
}
|
||||
|
||||
protected async Task Export()
|
||||
@@ -487,7 +490,6 @@ namespace Lantean.QBTMudBlade.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (actionState.Show is null || actionState.Show.Value)
|
||||
{
|
||||
var act = action with { };
|
||||
@@ -499,6 +501,8 @@ namespace Lantean.QBTMudBlade.Components
|
||||
{
|
||||
act.IsChecked = actionState.IsChecked.Value;
|
||||
}
|
||||
|
||||
yield return act;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
<MudToolBar Dense="true" DisableGutters="true" WrapContent="true">
|
||||
<MudToolBar Dense="true" Gutters="false" WrapContent="true">
|
||||
@{
|
||||
var (icon, color) = DisplayHelpers.GetStateIcon(Torrent.State);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Lantean.QBTMudBlade.Components
|
||||
[Parameter]
|
||||
public bool Active { get; set; }
|
||||
|
||||
[CascadingParameter]
|
||||
[CascadingParameter(Name = "RefreshInterval")]
|
||||
public int RefreshInterval { get; set; }
|
||||
|
||||
[Inject]
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Lantean.QBTMudBlade.Components
|
||||
[Parameter, EditorRequired]
|
||||
public string? Hash { get; set; }
|
||||
|
||||
[CascadingParameter]
|
||||
[CascadingParameter(Name = "RefreshInterval")]
|
||||
public int RefreshInterval { get; set; }
|
||||
|
||||
[Inject]
|
||||
|
||||
@@ -9,11 +9,11 @@ namespace Lantean.QBTMudBlade
|
||||
{
|
||||
public static class DialogHelper
|
||||
{
|
||||
public static readonly DialogOptions FormDialogOptions = new() { CloseButton = true, MaxWidth = MaxWidth.Medium, ClassBackground = "background-blur", FullWidth = true };
|
||||
public static readonly DialogOptions FormDialogOptions = new() { CloseButton = true, MaxWidth = MaxWidth.Medium, BackgroundClass = "background-blur", FullWidth = true };
|
||||
|
||||
public static readonly DialogOptions NonBlurFormDialogOptions = new() { CloseButton = true, MaxWidth = MaxWidth.Medium, FullWidth = true };
|
||||
|
||||
public static readonly DialogOptions ConfirmDialogOptions = new() { ClassBackground = "background-blur", MaxWidth = MaxWidth.Small, FullWidth = true };
|
||||
public static readonly DialogOptions ConfirmDialogOptions = new() { BackgroundClass = "background-blur", MaxWidth = MaxWidth.Small, FullWidth = true };
|
||||
|
||||
public static readonly DialogOptions NonBlurConfirmDialogOptions = new() { MaxWidth = MaxWidth.Small, FullWidth = true };
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Lantean.QBTMudBlade
|
||||
{
|
||||
var result = await dialogService.ShowAsync<AddTorrentFileDialog>("Upload local torrent", FormDialogOptions);
|
||||
var dialogResult = await result.Result;
|
||||
if (dialogResult.Canceled)
|
||||
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -69,7 +69,7 @@ namespace Lantean.QBTMudBlade
|
||||
{
|
||||
var result = await dialogService.ShowAsync<AddTorrentLinkDialog>("Download from URLs", FormDialogOptions);
|
||||
var dialogResult = await result.Result;
|
||||
if (dialogResult.Canceled)
|
||||
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -104,13 +104,13 @@ namespace Lantean.QBTMudBlade
|
||||
};
|
||||
|
||||
var reference = await dialogService.ShowAsync<DeleteDialog>($"Remove torrent{(hashes.Length == 1 ? "" : "s")}?", parameters, ConfirmDialogOptions);
|
||||
var result = await reference.Result;
|
||||
if (result.Canceled)
|
||||
var dialogResult = await reference.Result;
|
||||
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await apiClient.DeleteTorrents(hashes, (bool)result.Data);
|
||||
await apiClient.DeleteTorrents(hashes, (bool)dialogResult.Data);
|
||||
}
|
||||
|
||||
public static async Task InvokeRenameFilesDialog(this IDialogService dialogService, IApiClient apiClient, string hash)
|
||||
@@ -121,13 +121,13 @@ namespace Lantean.QBTMudBlade
|
||||
public static async Task<string?> ShowAddCategoryDialog(this IDialogService dialogService, IApiClient apiClient)
|
||||
{
|
||||
var reference = await dialogService.ShowAsync<AddCategoryDialog>("New Category", NonBlurFormDialogOptions);
|
||||
var result = await reference.Result;
|
||||
if (result.Canceled)
|
||||
var dialogResult = await reference.Result;
|
||||
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var category = (Category)result.Data;
|
||||
var category = (Category)dialogResult.Data;
|
||||
|
||||
await apiClient.AddCategory(category.Name, category.SavePath);
|
||||
|
||||
@@ -136,15 +136,15 @@ namespace Lantean.QBTMudBlade
|
||||
|
||||
public static async Task<HashSet<string>?> ShowAddTagsDialog(this IDialogService dialogService, IApiClient apiClient)
|
||||
{
|
||||
var dialogReference = await dialogService.ShowAsync<AddTagDialog>("Add Tags", NonBlurFormDialogOptions);
|
||||
var result = await dialogReference.Result;
|
||||
var reference = await dialogService.ShowAsync<AddTagDialog>("Add Tags", NonBlurFormDialogOptions);
|
||||
var dialogResult = await reference.Result;
|
||||
|
||||
if (result.Canceled)
|
||||
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var tags = (HashSet<string>)result.Data;
|
||||
var tags = (HashSet<string>)dialogResult.Data;
|
||||
|
||||
return tags;
|
||||
}
|
||||
@@ -158,7 +158,7 @@ namespace Lantean.QBTMudBlade
|
||||
var result = await dialogService.ShowAsync<ConfirmDialog>(title, parameters, ConfirmDialogOptions);
|
||||
|
||||
var dialogResult = await result.Result;
|
||||
return !dialogResult.Canceled;
|
||||
return dialogResult is not null && !dialogResult.Canceled;
|
||||
}
|
||||
|
||||
public static async Task ShowConfirmDialog(this IDialogService dialogService, string title, string content, Func<Task> onSuccess)
|
||||
@@ -170,7 +170,7 @@ namespace Lantean.QBTMudBlade
|
||||
var result = await dialogService.ShowAsync<ConfirmDialog>(title, parameters, ConfirmDialogOptions);
|
||||
|
||||
var dialogResult = await result.Result;
|
||||
if (dialogResult.Canceled)
|
||||
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -198,7 +198,7 @@ namespace Lantean.QBTMudBlade
|
||||
var result = await dialogService.ShowAsync<SingleFieldDialog<T>>(title, parameters, FormDialogOptions);
|
||||
|
||||
var dialogResult = await result.Result;
|
||||
if (dialogResult.Canceled)
|
||||
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -217,7 +217,7 @@ namespace Lantean.QBTMudBlade
|
||||
var result = await dialogService.ShowAsync<SliderFieldDialog<long>>("Download Rate", parameters, FormDialogOptions);
|
||||
|
||||
var dialogResult = await result.Result;
|
||||
if (dialogResult.Canceled)
|
||||
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -236,7 +236,7 @@ namespace Lantean.QBTMudBlade
|
||||
var result = await dialogService.ShowAsync<SliderFieldDialog<long>>("Upload Rate", parameters, FormDialogOptions);
|
||||
|
||||
var dialogResult = await result.Result;
|
||||
if (dialogResult.Canceled)
|
||||
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -255,7 +255,7 @@ namespace Lantean.QBTMudBlade
|
||||
var result = await dialogService.ShowAsync<SliderFieldDialog<float>>("Upload Rate", parameters, FormDialogOptions);
|
||||
|
||||
var dialogResult = await result.Result;
|
||||
if (dialogResult.Canceled)
|
||||
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -273,7 +273,7 @@ namespace Lantean.QBTMudBlade
|
||||
var result = await dialogService.ShowAsync<FilterOptionsDialog<T>>("Filters", parameters, FormDialogOptions);
|
||||
|
||||
var dialogResult = await result.Result;
|
||||
if (dialogResult.Canceled)
|
||||
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -291,13 +291,13 @@ namespace Lantean.QBTMudBlade
|
||||
};
|
||||
|
||||
var reference = await dialogService.ShowAsync<ColumnOptionsDialog<T>>("Column Options", parameters, FormDialogOptions);
|
||||
var result = await reference.Result;
|
||||
if (result.Canceled)
|
||||
var dialogResult = await reference.Result;
|
||||
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
return ((HashSet<string>, Dictionary<string, int?>))result.Data;
|
||||
return ((HashSet<string>, Dictionary<string, int?>))dialogResult.Data;
|
||||
}
|
||||
|
||||
public static async Task InvokeRssRulesDialog(this IDialogService dialogService)
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.6" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.6" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
|
||||
<PackageReference Include="MudBlazor" Version="6.20.0" />
|
||||
<PackageReference Include="MudBlazor" Version="7.0.0-rc.2" />
|
||||
<PackageReference Include="MudBlazor.ThemeManager" Version="1.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@inherits LayoutComponentBase
|
||||
@layout LoggedInLayout
|
||||
|
||||
<MudDrawer Open="DrawerOpen" ClipMode="DrawerClipMode.Always" Elevation="2" DisableOverlay="true">
|
||||
<MudDrawer Open="DrawerOpen" ClipMode="DrawerClipMode.Always" Elevation="2" Overlay="false">
|
||||
<TorrentsListNav Torrents="Torrents" SelectedTorrent="@SelectedTorrent" SortDirection="SortDirection" SortColumn="@SortColumn" />
|
||||
</MudDrawer>
|
||||
<MudMainContent>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@inherits LayoutComponentBase
|
||||
@layout LoggedInLayout
|
||||
|
||||
<MudDrawer Open="DrawerOpen" ClipMode="DrawerClipMode.Always" Elevation="2" DisableOverlay="true">
|
||||
<MudDrawer Open="DrawerOpen" ClipMode="DrawerClipMode.Always" Elevation="2" Overlay="false">
|
||||
<FiltersNav CategoryChanged="CategoryChanged" StatusChanged="StatusChanged" TagChanged="TagChanged" TrackerChanged="TrackerChanged" />
|
||||
</MudDrawer>
|
||||
<MudMainContent>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<MudThemeProvider @ref="MudThemeProvider" @bind-IsDarkMode="IsDarkMode" Theme="Theme" />
|
||||
<MudDialogProvider />
|
||||
<MudSnackbarProvider />
|
||||
<MudPopoverProvider />
|
||||
|
||||
<PageTitle>qBittorrent Web UI</PageTitle>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@inherits LayoutComponentBase
|
||||
@layout LoggedInLayout
|
||||
|
||||
<MudDrawer Open="DrawerOpen" ClipMode="DrawerClipMode.Always" Elevation="2" DisableOverlay="true">
|
||||
<MudDrawer Open="DrawerOpen" ClipMode="DrawerClipMode.Always" Elevation="2" Overlay="false">
|
||||
<MudNavMenu>
|
||||
<MudNavLink Icon="@(Icons.Material.Outlined.NavigateBefore)" OnClick="NavigateBack">Back</MudNavLink>
|
||||
<MudDivider />
|
||||
|
||||
16
Lantean.QBTMudBlade/Models/LogForm.cs
Normal file
16
Lantean.QBTMudBlade/Models/LogForm.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
namespace Lantean.QBTMudBlade.Models
|
||||
{
|
||||
public class LogForm
|
||||
{
|
||||
public bool Normal => SelectedTypes.Contains("Normal");
|
||||
public bool Info => SelectedTypes.Contains("Info");
|
||||
public bool Warning => SelectedTypes.Contains("Warning");
|
||||
public bool Critical => SelectedTypes.Contains("Critical");
|
||||
|
||||
public int? LastKnownId { get; set; }
|
||||
|
||||
public IEnumerable<string> SelectedTypes { get; set; } = new HashSet<string>();
|
||||
|
||||
public string? Criteria { get; set; }
|
||||
}
|
||||
}
|
||||
16
Lantean.QBTMudBlade/Models/LoginForm.cs
Normal file
16
Lantean.QBTMudBlade/Models/LoginForm.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Lantean.QBTMudBlade.Models
|
||||
{
|
||||
public class LoginForm
|
||||
{
|
||||
[Required]
|
||||
[NotNull]
|
||||
public string? Username { get; set; }
|
||||
|
||||
[Required]
|
||||
[NotNull]
|
||||
public string? Password { get; set; }
|
||||
}
|
||||
}
|
||||
11
Lantean.QBTMudBlade/Models/SearchForm.cs
Normal file
11
Lantean.QBTMudBlade/Models/SearchForm.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace Lantean.QBTMudBlade.Models
|
||||
{
|
||||
public class SearchForm
|
||||
{
|
||||
public string? SearchText { get; set; }
|
||||
|
||||
public string SelectedPlugin { get; set; } = "all";
|
||||
|
||||
public string SelectedCategory { get; set; } = "all";
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,59 @@
|
||||
@page "/blocks"
|
||||
@layout OtherLayout
|
||||
|
||||
<MudToolBar DisableGutters="true" Dense="true">
|
||||
<MudToolBar Gutters="false" Dense="true">
|
||||
@if (!DrawerOpen)
|
||||
{
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.NavigateBefore" OnClick="NavigateBack" Title="Back to torrent list" />
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.NavigateBefore" OnClick="NavigateBack" title="Back to torrent list" />
|
||||
<MudDivider Vertical="true" />
|
||||
}
|
||||
<MudDivider Vertical="true" />
|
||||
<MudText Class="pl-5 no-wrap">Blocked IPs</MudText>
|
||||
</MudToolBar>
|
||||
|
||||
<MudContainer MaxWidth="MaxWidth.ExtraExtraLarge" Class="details-tab-contents">
|
||||
<p>Coming soon.</p>
|
||||
</MudContainer>
|
||||
<MudCard Elevation="1" Class="ml-4 mr-4 mb-4">
|
||||
<MudCardContent>
|
||||
<EditForm Model="Model" OnSubmit="Submit">
|
||||
<MudGrid>
|
||||
<MudItem md="10">
|
||||
<MudTextField T="string" Label="Criteria" @bind-Value="Model.Criteria" ShrinkLabel Variant="Variant.Outlined" />
|
||||
</MudItem>
|
||||
<MudItem md="2">
|
||||
<MudButton ButtonType="ButtonType.Submit" FullWidth="true" Color="Color.Primary" EndIcon="@Icons.Material.Filled.Search" Variant="Variant.Filled" Class="mt-6">Filter</MudButton>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
</EditForm>
|
||||
</MudCardContent>
|
||||
</MudCard>
|
||||
|
||||
<MudTable Items="Results"
|
||||
T="Lantean.QBitTorrentClient.Models.PeerLog"
|
||||
Hover="true"
|
||||
FixedHeader="true"
|
||||
HeaderClass="table-head-bordered"
|
||||
Dense="true"
|
||||
Breakpoint="Breakpoint.None"
|
||||
Bordered="true"
|
||||
Square="true"
|
||||
LoadingProgressColor="Color.Info"
|
||||
HorizontalScrollbar="true"
|
||||
Virtualize="true"
|
||||
AllowUnsorted="false"
|
||||
SelectOnRowClick="false"
|
||||
Class="search-list"
|
||||
RowClassFunc="RowClass">
|
||||
<HeaderContent>
|
||||
<MudTh>Id</MudTh>
|
||||
<MudTh>Message</MudTh>
|
||||
<MudTh>Timestamp</MudTh>
|
||||
<MudTh>Status</MudTh>
|
||||
<MudTh>Reason</MudTh>
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="Id">@context.Id</MudTd>
|
||||
<MudTd DataLabel="IP">@context.IPAddress</MudTd>
|
||||
<MudTd DataLabel="Timestamp">@DisplayHelpers.DateTime(context.Timestamp)</MudTd>
|
||||
<MudTd DataLabel="Status">@(context.Blocked ? "Blocked" : "Banned")</MudTd>
|
||||
<MudTd DataLabel="Reason">@context.Reason</MudTd>
|
||||
</RowTemplate>
|
||||
</MudTable>
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
using Lantean.QBitTorrentClient;
|
||||
using Blazored.LocalStorage;
|
||||
using Lantean.QBitTorrentClient;
|
||||
using Lantean.QBTMudBlade.Models;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
using MudBlazor;
|
||||
using System.Net;
|
||||
|
||||
namespace Lantean.QBTMudBlade.Pages
|
||||
{
|
||||
public partial class Blocks
|
||||
public partial class Blocks : IAsyncDisposable
|
||||
{
|
||||
private readonly bool _refreshEnabled = true;
|
||||
private const string _selectedTypesStorageKey = "Blocks.SelectedTypes";
|
||||
|
||||
private readonly CancellationTokenSource _timerCancellationToken = new();
|
||||
private bool _disposedValue;
|
||||
|
||||
[Inject]
|
||||
protected IApiClient ApiClient { get; set; } = default!;
|
||||
|
||||
@@ -16,24 +25,129 @@ namespace Lantean.QBTMudBlade.Pages
|
||||
[Inject]
|
||||
protected NavigationManager NavigationManager { get; set; } = default!;
|
||||
|
||||
[CascadingParameter]
|
||||
public MainData? MainData { get; set; }
|
||||
[Inject]
|
||||
protected ILocalStorageService LocalStorage { get; set; } = default!;
|
||||
|
||||
[CascadingParameter(Name = "DrawerOpen")]
|
||||
public bool DrawerOpen { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? Hash { get; set; }
|
||||
protected LogForm Model { get; set; } = new LogForm();
|
||||
|
||||
protected int ActiveTab { get; set; } = 0;
|
||||
protected List<QBitTorrentClient.Models.PeerLog>? Results { get; private set; }
|
||||
|
||||
protected int RefreshInterval => MainData?.ServerState.RefreshInterval ?? 1500;
|
||||
protected MudSelect<string>? CategoryMudSelect { get; set; }
|
||||
|
||||
protected ServerState? ServerState => MainData?.ServerState;
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var selectedTypes = await LocalStorage.GetItemAsync<IEnumerable<string>>(_selectedTypesStorageKey);
|
||||
if (selectedTypes is not null)
|
||||
{
|
||||
Model.SelectedTypes = selectedTypes;
|
||||
}
|
||||
else
|
||||
{
|
||||
Model.SelectedTypes = ["Normal"];
|
||||
}
|
||||
|
||||
await DoSearch();
|
||||
}
|
||||
|
||||
protected void NavigateBack()
|
||||
{
|
||||
NavigationManager.NavigateTo("/");
|
||||
}
|
||||
|
||||
protected async Task SelectedValuesChanged(IEnumerable<string> values)
|
||||
{
|
||||
Model.SelectedTypes = values;
|
||||
|
||||
await LocalStorage.SetItemAsync(_selectedTypesStorageKey, Model.SelectedTypes);
|
||||
}
|
||||
|
||||
protected static string GenerateSelectedText(List<string> values)
|
||||
{
|
||||
if (values.Count == 4)
|
||||
{
|
||||
return "All";
|
||||
}
|
||||
|
||||
return $"{values.Count} selected";
|
||||
}
|
||||
|
||||
protected Task Submit(EditContext editContext)
|
||||
{
|
||||
return DoSearch();
|
||||
}
|
||||
|
||||
private async Task DoSearch()
|
||||
{
|
||||
var results = await ApiClient.GetPeerLog(Model.LastKnownId);
|
||||
if (results.Count > 0)
|
||||
{
|
||||
Results ??= [];
|
||||
Results.AddRange(results);
|
||||
Model.LastKnownId = results[^1].Id;
|
||||
}
|
||||
}
|
||||
|
||||
protected static string RowClass(QBitTorrentClient.Models.PeerLog log, int index)
|
||||
{
|
||||
return $"log-{(log.Blocked ? "critical" : "normal")}";
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||
await DisposeAsync(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual async Task DisposeAsync(bool disposing)
|
||||
{
|
||||
if (!_disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_timerCancellationToken.Cancel();
|
||||
_timerCancellationToken.Dispose();
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
_disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (!_refreshEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!firstRender)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using (var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(1500)))
|
||||
{
|
||||
while (!_timerCancellationToken.IsCancellationRequested && await timer.WaitForNextTickAsync())
|
||||
{
|
||||
try
|
||||
{
|
||||
await DoSearch();
|
||||
}
|
||||
catch (HttpRequestException exception) when (exception.StatusCode == HttpStatusCode.Forbidden || exception.StatusCode == HttpStatusCode.NotFound)
|
||||
{
|
||||
_timerCancellationToken.CancelIfNotDisposed();
|
||||
return;
|
||||
}
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
@page "/details/{hash}"
|
||||
@layout DetailsLayout
|
||||
|
||||
<MudToolBar DisableGutters="true" Dense="true">
|
||||
<MudToolBar Gutters="false" Dense="true">
|
||||
@if (!DrawerOpen)
|
||||
{
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.NavigateBefore" OnClick="NavigateBack" Title="Back to torrent list" />
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.NavigateBefore" OnClick="NavigateBack" title="Back to torrent list" />
|
||||
<MudDivider Vertical="true" />
|
||||
}
|
||||
@if (Hash is not null)
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
@if (ShowTabs)
|
||||
{
|
||||
<CascadingValue Value="RefreshInterval">
|
||||
<CascadingValue Value="RefreshInterval" Name="RefreshInterval">
|
||||
<MudTabs Elevation="2" ApplyEffectsToContainer="true" @bind-ActivePanelIndex="ActiveTab" KeepPanelsAlive="true" Border="true">
|
||||
<MudTabPanel Text="General">
|
||||
<GeneralTab Hash="@Hash" Active="@(ActiveTab == 0)" />
|
||||
|
||||
@@ -1,16 +1,65 @@
|
||||
@page "/log"
|
||||
@layout OtherLayout
|
||||
|
||||
<MudToolBar DisableGutters="true" Dense="true">
|
||||
<MudToolBar Gutters="false" Dense="true">
|
||||
@if (!DrawerOpen)
|
||||
{
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.NavigateBefore" OnClick="NavigateBack" Title="Back to torrent list" />
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.NavigateBefore" OnClick="NavigateBack" title="Back to torrent list" />
|
||||
<MudDivider Vertical="true" />
|
||||
}
|
||||
<MudDivider Vertical="true" />
|
||||
<MudText Class="pl-5 no-wrap">Execution Log</MudText>
|
||||
</MudToolBar>
|
||||
|
||||
<MudContainer MaxWidth="MaxWidth.ExtraExtraLarge" Class="details-tab-contents">
|
||||
<p>Coming soon.</p>
|
||||
</MudContainer>
|
||||
<MudCard Elevation="1" Class="ml-4 mr-4 mb-4">
|
||||
<MudCardContent>
|
||||
<EditForm Model="Model" OnSubmit="Submit">
|
||||
<MudGrid>
|
||||
<MudItem md="8">
|
||||
<MudTextField T="string" Label="Criteria" @bind-Value="Model.Criteria" ShrinkLabel Variant="Variant.Outlined" />
|
||||
</MudItem>
|
||||
<MudItem md="2">
|
||||
<MudSelect @ref="CategoryMudSelect" T="string" Label="Categories" SelectedValues="Model.SelectedTypes" SelectedValuesChanged="SelectedValuesChanged" ShrinkLabel Variant="Variant.Outlined" MultiSelection="true" MultiSelectionTextFunc="GenerateSelectedText" SelectAll="true">
|
||||
<MudSelectItem Value="@("Normal")">Normal</MudSelectItem>
|
||||
<MudSelectItem Value="@("Info")">Info</MudSelectItem>
|
||||
<MudSelectItem Value="@("Warning")">Warning</MudSelectItem>
|
||||
<MudSelectItem Value="@("Critical")">Critical</MudSelectItem>
|
||||
</MudSelect>
|
||||
</MudItem>
|
||||
<MudItem md="2">
|
||||
<MudButton ButtonType="ButtonType.Submit" FullWidth="true" Color="Color.Primary" EndIcon="@Icons.Material.Filled.Search" Variant="Variant.Filled" Class="mt-6">Filter</MudButton>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
</EditForm>
|
||||
</MudCardContent>
|
||||
</MudCard>
|
||||
|
||||
<MudTable Items="Results"
|
||||
T="Lantean.QBitTorrentClient.Models.Log"
|
||||
Hover="true"
|
||||
FixedHeader="true"
|
||||
HeaderClass="table-head-bordered"
|
||||
Dense="true"
|
||||
Breakpoint="Breakpoint.None"
|
||||
Bordered="true"
|
||||
Square="true"
|
||||
LoadingProgressColor="Color.Info"
|
||||
HorizontalScrollbar="true"
|
||||
Virtualize="true"
|
||||
AllowUnsorted="false"
|
||||
SelectOnRowClick="false"
|
||||
Class="search-list"
|
||||
RowClassFunc="RowClass">
|
||||
<HeaderContent>
|
||||
<MudTh>Id</MudTh>
|
||||
<MudTh>Message</MudTh>
|
||||
<MudTh>Timestamp</MudTh>
|
||||
<MudTh>Log Type</MudTh>
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="Od">@context.Id</MudTd>
|
||||
<MudTd DataLabel="Message">@context.Message</MudTd>
|
||||
<MudTd DataLabel="Timestamp">@DisplayHelpers.DateTime(context.Timestamp)</MudTd>
|
||||
<MudTd DataLabel="Log Type">@context.Type</MudTd>
|
||||
</RowTemplate>
|
||||
</MudTable>
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
using Lantean.QBitTorrentClient;
|
||||
using Blazored.LocalStorage;
|
||||
using Lantean.QBitTorrentClient;
|
||||
using Lantean.QBTMudBlade.Models;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
using MudBlazor;
|
||||
using System.Net;
|
||||
|
||||
namespace Lantean.QBTMudBlade.Pages
|
||||
{
|
||||
public partial class Log
|
||||
public partial class Log : IAsyncDisposable
|
||||
{
|
||||
private readonly bool _refreshEnabled = true;
|
||||
private const string _selectedTypesStorageKey = "Log.SelectedTypes";
|
||||
|
||||
private readonly CancellationTokenSource _timerCancellationToken = new();
|
||||
private bool _disposedValue;
|
||||
|
||||
[Inject]
|
||||
protected IApiClient ApiClient { get; set; } = default!;
|
||||
|
||||
@@ -16,24 +25,129 @@ namespace Lantean.QBTMudBlade.Pages
|
||||
[Inject]
|
||||
protected NavigationManager NavigationManager { get; set; } = default!;
|
||||
|
||||
[CascadingParameter]
|
||||
public MainData? MainData { get; set; }
|
||||
[Inject]
|
||||
protected ILocalStorageService LocalStorage { get; set; } = default!;
|
||||
|
||||
[CascadingParameter(Name = "DrawerOpen")]
|
||||
public bool DrawerOpen { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? Hash { get; set; }
|
||||
protected LogForm Model { get; set; } = new LogForm();
|
||||
|
||||
protected int ActiveTab { get; set; } = 0;
|
||||
protected List<QBitTorrentClient.Models.Log>? Results { get; private set; }
|
||||
|
||||
protected int RefreshInterval => MainData?.ServerState.RefreshInterval ?? 1500;
|
||||
protected MudSelect<string>? CategoryMudSelect { get; set; }
|
||||
|
||||
protected ServerState? ServerState => MainData?.ServerState;
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var selectedTypes = await LocalStorage.GetItemAsync<IEnumerable<string>>(_selectedTypesStorageKey);
|
||||
if (selectedTypes is not null)
|
||||
{
|
||||
Model.SelectedTypes = selectedTypes;
|
||||
}
|
||||
else
|
||||
{
|
||||
Model.SelectedTypes = ["Normal"];
|
||||
}
|
||||
|
||||
await DoSearch();
|
||||
}
|
||||
|
||||
protected void NavigateBack()
|
||||
{
|
||||
NavigationManager.NavigateTo("/");
|
||||
}
|
||||
|
||||
protected async Task SelectedValuesChanged(IEnumerable<string> values)
|
||||
{
|
||||
Model.SelectedTypes = values;
|
||||
|
||||
await LocalStorage.SetItemAsync(_selectedTypesStorageKey, Model.SelectedTypes);
|
||||
}
|
||||
|
||||
protected static string GenerateSelectedText(List<string> values)
|
||||
{
|
||||
if (values.Count == 4)
|
||||
{
|
||||
return "All";
|
||||
}
|
||||
|
||||
return $"{values.Count} selected";
|
||||
}
|
||||
|
||||
protected Task Submit(EditContext editContext)
|
||||
{
|
||||
return DoSearch();
|
||||
}
|
||||
|
||||
private async Task DoSearch()
|
||||
{
|
||||
var results = await ApiClient.GetLog(Model.Normal, Model.Info, Model.Warning, Model.Critical, Model.LastKnownId);
|
||||
if (results.Count > 0)
|
||||
{
|
||||
Results ??= [];
|
||||
Results.AddRange(results);
|
||||
Model.LastKnownId = results[^1].Id;
|
||||
}
|
||||
}
|
||||
|
||||
protected static string RowClass(QBitTorrentClient.Models.Log log, int index)
|
||||
{
|
||||
return $"log-{log.Type.ToString().ToLower()}";
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||
await DisposeAsync(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual async Task DisposeAsync(bool disposing)
|
||||
{
|
||||
if (!_disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_timerCancellationToken.Cancel();
|
||||
_timerCancellationToken.Dispose();
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
_disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (!_refreshEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!firstRender)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using (var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(1500)))
|
||||
{
|
||||
while (!_timerCancellationToken.IsCancellationRequested && await timer.WaitForNextTickAsync())
|
||||
{
|
||||
try
|
||||
{
|
||||
await DoSearch();
|
||||
}
|
||||
catch (HttpRequestException exception) when (exception.StatusCode == HttpStatusCode.Forbidden || exception.StatusCode == HttpStatusCode.NotFound)
|
||||
{
|
||||
_timerCancellationToken.CancelIfNotDisposed();
|
||||
return;
|
||||
}
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
using Lantean.QBitTorrentClient;
|
||||
using Lantean.QBTMudBlade.Models;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Net;
|
||||
|
||||
namespace Lantean.QBTMudBlade.Pages
|
||||
@@ -15,7 +14,7 @@ namespace Lantean.QBTMudBlade.Pages
|
||||
[Inject]
|
||||
protected NavigationManager NavigationManager { get; set; } = default!;
|
||||
|
||||
protected LoginModel Model { get; set; } = new LoginModel();
|
||||
protected LoginForm Model { get; set; } = new LoginForm();
|
||||
|
||||
protected string? ApiError { get; set; }
|
||||
|
||||
@@ -49,19 +48,8 @@ namespace Lantean.QBTMudBlade.Pages
|
||||
#if DEBUG
|
||||
protected override Task OnInitializedAsync()
|
||||
{
|
||||
return DoLogin("admin", "6K3mtPNnQ");
|
||||
return DoLogin("admin", "STMeVwB22");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public class LoginModel
|
||||
{
|
||||
[Required]
|
||||
[NotNull]
|
||||
public string? Username { get; set; }
|
||||
|
||||
[Required]
|
||||
[NotNull]
|
||||
public string? Password { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<NavigationLock ConfirmExternalNavigation="@(UpdatePreferences is not null)" OnBeforeInternalNavigation="ValidateExit" />
|
||||
|
||||
<MudToolBar DisableGutters="true" Dense="true">
|
||||
<MudToolBar Gutters="false" Dense="true">
|
||||
@if (!DrawerOpen)
|
||||
{
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.NavigateBefore" OnClick="NavigateBack" />
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
@page "/rss"
|
||||
@layout OtherLayout
|
||||
|
||||
<MudToolBar DisableGutters="true" Dense="true">
|
||||
<MudToolBar Gutters="false" Dense="true">
|
||||
@if (!DrawerOpen)
|
||||
{
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.NavigateBefore" OnClick="NavigateBack" Title="Back to torrent list" />
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.NavigateBefore" OnClick="NavigateBack" title="Back to torrent list" />
|
||||
<MudDivider Vertical="true" />
|
||||
}
|
||||
<MudDivider Vertical="true" />
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
@page "/search"
|
||||
@layout OtherLayout
|
||||
|
||||
<MudToolBar DisableGutters="true" Dense="true">
|
||||
<MudToolBar Gutters="false" Dense="true">
|
||||
@if (!DrawerOpen)
|
||||
{
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.NavigateBefore" OnClick="NavigateBack" Title="Back to torrent list" />
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.NavigateBefore" OnClick="NavigateBack" title="Back to torrent list" />
|
||||
<MudDivider Vertical="true" />
|
||||
}
|
||||
<MudDivider Vertical="true" />
|
||||
<MudText Class="pl-5 no-wrap">Search</MudText>
|
||||
</MudToolBar>
|
||||
|
||||
<MudCard Elevation="1" Class="ml-4 mr-4 mb-4 mt-4">
|
||||
<MudCardContent Class="pt-0">
|
||||
<MudCard Elevation="1" Class="ml-4 mr-4 mb-4">
|
||||
<MudCardContent>
|
||||
<EditForm Model="Model" OnValidSubmit="DoSearch">
|
||||
<MudGrid>
|
||||
<MudItem xs="12" md="6">
|
||||
<MudTextField T="string" Label="Search" Value="SearchText" ValueChanged="SearchTextChanged" ShrinkLabel Variant="Variant.Outlined" />
|
||||
<MudTextField T="string" Label="Criteria" @bind-Value="Model.SearchText" ShrinkLabel Variant="Variant.Outlined" />
|
||||
</MudItem>
|
||||
<MudItem md="2">
|
||||
<MudSelect T="string" Label="Categories" Value="SelectedCategory" ValueChanged="SelectedCategoryChanged" ShrinkLabel Variant="Variant.Outlined">
|
||||
<MudSelect T="string" Label="Categories" @bind-Value="Model.SelectedCategory" ShrinkLabel Variant="Variant.Outlined">
|
||||
@foreach (var (value, name) in Categories)
|
||||
{
|
||||
<MudSelectItem Value="value">@name</MudSelectItem>
|
||||
@@ -30,7 +31,7 @@
|
||||
</MudSelect>
|
||||
</MudItem>
|
||||
<MudItem md="2">
|
||||
<MudSelect T="string" Label="Plugins" Value="SelectedPlugin" ValueChanged="SelectedPluginChanged" ShrinkLabel Variant="Variant.Outlined">
|
||||
<MudSelect T="string" Label="Plugins" @bind-Value="Model.SelectedPlugin" ShrinkLabel Variant="Variant.Outlined">
|
||||
<MudSelectItem Value="@("all")">All</MudSelectItem>
|
||||
<MudDivider />
|
||||
@foreach (var (value, name) in Plugins)
|
||||
@@ -40,10 +41,11 @@
|
||||
</MudSelect>
|
||||
</MudItem>
|
||||
<MudItem md="2">
|
||||
<MudButton OnClick="DoSearch" FullWidth="true" Variant="Variant.Outlined">@(_searchId is null ? "Search" : "Stop")</MudButton>
|
||||
<MudButton ButtonType="ButtonType.Submit" FullWidth="true" Color="Color.Primary" EndIcon="@Icons.Material.Filled.Search" Variant="Variant.Filled" Class="mt-6">@(_searchId is null ? "Search" : "Stop")</MudButton>
|
||||
</MudItem>
|
||||
|
||||
</MudGrid>
|
||||
</EditForm>
|
||||
</MudCardContent>
|
||||
</MudCard>
|
||||
|
||||
@@ -62,7 +64,7 @@
|
||||
Virtualize="true"
|
||||
AllowUnsorted="false"
|
||||
SelectOnRowClick="false"
|
||||
Class="details-list">
|
||||
Class="search-list">
|
||||
<HeaderContent>
|
||||
<MudTh>Name</MudTh>
|
||||
<MudTh>Size</MudTh>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Lantean.QBitTorrentClient;
|
||||
using Lantean.QBTMudBlade.Models;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
using MudBlazor;
|
||||
|
||||
namespace Lantean.QBTMudBlade.Pages
|
||||
@@ -33,21 +34,11 @@ namespace Lantean.QBTMudBlade.Pages
|
||||
[Parameter]
|
||||
public string? Hash { get; set; }
|
||||
|
||||
protected int ActiveTab { get; set; } = 0;
|
||||
|
||||
protected int RefreshInterval => MainData?.ServerState.RefreshInterval ?? 1500;
|
||||
|
||||
protected ServerState? ServerState => MainData?.ServerState;
|
||||
|
||||
protected string? SearchText { get; set; }
|
||||
|
||||
protected string SelectedPlugin { get; set; } = "all";
|
||||
|
||||
protected string SelectedCategory { get; set; } = "all";
|
||||
protected SearchForm Model { get; set; } = new SearchForm();
|
||||
|
||||
protected Dictionary<string, string> Plugins => _plugins is null ? [] : _plugins.ToDictionary(a => a.Name, a => a.FullName);
|
||||
|
||||
protected Dictionary<string, string> Categories => GetCategories(SelectedPlugin);
|
||||
protected Dictionary<string, string> Categories => GetCategories(Model.SelectedPlugin);
|
||||
|
||||
protected IEnumerable<QBitTorrentClient.Models.SearchResult>? Results => _searchResults?.Results;
|
||||
|
||||
@@ -97,21 +88,6 @@ namespace Lantean.QBTMudBlade.Pages
|
||||
NavigationManager.NavigateTo("/");
|
||||
}
|
||||
|
||||
protected void SearchTextChanged(string value)
|
||||
{
|
||||
SearchText = value;
|
||||
}
|
||||
|
||||
protected void SelectedCategoryChanged(string value)
|
||||
{
|
||||
SelectedCategory = value;
|
||||
}
|
||||
|
||||
protected void SelectedPluginChanged(string value)
|
||||
{
|
||||
SelectedPlugin = value;
|
||||
}
|
||||
|
||||
private Dictionary<string, string> GetCategories(string plugin)
|
||||
{
|
||||
if (_plugins is null)
|
||||
@@ -133,17 +109,17 @@ namespace Lantean.QBTMudBlade.Pages
|
||||
return pluginItem.SupportedCategories.ToDictionary(a => a.Id, a => a.Name);
|
||||
}
|
||||
|
||||
protected async Task DoSearch()
|
||||
protected async Task DoSearch(EditContext editContext)
|
||||
{
|
||||
if (_searchId is null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(SearchText))
|
||||
if (string.IsNullOrEmpty(Model.SearchText))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_searchResults = null;
|
||||
_searchId = await ApiClient.StartSearch(SearchText, [SelectedPlugin], SelectedCategory);
|
||||
_searchId = await ApiClient.StartSearch(Model.SearchText, [Model.SelectedPlugin], Model.SelectedCategory);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
@page "/statistics"
|
||||
@layout OtherLayout
|
||||
|
||||
<MudToolBar DisableGutters="true" Dense="true">
|
||||
<MudToolBar Gutters="false" Dense="true">
|
||||
@if (!DrawerOpen)
|
||||
{
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.NavigateBefore" OnClick="NavigateBack" Title="Back to torrent list" />
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.NavigateBefore" OnClick="NavigateBack" title="Back to torrent list" />
|
||||
<MudDivider Vertical="true" />
|
||||
}
|
||||
<MudDivider Vertical="true" />
|
||||
|
||||
@@ -22,13 +22,14 @@ namespace Lantean.QBTMudBlade.Pages
|
||||
[CascadingParameter(Name = "DrawerOpen")]
|
||||
public bool DrawerOpen { get; set; }
|
||||
|
||||
[CascadingParameter(Name = "RefreshInterval")]
|
||||
public int RefreshInterval { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? Hash { get; set; }
|
||||
|
||||
protected int ActiveTab { get; set; } = 0;
|
||||
|
||||
protected int RefreshInterval => MainData?.ServerState.RefreshInterval ?? 1500;
|
||||
|
||||
protected ServerState? ServerState => MainData?.ServerState;
|
||||
|
||||
protected void NavigateBack()
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
@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" />
|
||||
<MudToolBar Gutters="false" 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 RenderType="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" />
|
||||
<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>
|
||||
|
||||
@@ -171,3 +171,23 @@ td.no-wrap {
|
||||
height: calc(100vh - 200px);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.search-list .mud-table-container {
|
||||
height: calc(100vh - 260px);
|
||||
}
|
||||
|
||||
tr.log-normal td {
|
||||
color: var(--mud-palette-text-primary) !important;
|
||||
}
|
||||
|
||||
tr.log-info td {
|
||||
color: var(--mud-palette-info) !important;
|
||||
}
|
||||
|
||||
tr.log-warning td {
|
||||
color: var(--mud-palette-warning) !important;
|
||||
}
|
||||
|
||||
tr.log-critical td {
|
||||
color: var(--mud-palette-error) !important;
|
||||
}
|
||||
@@ -887,10 +887,10 @@ namespace Lantean.QBitTorrentClient
|
||||
{
|
||||
var content = new FormUrlEncodedBuilder()
|
||||
.AddAllOrPipeSeparated("hashes", all, hashes)
|
||||
.Add("enable", value)
|
||||
.Add("value", value)
|
||||
.ToFormUrlEncodedContent();
|
||||
|
||||
var response = await _httpClient.PostAsync("torrents/setFOrceStart", content);
|
||||
var response = await _httpClient.PostAsync("torrents/setForceStart", content);
|
||||
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
@@ -1104,17 +1104,31 @@ namespace Lantean.QBitTorrentClient
|
||||
}
|
||||
|
||||
private async Task<IReadOnlyList<T>> GetJsonList<T>(HttpContent content)
|
||||
{
|
||||
try
|
||||
{
|
||||
var items = await GetJson<IEnumerable<T>>(content);
|
||||
|
||||
return items.ToList().AsReadOnly();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<IReadOnlyDictionary<TKey, TValue>> GetJsonDictionary<TKey, TValue>(HttpContent content) where TKey : notnull
|
||||
{
|
||||
try
|
||||
{
|
||||
var items = await GetJson<IDictionary<TKey, TValue>>(content);
|
||||
|
||||
return items.AsReadOnly();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return new Dictionary<TKey, TValue>().AsReadOnly();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user