Reorganise folder structure, add document based keyboard events, update remaining tables to use DynamicTable, and general polish.

This commit is contained in:
ahjephson
2024-08-09 20:11:55 +01:00
parent 08d29e0854
commit cf12078de9
73 changed files with 1422 additions and 819 deletions

View File

@@ -12,8 +12,8 @@
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
<PackageReference Include="xunit" Version="2.8.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
<PackageReference Include="xunit" Version="2.9.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@@ -0,0 +1,25 @@
<MudDialog>
<DialogContent>
<table width="100%">
<tbody>
<tr>
<td style="width: 70%"><MudTextField T="string" Label="IP" Value="@IP" ValueChanged="SetIP" Required Variant="Variant.Outlined" /></td>
<td style="width: 30%"><MudNumericField T="int?" Label="Port" Value="@Port" ValueChanged="SetPort" Required Variant="Variant.Outlined" /></td>
<td><MudIconButton Icon="@Icons.Material.Filled.Add" OnClick="AddTracker" /></td>
</tr>
@foreach (var peer in Peers)
{
var peerRef = peer;
<tr>
<td>@peer</td>
<td><MudIconButton Icon="@Icons.Material.Filled.Delete" OnClick="@(e => DeletePeer(peerRef))" /></td>
</tr>
}
</tbody>
</table>
</DialogContent>
<DialogActions>
<MudButton OnClick="Cancel">Cancel</MudButton>
<MudButton Color="Color.Primary" OnClick="Submit">Save</MudButton>
</DialogActions>
</MudDialog>

View File

@@ -0,0 +1,54 @@
using Lantean.QBitTorrentClient.Models;
using Microsoft.AspNetCore.Components;
using MudBlazor;
namespace Lantean.QBTMudBlade.Components.Dialogs
{
public partial class AddPeerDialog
{
[CascadingParameter]
public MudDialogInstance MudDialog { get; set; } = default!;
protected HashSet<PeerId> Peers { get; } = [];
protected string? IP { get; set; }
protected int? Port { get; set; }
protected void AddTracker()
{
if (string.IsNullOrEmpty(IP) || !Port.HasValue)
{
return;
}
Peers.Add(new PeerId(IP, Port.Value));
IP = null;
Port = null;
}
protected void SetIP(string value)
{
IP = value;
}
protected void SetPort(int? value)
{
Port = value;
}
protected void DeletePeer(PeerId peer)
{
Peers.Remove(peer);
}
protected void Cancel()
{
MudDialog.Cancel();
}
protected void Submit()
{
MudDialog.Close(Peers);
}
}
}

View File

@@ -32,10 +32,10 @@
</MudSelect>
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Start torrent" @bind-Value="StartTorrent" />
<FieldSwitch Label="Start torrent" @bind-Value="StartTorrent" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Add to top of queue" @bind-Value="AddToTopOfQueue" />
<FieldSwitch Label="Add to top of queue" @bind-Value="AddToTopOfQueue" />
</MudItem>
<MudItem xs="12">
<MudSelect Label="Stop condition" @bind-Value="StopCondition" Variant="Variant.Outlined">
@@ -45,7 +45,7 @@
</MudSelect>
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Skip hash check" @bind-Value="SkipHashCheck" />
<FieldSwitch Label="Skip hash check" @bind-Value="SkipHashCheck" />
</MudItem>
<MudSelect Label="Content layout" @bind-Value="ContentLayout" Variant="Variant.Outlined">
<MudSelectItem Value="@("Original")">Original</MudSelectItem>
@@ -53,10 +53,10 @@
<MudSelectItem Value="@("NoSubfolder")">Don't create subfolder'</MudSelectItem>
</MudSelect>
<MudItem xs="12">
<MudFieldSwitch Label="Download in sequentual order" @bind-Value="DownloadInSequentialOrder" />
<FieldSwitch Label="Download in sequentual order" @bind-Value="DownloadInSequentialOrder" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Download first and last pieces first" @bind-Value="DownloadFirstAndLastPiecesFirst" />
<FieldSwitch 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" />

View File

@@ -3,7 +3,7 @@
<table width="100%">
<tbody>
<tr>
<td style="width: 100%"><MudTextField T="string" Label="Tag" Value="@Tracker" ValueChanged="SetTracker" Required Variant="Variant.Outlined" /></td>
<td style="width: 100%"><MudTextField T="string" Label="Tracker" Value="@Tracker" ValueChanged="SetTracker" Required Variant="Variant.Outlined" /></td>
<td><MudIconButton Icon="@Icons.Material.Filled.Add" OnClick="AddTracker" /></td>
</tr>
@foreach (var tracker in Trackers)

View File

@@ -105,7 +105,7 @@ namespace Lantean.QBTMudBlade.Components.Dialogs
protected async Task AddCategory()
{
var addedCategoy = await DialogService.ShowAddCategoryDialog(ApiClient);
var addedCategoy = await DialogService.InvokeAddCategoryDialog(ApiClient);
if (addedCategoy is null)
{
return;

View File

@@ -0,0 +1,24 @@
<MudDialog>
<DialogContent>
<table width="100%">
<tbody>
<tr>
<td style="width: 100%"><MudTextField T="string" Label="@(Label)" Value="@Value" ValueChanged="SetValue" Required Variant="Variant.Outlined" /></td>
<td><MudIconButton Icon="@Icons.Material.Filled.Add" OnClick="AddValue" /></td>
</tr>
@foreach (var value in NewValues)
{
var valueRef = value;
<tr>
<td>@value</td>
<td><MudIconButton Icon="@Icons.Material.Filled.Delete" OnClick="@(e => DeleteValue(valueRef))" /></td>
</tr>
}
</tbody>
</table>
</DialogContent>
<DialogActions>
<MudButton OnClick="Cancel">Cancel</MudButton>
<MudButton Color="Color.Primary" OnClick="Submit">Save</MudButton>
</DialogActions>
</MudDialog>

View File

@@ -0,0 +1,62 @@
using Microsoft.AspNetCore.Components;
using MudBlazor;
namespace Lantean.QBTMudBlade.Components.Dialogs
{
public partial class MultipleFieldDialog
{
[CascadingParameter]
public MudDialogInstance MudDialog { get; set; } = default!;
[Parameter]
public string Label { get; set; } = default!;
[Parameter]
public HashSet<string> Values { get; set; } = [];
protected HashSet<string> NewValues { get; } = [];
protected string? Value { get; set; }
protected override void OnParametersSet()
{
if (NewValues.Count == 0)
{
foreach (var value in Values)
{
NewValues.Add(value);
}
}
}
protected void AddValue()
{
if (string.IsNullOrEmpty(Value))
{
return;
}
NewValues.Add(Value);
Value = null;
}
protected void SetValue(string tracker)
{
Value = tracker;
}
protected void DeleteValue(string tracker)
{
NewValues.Remove(tracker);
}
protected void Cancel()
{
MudDialog.Cancel();
}
protected void Submit()
{
MudDialog.Close(NewValues);
}
}
}

View File

@@ -33,12 +33,11 @@ namespace Lantean.QBTMudBlade.Components
return Task.CompletedTask;
}
public async Task RecoverAndClearErrors()
public Task RecoverAndClearErrors()
{
Recover();
_exceptions.Clear();
await OnClear.InvokeAsync();
return ClearErrors();
}
public async Task ClearErrors()

View File

@@ -1,5 +1,9 @@
<MudToolBar Gutters="false" Dense="true">
<MudIconButton Icon="@Icons.Material.Filled.DriveFileRenameOutline" OnClick="RenameFile" title="Rename" />
<ContextMenu @ref="ContextMenu" Dense="true">
<MudMenuItem Icon="@Icons.Material.Filled.DriveFileRenameOutline" OnClick="RenameFileContextMenu">Rename</MudMenuItem>
</ContextMenu>
<MudToolBar Gutters="false" Dense="true">
<MudIconButton Icon="@Icons.Material.Filled.DriveFileRenameOutline" OnClick="RenameFileToolbar" title="Rename" />
<MudDivider Vertical="true" />
<MudIconButton Icon="@Icons.Material.Outlined.ViewColumn" Color="Color.Inherit" OnClick="ColumnOptions" title="Choose Columns" />
<MudDivider Vertical="true" />
@@ -25,10 +29,13 @@
ColumnDefinitions="Columns"
Items="Files"
MultiSelection="false"
SelectOnRowClick="true"
PreSorted="true"
SelectedItemChanged="SelectedItemChanged"
SortColumnChanged="SortColumnChanged"
SortDirectionChanged="SortDirectionChanged"
OnTableDataContextMenu="TableDataContextMenu"
OnTableDataLongPress="TableDataLongPress"
Class="file-list"
/>
@@ -39,10 +46,10 @@
{
return context => __builder =>
{
<div style="@($"margin-left: {context.Data.Level * 14}px")">
<div style="@($"margin-left: {(context.Data.Level * 14) + (context.Data.Level >= 1 ? 16 : 0)}px")">
@if (context.Data.IsFolder)
{
<MudIconButton Edge="Edge.Start" ButtonType="ButtonType.Button" Icon="@(ExpandedNodes.Contains(context.Data.Name) ? Icons.Material.Filled.KeyboardArrowDown : Icons.Material.Filled.KeyboardArrowRight)" OnClick="@(c => ToggleNode(context.Data))"></MudIconButton>
<MudIconButton Class="folder-button" Edge="Edge.Start" ButtonType="ButtonType.Button" Icon="@(ExpandedNodes.Contains(context.Data.Name) ? Icons.Material.Filled.KeyboardArrowDown : Icons.Material.Filled.KeyboardArrowRight)" OnClick="@(c => ToggleNode(context.Data))"></MudIconButton>
<MudIcon Icon="@Icons.Material.Filled.Folder" Class="pt-0" Style="margin-right: 4px; position: relative; top: 7px; margin-left: -15px" />
}
@context.Data.DisplayName

View File

@@ -1,6 +1,7 @@
using Blazored.LocalStorage;
using Lantean.QBitTorrentClient;
using Lantean.QBTMudBlade.Components.Dialogs;
using Lantean.QBTMudBlade.Components.UI;
using Lantean.QBTMudBlade.Filter;
using Lantean.QBTMudBlade.Models;
using Lantean.QBTMudBlade.Services;
@@ -55,13 +56,15 @@ namespace Lantean.QBTMudBlade.Components
protected ContentItem? SelectedItem { get; set; }
protected ContentItem? ContextMenuItem { get; set; }
protected string? SearchText { get; set; }
public IEnumerable<Func<ContentItem, bool>>? Filters { get; set; }
private DynamicTable<ContentItem>? Table { get; set; }
private ContextMenu? ContextMenu { get; set; }
public FilesTab()
{
@@ -123,22 +126,6 @@ namespace Lantean.QBTMudBlade.Components
GC.SuppressFinalize(this);
}
protected static float CalculateProgress(IEnumerable<ContentItem> items)
{
return (float)items.Sum(i => i.Downloaded) / items.Sum(i => i.Size);
}
protected static Priority GetPriority(IEnumerable<ContentItem> items)
{
var distinctPriorities = items.Select(i => i.Priority).Distinct();
if (distinctPriorities.Count() == 1)
{
return distinctPriorities.First();
}
return Priority.Mixed;
}
protected virtual async Task DisposeAsync(bool disposing)
{
if (!_disposedValue)
@@ -155,19 +142,42 @@ namespace Lantean.QBTMudBlade.Components
}
}
protected static Priority GetPriority(IEnumerable<ContentItem> items)
{
var distinctPriorities = items.Select(i => i.Priority).Distinct();
if (distinctPriorities.Count() == 1)
{
return distinctPriorities.First();
}
return Priority.Mixed;
}
protected void SearchTextChanged(string value)
{
SearchText = value;
}
protected async Task EnabledValueChanged(ContentItem contentItem, bool value)
protected Task TableDataContextMenu(TableDataContextMenuEventArgs<ContentItem> eventArgs)
{
if (Hash is null)
return ShowContextMenu(eventArgs.Item, eventArgs.MouseEventArgs);
}
protected Task TableDataLongPress(TableDataLongPressEventArgs<ContentItem> eventArgs)
{
return ShowContextMenu(eventArgs.Item, eventArgs.LongPressEventArgs);
}
private async Task ShowContextMenu(ContentItem? contentItem, EventArgs eventArgs)
{
ContextMenuItem = contentItem;
if (ContextMenu is null)
{
return;
}
await ApiClient.SetFilePriority(Hash, [contentItem.Index], MapPriority(value ? Priority.Normal : Priority.DoNotDownload));
await ContextMenu.OpenMenuAsync(eventArgs);
}
protected override async Task OnAfterRenderAsync(bool firstRender)
@@ -267,21 +277,43 @@ namespace Lantean.QBTMudBlade.Components
await ApiClient.SetFilePriority(Hash, fileIndexes, MapPriority(priority));
}
protected async Task RenameFile()
protected Task RenameFileToolbar()
{
if (Hash is null || FileList is null || SelectedItem is null)
if (SelectedItem is null)
{
return Task.CompletedTask;
}
return RenameFiles(SelectedItem);
}
protected Task RenameFileContextMenu()
{
if (ContextMenuItem is null)
{
return Task.CompletedTask;
}
return RenameFiles(ContextMenuItem);
}
private async Task RenameFiles(params ContentItem[] contentItems)
{
if (Hash is null || contentItems.Length == 0)
{
return;
}
var contentItem = FileList.Values.FirstOrDefault(c => c.Index == SelectedItem.Index);
if (contentItem is null)
if (contentItems.Length == 1)
{
return;
var contentItem = contentItems[0];
var name = contentItem.GetFileName();
await DialogService.ShowSingleFieldDialog("Rename", "New name", name, async value => await ApiClient.RenameFile(Hash, contentItem.Name, contentItem.Path + value));
}
else
{
await DialogService.InvokeRenameFilesDialog(ApiClient, Hash);
}
var name = contentItem.GetFileName();
await DialogService.ShowSingleFieldDialog("Rename", "New name", name, async value => await ApiClient.RenameFile(Hash, contentItem.Name, contentItem.Path + value));
}
protected void SortColumnChanged(string sortColumn)
@@ -511,7 +543,7 @@ namespace Lantean.QBTMudBlade.Components
public static List<ColumnDefinition<ContentItem>> ColumnsDefinitions { get; } =
[
CreateColumnDefinition("Name", c => c.Name, width: 400, initialDirection: SortDirection.Ascending, classFunc: c => c.IsFolder ? "pa-0" : "pa-3"),
CreateColumnDefinition("Name", c => c.Name, width: 400, initialDirection: SortDirection.Ascending, classFunc: c => c.IsFolder ? "pa-0" : "pa-2"),
CreateColumnDefinition("Total Size", c => c.Size, c => DisplayHelpers.Size(c.Size)),
CreateColumnDefinition("Progress", c => c.Progress, ProgressBarColumn, tdClass: "table-progress pl-2 pr-2"),
CreateColumnDefinition("Priority", c => c.Priority, tdClass: "table-select pa-0"),

View File

@@ -1,8 +1,8 @@
<ContextMenu @ref="StatusContextMenu" Dense="true" InsideDrawer="true">
<ContextMenu @ref="StatusContextMenu" Dense="true" AdjustmentY="-60">
@TorrentControls(_statusType)
</ContextMenu>
<ContextMenu @ref="CategoryContextMenu" Dense="true" InsideDrawer="true">
<ContextMenu @ref="CategoryContextMenu" Dense="true" AdjustmentY="-60">
<MudMenuItem Icon="@Icons.Material.Outlined.AddCircle" IconColor="Color.Info" OnClick="AddCategory">Add category</MudMenuItem>
@if (IsCategoryTarget)
{
@@ -14,7 +14,7 @@
@TorrentControls(_categoryType)
</ContextMenu>
<ContextMenu @ref="TagContextMenu" Dense="true" InsideDrawer="true">
<ContextMenu @ref="TagContextMenu" Dense="true" AdjustmentY="-60">
<MudMenuItem Icon="@Icons.Material.Outlined.AddCircle" IconColor="Color.Info" OnClick="AddTag">Add tag</MudMenuItem>
@if (IsTagTarget)
{
@@ -25,7 +25,7 @@
@TorrentControls(_tagType)
</ContextMenu>
<ContextMenu @ref="TrackerContextMenu" Dense="true" InsideDrawer="true">
<ContextMenu @ref="TrackerContextMenu" Dense="true" AdjustmentY="-60">
<MudMenuItem Icon="@Icons.Material.Filled.Delete" IconColor="Color.Error" OnClick="RemoveUnusedCategories">Remove tracker</MudMenuItem>
<MudDivider />
@TorrentControls(_trackerType)
@@ -36,25 +36,25 @@
@foreach (var (status, count) in Statuses)
{
var (icon, color) = DisplayHelpers.GetStatusIcon(status);
<FakeNavLink Class="filter-menu-item" Active="@(Status == status)" Icon="@icon" IconColor="@color" OnClick="@(e => StatusValueChanged(status))" OnContextMenu="@(e => StatusOnContextMenu(e, status))" OnLongPress="@(e => StatusOnLongPress(e, status))">@($"{status.GetStatusName()} ({count})")</FakeNavLink>
<CustomNavLink Class="filter-menu-item" Active="@(Status == status)" Icon="@icon" IconColor="@color" OnClick="@(e => StatusValueChanged(status))" OnContextMenu="@(e => StatusOnContextMenu(e, status))" OnLongPress="@(e => StatusOnLongPress(e, status))">@($"{status.GetStatusName()} ({count})")</CustomNavLink>
}
</MudNavGroup>
<MudNavGroup Title="Categories" @bind-Expanded="_categoriesExpanded">
@foreach (var (category, count) in Categories)
{
<FakeNavLink Class="filter-menu-item" Active="@(Category == category)" Icon="@Icons.Material.Filled.List" IconColor="Color.Info" OnClick="@(e => CategoryValueChanged(category))" OnContextMenu="@(e => CategoryOnContextMenu(e, category))" OnLongPress="@(e => CategoryOnLongPress(e, category))">@($"{category} ({count})")</FakeNavLink>
<CustomNavLink Class="filter-menu-item" Active="@(Category == category)" Icon="@Icons.Material.Filled.List" IconColor="Color.Info" OnClick="@(e => CategoryValueChanged(category))" OnContextMenu="@(e => CategoryOnContextMenu(e, category))" OnLongPress="@(e => CategoryOnLongPress(e, category))">@($"{category} ({count})")</CustomNavLink>
}
</MudNavGroup>
<MudNavGroup Title="Tags" @bind-Expanded="_tagsExpanded">
@foreach (var (tag, count) in Tags)
{
<FakeNavLink Class="filter-menu-item" Active="@(Tag == tag)" Icon="@Icons.Material.Filled.Label" IconColor="Color.Info" OnClick="@(e => TagValueChanged(tag))" OnContextMenu="@(e => TagOnContextMenu(e, tag))" OnLongPress="@(e => TagOnLongPress(e, tag))">@($"{tag} ({count})")</FakeNavLink>
<CustomNavLink Class="filter-menu-item" Active="@(Tag == tag)" Icon="@Icons.Material.Filled.Label" IconColor="Color.Info" OnClick="@(e => TagValueChanged(tag))" OnContextMenu="@(e => TagOnContextMenu(e, tag))" OnLongPress="@(e => TagOnLongPress(e, tag))">@($"{tag} ({count})")</CustomNavLink>
}
</MudNavGroup>
<MudNavGroup Title="Trackers" @bind-Expanded="_trackersExpanded">
@foreach (var (tracker, count) in Trackers)
{
<FakeNavLink Class="filter-menu-item" Active="@(Tracker == tracker)" Icon="@Icons.Material.Filled.PinDrop" IconColor="Color.Info" OnClick="@(e => TrackerValueChanged(tracker))" OnContextMenu="@(e => TrackerOnContextMenu(e, tracker))" OnLongPress="@(e => TrackerOnLongPress(e, tracker))">@($"{tracker} ({count})")</FakeNavLink>
<CustomNavLink Class="filter-menu-item" Active="@(Tracker == tracker)" Icon="@Icons.Material.Filled.PinDrop" IconColor="Color.Info" OnClick="@(e => TrackerValueChanged(tracker))" OnContextMenu="@(e => TrackerOnContextMenu(e, tracker))" OnLongPress="@(e => TrackerOnLongPress(e, tracker))">@($"{tracker} ({count})")</CustomNavLink>
}
</MudNavGroup>
</MudNavMenu>

View File

@@ -1,10 +1,10 @@
using Blazored.LocalStorage;
using Lantean.QBitTorrentClient;
using Lantean.QBTMudBlade.Components.UI;
using Lantean.QBTMudBlade.Models;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using MudBlazor;
using System.Collections.Generic;
namespace Lantean.QBTMudBlade.Components
{
@@ -271,7 +271,7 @@ namespace Lantean.QBTMudBlade.Components
protected async Task AddCategory()
{
await DialogService.ShowAddCategoryDialog(ApiClient);
await DialogService.InvokeAddCategoryDialog(ApiClient);
}
protected async Task EditCategory()
@@ -281,7 +281,7 @@ namespace Lantean.QBTMudBlade.Components
return;
}
await DialogService.ShowEditCategoryDialog(ApiClient, ContextMenuCategory);
await DialogService.InvokeEditCategoryDialog(ApiClient, ContextMenuCategory);
}
protected async Task RemoveCategory()

View File

@@ -5,6 +5,9 @@
<MudMenuItem Icon="@Icons.Material.Filled.List" Href="/log">Execution Log</MudMenuItem>
<MudMenuItem Icon="@Icons.Material.Filled.DisabledByDefault" Href="/blocks">Blocked IPs</MudMenuItem>
<MudDivider />
<MudMenuItem Icon="@Icons.Material.Filled.Label" Href="/tags">Tag Management</MudMenuItem>
<MudMenuItem Icon="@Icons.Material.Filled.List" Href="/categories">Category Management</MudMenuItem>
<MudDivider />
<MudMenuItem Icon="@Icons.Material.Filled.Settings" Href="/settings">Settings</MudMenuItem>
<MudMenuItem Icon="@Icons.Material.Filled.Undo" OnClick="ResetWebUI">Reset Web UI</MudMenuItem>
<MudDivider />

View File

@@ -1,3 +0,0 @@
<MudField Variant="Variant.Outlined" InnerPadding="false" Label="@Label" HelperText="@HelperText">
<MudTickSwitch T="bool" Value="@Value" ValueChanged="ValueChangedCallback" Class="pt-1 pb-1" Disabled="Disabled" Validation="Validation" />
</MudField>

View File

@@ -44,25 +44,25 @@
<MudNumericField T="int" Label=".torrent file size limit" Value="TorrentFileSizeLimit" ValueChanged="TorrentFileSizeLimitChanged" Min="0" Variant="Variant.Outlined" Adornment="Adornment.End" AdornmentText="MiB" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Recheck torrents on completion" Value="RecheckCompletedTorrents" ValueChanged="RecheckCompletedTorrentsChanged" />
<FieldSwitch Label="Recheck torrents on completion" Value="RecheckCompletedTorrents" ValueChanged="RecheckCompletedTorrentsChanged" />
</MudItem>
<MudItem xs="12">
<MudNumericField T="int" Label="Refresh interval" Value="RefreshInterval" ValueChanged="RefreshIntervalChanged" Min="0" Variant="Variant.Outlined" Adornment="Adornment.End" AdornmentText="ms" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Resolve peer countries" Value="ResolvePeerCountries" ValueChanged="ResolvePeerCountriesChanged" />
<FieldSwitch Label="Resolve peer countries" Value="ResolvePeerCountries" ValueChanged="ResolvePeerCountriesChanged" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Reannounce to all trackers when IP or port changed" Value="ReannounceWhenAddressChanged" ValueChanged="ReannounceWhenAddressChangedChanged" />
<FieldSwitch Label="Reannounce to all trackers when IP or port changed" Value="ReannounceWhenAddressChanged" ValueChanged="ReannounceWhenAddressChangedChanged" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Enable embedded tracker" Value="EnableEmbeddedTracker" ValueChanged="EnableEmbeddedTrackerChanged" />
<FieldSwitch Label="Enable embedded tracker" Value="EnableEmbeddedTracker" ValueChanged="EnableEmbeddedTrackerChanged" />
</MudItem>
<MudItem xs="12">
<MudNumericField T="int" Label="Embedded tracker port" Value="EmbeddedTrackerPort" ValueChanged="EmbeddedTrackerPortChanged" Min="@Options.MinPortValue" Max="@Options.MaxPortValue" Variant="Variant.Outlined" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Enable port forwarding for embedded tracker" Value="EmbeddedTrackerPortForwarding" ValueChanged="EmbeddedTrackerPortForwardingChanged" />
<FieldSwitch Label="Enable port forwarding for embedded tracker" Value="EmbeddedTrackerPortForwarding" ValueChanged="EmbeddedTrackerPortForwardingChanged" />
</MudItem>
</MudGrid>
</MudCardContent>
@@ -124,13 +124,13 @@
</MudSelect>
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Coalesce reads &amp; writes (requires libtorrent &lt; 2.0)" Value="EnableCoalesceReadWrite" ValueChanged="EnableCoalesceReadWriteChanged" />
<FieldSwitch Label="Coalesce reads &amp; writes (requires libtorrent &lt; 2.0)" Value="EnableCoalesceReadWrite" ValueChanged="EnableCoalesceReadWriteChanged" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Use piece extent affinity" Value="EnablePieceExtentAffinity" ValueChanged="EnablePieceExtentAffinityChanged" />
<FieldSwitch Label="Use piece extent affinity" Value="EnablePieceExtentAffinity" ValueChanged="EnablePieceExtentAffinityChanged" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Send upload piece suggestions" Value="EnableUploadSuggestions" ValueChanged="EnableUploadSuggestionsChanged" />
<FieldSwitch Label="Send upload piece suggestions" Value="EnableUploadSuggestions" ValueChanged="EnableUploadSuggestionsChanged" />
</MudItem>
<MudItem xs="12">
<MudNumericField T="int" Label="Send buffer watermark" Value="SendBufferWatermark" ValueChanged="SendBufferWatermarkChanged" Min="0" Variant="Variant.Outlined" Adornment="Adornment.End" AdornmentText="KiB" />
@@ -172,19 +172,19 @@
</MudSelect>
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Support internationalized domain name (IDN)" Value="IdnSupportEnabled" ValueChanged="IdnSupportEnabledChanged" />
<FieldSwitch Label="Support internationalized domain name (IDN)" Value="IdnSupportEnabled" ValueChanged="IdnSupportEnabledChanged" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Allow multiple connections from the same IP address" Value="EnableMultiConnectionsFromSameIp" ValueChanged="EnableMultiConnectionsFromSameIpChanged" />
<FieldSwitch Label="Allow multiple connections from the same IP address" Value="EnableMultiConnectionsFromSameIp" ValueChanged="EnableMultiConnectionsFromSameIpChanged" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Validate HTTPS tracker certificate" Value="ValidateHttpsTrackerCertificate" ValueChanged="ValidateHttpsTrackerCertificateChanged" />
<FieldSwitch Label="Validate HTTPS tracker certificate" Value="ValidateHttpsTrackerCertificate" ValueChanged="ValidateHttpsTrackerCertificateChanged" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Server-side request forgery (SSRF) mitigation" Value="SsrfMitigation" ValueChanged="SsrfMitigationChanged" />
<FieldSwitch Label="Server-side request forgery (SSRF) mitigation" Value="SsrfMitigation" ValueChanged="SsrfMitigationChanged" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Disallow connection to peers on privileged ports" Value="BlockPeersOnPrivilegedPorts" ValueChanged="BlockPeersOnPrivilegedPortsChanged" />
<FieldSwitch Label="Disallow connection to peers on privileged ports" Value="BlockPeersOnPrivilegedPorts" ValueChanged="BlockPeersOnPrivilegedPortsChanged" />
</MudItem>
<MudItem xs="12">
<MudSelect T="int" Label="Upload slots behavior" Value="UploadSlotsBehavior" ValueChanged="UploadSlotsBehaviorChanged" Variant="Variant.Outlined">
@@ -200,10 +200,10 @@
</MudSelect>
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Always announce to all trackers in a tier" Value="AnnounceToAllTrackers" ValueChanged="AnnounceToAllTrackersChanged" />
<FieldSwitch Label="Always announce to all trackers in a tier" Value="AnnounceToAllTrackers" ValueChanged="AnnounceToAllTrackersChanged" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Always announce to all tiers" Value="AnnounceToAllTiers" ValueChanged="AnnounceToAllTiersChanged" />
<FieldSwitch Label="Always announce to all tiers" Value="AnnounceToAllTiers" ValueChanged="AnnounceToAllTiersChanged" />
</MudItem>
<MudItem xs="12">
<MudTextField T="string" Label="IP address reported to trackers (requires restart)" Value="AnnounceIp" ValueChanged="AnnounceIpChanged" Variant="Variant.Outlined" />

View File

@@ -26,19 +26,19 @@
<MudCardContent Class="pt-0">
<MudGrid>
<MudItem xs="12">
<MudFieldSwitch Label="Log file" Value="FileLogEnabled" ValueChanged="FileLogEnabledChanged" />
<FieldSwitch Label="Log file" Value="FileLogEnabled" ValueChanged="FileLogEnabledChanged" />
</MudItem>
<MudItem xs="12">
<MudTextField T="string" Label="Save Path" Value="FileLogPath" ValueChanged="FileLogPathChanged" Disabled="@(!FileLogEnabled)" Variant="Variant.Outlined" />
</MudItem>
<MudItem xs="3">
<MudFieldSwitch Label="Backup the log after" Value="FileLogBackupEnabled" ValueChanged="FileLogBackupEnabledChanged" Disabled="@(!FileLogEnabled)" />
<FieldSwitch Label="Backup the log after" Value="FileLogBackupEnabled" ValueChanged="FileLogBackupEnabledChanged" Disabled="@(!FileLogEnabled)" />
</MudItem>
<MudItem xs="9">
<MudNumericField T="int" Value="FileLogMaxSize" ValueChanged="FileLogMaxSizeChanged" Disabled="@(!FileLogEnabled)" Min="1" Max="1024000" Variant="Variant.Outlined" Adornment="Adornment.End" AdornmentText="KiB" />
</MudItem>
<MudItem xs="3">
<MudFieldSwitch Label="Delete backups older than" Value="FileLogDeleteOld" ValueChanged="FileLogDeleteOldChanged" Disabled="@(!FileLogEnabled)" />
<FieldSwitch Label="Delete backups older than" Value="FileLogDeleteOld" ValueChanged="FileLogDeleteOldChanged" Disabled="@(!FileLogEnabled)" />
</MudItem>
<MudItem xs="9">
<MudGrid>
@@ -67,7 +67,7 @@
<MudCardContent>
<MudGrid>
<MudItem xs="12">
<MudFieldSwitch Label="Log performance warnings" Value="PerformanceWarning" ValueChanged="PerformanceWarningChanged" />
<FieldSwitch Label="Log performance warnings" Value="PerformanceWarning" ValueChanged="PerformanceWarningChanged" />
</MudItem>
</MudGrid>
</MudCardContent>

View File

@@ -9,13 +9,13 @@
<MudCardContent Class="pt-0">
<MudGrid>
<MudItem xs="12">
<MudFieldSwitch Label="Enable DHT (decentralized network) to find more peers" Value="Dht" ValueChanged="DhtChanged" />
<FieldSwitch Label="Enable DHT (decentralized network) to find more peers" Value="Dht" ValueChanged="DhtChanged" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Enable Peer Exchange (PeX) to find more peers" Value="Pex" ValueChanged="PexChanged" />
<FieldSwitch Label="Enable Peer Exchange (PeX) to find more peers" Value="Pex" ValueChanged="PexChanged" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Enable Local Peer Discovery to find more peers" Value="Lsd" ValueChanged="LsdChanged" />
<FieldSwitch Label="Enable Local Peer Discovery to find more peers" Value="Lsd" ValueChanged="LsdChanged" />
</MudItem>
<MudItem xs="12">
<MudSelect T="int" Label="Encryption mode" Value="Encryption" ValueChanged="EncryptionChanged" Variant="Variant.Outlined">
@@ -25,7 +25,7 @@
</MudSelect>
</MudItem>
<MudItem xs="6">
<MudFieldSwitch Label="Enable anonymous mode" Value="AnonymousMode" ValueChanged="AnonymousModeChanged" />
<FieldSwitch Label="Enable anonymous mode" Value="AnonymousMode" ValueChanged="AnonymousModeChanged" />
</MudItem>
<MudItem xs="6">
<MudLink Href="https://github.com/qbittorrent/qBittorrent/wiki/Anonymous-Mode" Underline="Underline.Always" Target="https://github.com/qbittorrent/qBittorrent/wiki/Anonymous-Mode">More information</MudLink>
@@ -53,7 +53,7 @@
<MudCardContent Class="pt-0">
<MudGrid>
<MudItem xs="12">
<MudFieldSwitch Label="Queueing enabled" Value="QueueingEnabled" ValueChanged="QueueingEnabledChanged" />
<FieldSwitch Label="Queueing enabled" Value="QueueingEnabled" ValueChanged="QueueingEnabledChanged" />
</MudItem>
<MudItem xs="12">
<MudNumericField T="int" Label="Maximum active downloads" Value="MaxActiveDownloads" ValueChanged="MaxActiveDownloadsChanged" Min="-1" Disabled="@(!QueueingEnabled)" Variant="Variant.Outlined" Validation="MaxActiveDownloadsValidation" />
@@ -65,7 +65,7 @@
<MudNumericField T="int" Label="Maximum active torrents" Value="MaxActiveTorrents" ValueChanged="MaxActiveTorrentsChanged" Min="-1" Disabled="@(!QueueingEnabled)" Variant="Variant.Outlined" Validation="MaxActiveTorrentsValidation" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Do not count slow torrents in these limits" Value="DontCountSlowTorrents" ValueChanged="DontCountSlowTorrentsChanged" Disabled="@(!QueueingEnabled)" />
<FieldSwitch Label="Do not count slow torrents in these limits" Value="DontCountSlowTorrents" ValueChanged="DontCountSlowTorrentsChanged" Disabled="@(!QueueingEnabled)" />
</MudItem>
<MudItem xs="12">
<MudNumericField T="int" Label="Download rate threshold" Value="SlowTorrentDlRateThreshold" ValueChanged="SlowTorrentDlRateThresholdChanged" Min="0" Variant="Variant.Outlined" Adornment="Adornment.End" AdornmentText="KiB/s" Validation="SlowTorrentDlRateThresholdValidation" />
@@ -89,19 +89,19 @@
<MudCardContent Class="pt-0">
<MudGrid>
<MudItem xs="3">
<MudFieldSwitch Label="When ratio reaches" Value="MaxRatioEnabled" ValueChanged="MaxRatioEnabledChanged" />
<FieldSwitch Label="When ratio reaches" Value="MaxRatioEnabled" ValueChanged="MaxRatioEnabledChanged" />
</MudItem>
<MudItem xs="9">
<MudNumericField T="int" Label="" Value="MaxRatio" ValueChanged="MaxRatioChanged" Disabled="@(!MaxRatioEnabled)" Min="0" Max="9998" Variant="Variant.Outlined" Validation="MaxRatioValidation" />
</MudItem>
<MudItem xs="3">
<MudFieldSwitch Label="When total seeding time reaches" Value="MaxSeedingTimeEnabled" ValueChanged="MaxSeedingTimeEnabledChanged" />
<FieldSwitch Label="When total seeding time reaches" Value="MaxSeedingTimeEnabled" ValueChanged="MaxSeedingTimeEnabledChanged" />
</MudItem>
<MudItem xs="9">
<MudNumericField T="int" Label="minutes" Value="MaxSeedingTime" ValueChanged="MaxSeedingTimeChanged" Disabled="@(!MaxSeedingTimeEnabled)" Min="0" Max="525600" Variant="Variant.Outlined" Adornment="Adornment.End" AdornmentText="minutes" Validation="MaxSeedingTimeValidation" />
</MudItem>
<MudItem xs="3">
<MudFieldSwitch Label="When inactive seeding time reaches" Value="MaxInactiveSeedingTimeEnabled" ValueChanged="MaxInactiveSeedingTimeEnabledChanged" />
<FieldSwitch Label="When inactive seeding time reaches" Value="MaxInactiveSeedingTimeEnabled" ValueChanged="MaxInactiveSeedingTimeEnabledChanged" />
</MudItem>
<MudItem xs="9">
<MudNumericField T="int" Label="minutes" Value="MaxInactiveSeedingTime" ValueChanged="MaxInactiveSeedingTimeChanged" Disabled="@(!MaxInactiveSeedingTimeEnabled)" Min="0" Max="525600" Variant="Variant.Outlined" Validation="MaxInactiveSeedingTimeValidation" />
@@ -127,7 +127,7 @@
<MudCardContent Class="pt-0">
<MudGrid>
<MudItem xs="12">
<MudFieldSwitch Label="Automatically add these trackers to new downloads" Value="AddTrackersEnabled" ValueChanged="AddTrackersEnabledChanged" />
<FieldSwitch Label="Automatically add these trackers to new downloads" Value="AddTrackersEnabled" ValueChanged="AddTrackersEnabledChanged" />
</MudItem>
<MudItem xs="12">
<MudTextField T="string" Label="Trackers" Value="AddTrackers" ValueChanged="AddTrackersChanged" Lines="5" Variant="Variant.Outlined" />

View File

@@ -26,7 +26,7 @@
<MudNumericField T="int" Label="Port used for incoming connections" Value="ListenPort" ValueChanged="ListenPortChanged" Min="@MinNonNegativePortValue" Max="@MaxPortValue" Variant="Variant.Outlined" Adornment="Adornment.End" AdornmentIcon="@CustomIcons.Random" OnAdornmentClick="GenerateRandomPort" HelperText="Set to 0 to let your system pick an unused port" Validation="PortNonNegativeValidation" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Use UPnp / NAT-PMP port forwarding from my router" Value="Upnp" ValueChanged="UpnpChanged" />
<FieldSwitch Label="Use UPnp / NAT-PMP port forwarding from my router" Value="Upnp" ValueChanged="UpnpChanged" />
</MudItem>
</MudGrid>
</MudCardContent>
@@ -41,25 +41,25 @@
<MudCardContent Class="pt-0">
<MudGrid>
<MudItem xs="12" md="6">
<MudFieldSwitch T="bool" Label="Global maximum number of connections" Value="MaxConnecEnabled" ValueChanged="MaxConnecEnabledChanged" />
<FieldSwitch T="bool" Label="Global maximum number of connections" Value="MaxConnecEnabled" ValueChanged="MaxConnecEnabledChanged" />
</MudItem>
<MudItem xs="12" md="6">
<MudNumericField T="int" Label="Connections" Value="MaxConnec" ValueChanged="MaxConnecChanged" Min="0" Disabled="@(!MaxConnecEnabled)" Variant="Variant.Outlined" Validation="MaxConnectValidation" />
</MudItem>
<MudItem xs="12" md="6">
<MudFieldSwitch Label="Maximum number of connections per torrent" Value="MaxConnecPerTorrentEnabled" ValueChanged="MaxConnecPerTorrentEnabledChanged" />
<FieldSwitch Label="Maximum number of connections per torrent" Value="MaxConnecPerTorrentEnabled" ValueChanged="MaxConnecPerTorrentEnabledChanged" />
</MudItem>
<MudItem xs="12" md="6">
<MudNumericField T="int" Label="Connections" Value="MaxConnecPerTorrent" ValueChanged="MaxConnecPerTorrentChanged" Min="0" Disabled="@(!MaxConnecPerTorrentEnabled)" Variant="Variant.Outlined" Validation="MaxConnecPerTorrentValidation" />
</MudItem>
<MudItem xs="12" md="6">
<MudFieldSwitch Label="Global maximum number of upload slots" Value="MaxUploadsEnabled" ValueChanged="MaxUploadsEnabledChanged" />
<FieldSwitch Label="Global maximum number of upload slots" Value="MaxUploadsEnabled" ValueChanged="MaxUploadsEnabledChanged" />
</MudItem>
<MudItem xs="12" md="6">
<MudNumericField T="int" Label="Slots" Value="MaxUploads" ValueChanged="MaxUploadsChanged" Min="0" Disabled="@(!MaxUploadsEnabled)" Variant="Variant.Outlined" Validation="MaxUploadsValidation" />
</MudItem>
<MudItem xs="12" md="6">
<MudFieldSwitch Label="Maximum number of upload slots per torrent" Value="MaxUploadsPerTorrentEnabled" ValueChanged="MaxUploadsPerTorrentEnabledChanged" />
<FieldSwitch Label="Maximum number of upload slots per torrent" Value="MaxUploadsPerTorrentEnabled" ValueChanged="MaxUploadsPerTorrentEnabledChanged" />
</MudItem>
<MudItem xs="12" md="6">
<MudNumericField T="int" Label="Slots" Value="MaxUploadsPerTorrent" ValueChanged="MaxUploadsPerTorrentChanged" Min="0" Disabled="@(!MaxUploadsPerTorrentEnabled)" Variant="Variant.Outlined" Validation="MaxUploadsPerTorrentValidation" />
@@ -72,7 +72,7 @@
<MudCardContent Class="pt-0">
<MudGrid>
<MudItem xs="12">
<MudFieldSwitch Label="I2P (Experimental)" Value="I2pEnabled" ValueChanged="I2pEnabledChanged" />
<FieldSwitch Label="I2P (Experimental)" Value="I2pEnabled" ValueChanged="I2pEnabledChanged" />
</MudItem>
<MudItem xs="12" md="6">
<MudTextField T="string" Label="Host" Value="I2pAddress" ValueChanged="I2pAddressChanged" Disabled="@(!I2pEnabled)" Variant="Variant.Outlined" />
@@ -81,7 +81,7 @@
<MudNumericField T="int" Label="Slots" Value="I2pPort" ValueChanged="I2pPortChanged" Min="0" Max="65535" Disabled="@(!I2pEnabled)" Variant="Variant.Outlined" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Mixed mode" Value="I2pMixedMode" ValueChanged="I2pMixedModeChanged" Disabled="@(!I2pEnabled)" HelperText="If &quot;mixed mode&quot; is enabled, I2P torrents are allowed to also get peers from other sources than the tracker, and connect to regular IPs, not providing any anonymization. This may be useful if the user is not interested in the anonymization of I2P, but still wants to be able to connect to I2P peers." />
<FieldSwitch Label="Mixed mode" Value="I2pMixedMode" ValueChanged="I2pMixedModeChanged" Disabled="@(!I2pEnabled)" HelperText="If &quot;mixed mode&quot; is enabled, I2P torrents are allowed to also get peers from other sources than the tracker, and connect to regular IPs, not providing any anonymization. This may be useful if the user is not interested in the anonymization of I2P, but still wants to be able to connect to I2P peers." />
</MudItem>
</MudGrid>
</MudCardContent>
@@ -110,10 +110,10 @@
<MudNumericField T="int" Label="Port" Value="ProxyPort" ValueChanged="ProxyPortChanged" Min="1" Max="@ConnectionOptions.MaxPortValue" Disabled="ProxyDisabled" Variant="Variant.Outlined" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Perform hostname lookup via proxy" Value="ProxyHostnameLookup" ValueChanged="ProxyHostnameLookupChanged" HelperText="If checked, hostname lookups are done via the proxy." />
<FieldSwitch Label="Perform hostname lookup via proxy" Value="ProxyHostnameLookup" ValueChanged="ProxyHostnameLookupChanged" HelperText="If checked, hostname lookups are done via the proxy." />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Authentication" Value="ProxyAuthEnabled" ValueChanged="ProxyAuthEnabledChanged" Disabled="@(ProxyDisabled || ProxySocks4)" />
<FieldSwitch Label="Authentication" Value="ProxyAuthEnabled" ValueChanged="ProxyAuthEnabledChanged" Disabled="@(ProxyDisabled || ProxySocks4)" />
</MudItem>
<MudItem xs="12" md="6">
<MudTextField T="string" Label="Username" Value="ProxyUsername" ValueChanged="ProxyUsernameChanged" Disabled="@(ProxyDisabled || ProxySocks4)" Variant="Variant.Outlined" />
@@ -123,16 +123,16 @@
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Use proxy for BitTorrent purposes" Value="ProxyBittorrent" ValueChanged="ProxyBittorrentChanged" Disabled="ProxyDisabled" />
<FieldSwitch Label="Use proxy for BitTorrent purposes" Value="ProxyBittorrent" ValueChanged="ProxyBittorrentChanged" Disabled="ProxyDisabled" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Use proxy for peer connections" Value="ProxyPeerConnections" ValueChanged="ProxyPeerConnectionsChanged" Disabled="@(ProxyDisabled || ProxyAuthEnabled)" />
<FieldSwitch Label="Use proxy for peer connections" Value="ProxyPeerConnections" ValueChanged="ProxyPeerConnectionsChanged" Disabled="@(ProxyDisabled || ProxyAuthEnabled)" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Use proxy for RSS purposes" Value="ProxyRss" ValueChanged="ProxyRssChanged" Disabled="@(ProxyDisabled || ProxySocks4)" />
<FieldSwitch Label="Use proxy for RSS purposes" Value="ProxyRss" ValueChanged="ProxyRssChanged" Disabled="@(ProxyDisabled || ProxySocks4)" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Use proxy for general purposes" Value="ProxyMisc" ValueChanged="ProxyMiscChanged" Disabled="@(ProxyDisabled || ProxySocks4)" />
<FieldSwitch Label="Use proxy for general purposes" Value="ProxyMisc" ValueChanged="ProxyMiscChanged" Disabled="@(ProxyDisabled || ProxySocks4)" />
</MudItem>
</MudGrid>
</MudCardContent>
@@ -147,13 +147,13 @@
<MudCardContent Class="pt-0">
<MudGrid>
<MudItem xs="12">
<MudFieldSwitch Label="IP Filter" Value="IpFilterEnabled" ValueChanged="IpFilterEnabledChanged" />
<FieldSwitch Label="IP Filter" Value="IpFilterEnabled" ValueChanged="IpFilterEnabledChanged" />
</MudItem>
<MudItem xs="12">
<MudTextField T="string" Label="Filter path (.dat, .p2p, .p2b)" Value="IpFilterPath" ValueChanged="IpFilterPathChanged" Disabled="@(!IpFilterEnabled)" Variant="Variant.Outlined" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Apply to trackers" Value="IpFilterTrackers" ValueChanged="IpFilterTrackersChanged" />
<FieldSwitch Label="Apply to trackers" Value="IpFilterTrackers" ValueChanged="IpFilterTrackersChanged" />
</MudItem>
<MudItem xs="12">
<MudTextField T="string" Label="Manually banned IP addresses" Value="BannedIPs" ValueChanged="BannedIPsChanged" Lines="5" Variant="Variant.Outlined" />

View File

@@ -16,10 +16,10 @@
</MudSelect>
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Add to top of queue" Value="AddToTopOfQueue" ValueChanged="AddToTopOfQueueChanged" />
<FieldSwitch Label="Add to top of queue" Value="AddToTopOfQueue" ValueChanged="AddToTopOfQueueChanged" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Do not start the download automatically" Value="StartPausedEnabled" ValueChanged="StartPausedEnabledChanged" />
<FieldSwitch Label="Do not start the download automatically" Value="StartPausedEnabled" ValueChanged="StartPausedEnabledChanged" />
</MudItem>
<MudItem xs="12">
<MudSelect T="string" Label="Torrent stop condition" Value="TorrentStopCondition" ValueChanged="TorrentStopConditionChanged" Variant="Variant.Outlined">
@@ -29,7 +29,7 @@
</MudSelect>
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Delete .torrent files afterwards" Value="AutoDeleteMode" ValueChanged="AutoDeleteModeChanged" />
<FieldSwitch Label="Delete .torrent files afterwards" Value="AutoDeleteMode" ValueChanged="AutoDeleteModeChanged" />
</MudItem>
</MudGrid>
</MudCardContent>
@@ -44,10 +44,10 @@
<MudCardContent>
<MudGrid>
<MudItem xs="12">
<MudFieldSwitch Label="Pre-allocate disk space for all files" Value="PreallocateAll" ValueChanged="PreallocateAllChanged" />
<FieldSwitch Label="Pre-allocate disk space for all files" Value="PreallocateAll" ValueChanged="PreallocateAllChanged" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Append .!qB extension to incomplete files" Value="IncompleteFilesExt" ValueChanged="IncompleteFilesExtChanged" />
<FieldSwitch Label="Append .!qB extension to incomplete files" Value="IncompleteFilesExt" ValueChanged="IncompleteFilesExtChanged" />
</MudItem>
</MudGrid>
</MudCardContent>
@@ -86,7 +86,7 @@
</MudSelect>
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Use Subcategories" Value="UseSubcategories" ValueChanged="UseSubcategoriesChanged" />
<FieldSwitch Label="Use Subcategories" Value="UseSubcategories" ValueChanged="UseSubcategoriesChanged" />
</MudItem>
<MudItem xs="12">
<MudTextField T="string" Label="Default Save Path" Value="SavePath" ValueChanged="SavePathChanged" Variant="Variant.Outlined" />
@@ -94,7 +94,7 @@
<MudItem xs="12">
<MudGrid>
<MudItem xs="12" sm="6" md="3">
<MudFieldSwitch Label="Keep incomplete torrents in" Value="TempPathEnabled" ValueChanged="TempPathEnabledChanged" />
<FieldSwitch Label="Keep incomplete torrents in" Value="TempPathEnabled" ValueChanged="TempPathEnabledChanged" />
</MudItem>
<MudItem xs="12" sm="6" md="9">
<MudTextField T="string" Label="Path" Value="TempPath" ValueChanged="TempPathChanged" Disabled="@(!TempPathEnabled)" Variant="Variant.Outlined" />
@@ -104,7 +104,7 @@
<MudItem xs="12">
<MudGrid>
<MudItem xs="12" sm="6" md="3">
<MudFieldSwitch Label="Copy .torrent files to" Value="ExportDirEnabled" ValueChanged="ExportDirEnabledChanged" />
<FieldSwitch Label="Copy .torrent files to" Value="ExportDirEnabled" ValueChanged="ExportDirEnabledChanged" />
</MudItem>
<MudItem xs="12" sm="6" md="9">
<MudTextField T="string" Label="Path" Value="ExportDir" ValueChanged="ExportDirChanged" Disabled="@(!TempPathEnabled)" Variant="Variant.Outlined" />
@@ -114,7 +114,7 @@
<MudItem xs="12">
<MudGrid>
<MudItem xs="12" sm="6" md="3">
<MudFieldSwitch Label="Copy .torrent files for finished downloads to" Value="ExportDirFinEnabled" ValueChanged="ExportDirFinEnabledChanged" />
<FieldSwitch Label="Copy .torrent files for finished downloads to" Value="ExportDirFinEnabled" ValueChanged="ExportDirFinEnabledChanged" />
</MudItem>
<MudItem xs="12" sm="6" md="9">
<MudTextField T="string" Label="Path" Value="ExportDirFin" ValueChanged="ExportDirFinChanged" Disabled="@(!TempPathEnabled)" Variant="Variant.Outlined" />
@@ -222,7 +222,7 @@
<MudCardContent>
<MudGrid>
<MudItem xs="12">
<MudFieldSwitch Label="Excluded file names" Value="ExcludedFileNamesEnabled" ValueChanged="ExcludedFileNamesEnabledChanged" />
<FieldSwitch Label="Excluded file names" Value="ExcludedFileNamesEnabled" ValueChanged="ExcludedFileNamesEnabledChanged" />
</MudItem>
<MudItem xs="12">
<MudTextField T="string" Label="Excluded files names" Value="ExcludedFileNames" ValueChanged="ExcludedFileNamesChanged" Lines="5" Disabled="@(!ExcludedFileNamesEnabled)" Variant="Variant.Outlined" />
@@ -240,7 +240,7 @@
<MudCardContent>
<MudGrid>
<MudItem xs="12">
<MudFieldSwitch Label="Email notification upon download completion" Value="MailNotificationEnabled" ValueChanged="MailNotificationEnabledChanged" />
<FieldSwitch Label="Email notification upon download completion" Value="MailNotificationEnabled" ValueChanged="MailNotificationEnabledChanged" />
</MudItem>
<MudItem xs="12">
<MudTextField T="string" Label="From" Value="MailNotificationSender" ValueChanged="MailNotificationSenderChanged" Disabled="@(!MailNotificationEnabled)" Variant="Variant.Outlined" />
@@ -252,10 +252,10 @@
<MudTextField T="string" Label="SMTP server" Value="MailNotificationSmtp" ValueChanged="MailNotificationSmtpChanged" Disabled="@(!MailNotificationEnabled)" Variant="Variant.Outlined" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="This server requires a secure connection (SSL)" Value="MailNotificationSslEnabled" ValueChanged="MailNotificationSslEnabledChanged" Disabled="@(!MailNotificationEnabled)" />
<FieldSwitch Label="This server requires a secure connection (SSL)" Value="MailNotificationSslEnabled" ValueChanged="MailNotificationSslEnabledChanged" Disabled="@(!MailNotificationEnabled)" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Authentication" Value="MailNotificationAuthEnabled" ValueChanged="MailNotificationAuthEnabledChanged" Disabled="@(!MailNotificationEnabled)" />
<FieldSwitch Label="Authentication" Value="MailNotificationAuthEnabled" ValueChanged="MailNotificationAuthEnabledChanged" Disabled="@(!MailNotificationEnabled)" />
</MudItem>
<MudItem xs="12">
<MudTextField T="string" Label="Username" Value="MailNotificationUsername" ValueChanged="MailNotificationUsernameChanged" Disabled="@(!(MailNotificationEnabled && MailNotificationAuthEnabled))" Variant="Variant.Outlined" />
@@ -276,13 +276,13 @@
<MudCardContent Class="pt-0">
<MudGrid>
<MudItem xs="12">
<MudFieldSwitch Label="Run external program on torrent added" Value="AutorunOnTorrentAddedEnabled" ValueChanged="AutorunOnTorrentAddedEnabledChanged" />
<FieldSwitch Label="Run external program on torrent added" Value="AutorunOnTorrentAddedEnabled" ValueChanged="AutorunOnTorrentAddedEnabledChanged" />
</MudItem>
<MudItem xs="12">
<MudTextField T="string" Label="External program" Value="AutorunOnTorrentAddedProgram" ValueChanged="AutorunOnTorrentAddedProgramChanged" Disabled="@(!AutorunOnTorrentAddedEnabled)" Variant="Variant.Outlined" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Run external program on torrent finished" Value="AutorunEnabled" ValueChanged="AutorunEnabledChanged" />
<FieldSwitch Label="Run external program on torrent finished" Value="AutorunEnabled" ValueChanged="AutorunEnabledChanged" />
</MudItem>
<MudItem xs="12">
<MudTextField T="string" Label="External program" Value="AutorunProgram" ValueChanged="AutorunProgramChanged" Disabled="@(!AutorunEnabled)" Variant="Variant.Outlined" />

View File

@@ -9,7 +9,7 @@
<MudCardContent Class="pt-0">
<MudGrid>
<MudItem xs="12">
<MudFieldSwitch Label="Enable fetching RSS feeds" Value="RssProcessingEnabled" ValueChanged="RssProcessingEnabledChanged" />
<FieldSwitch Label="Enable fetching RSS feeds" Value="RssProcessingEnabled" ValueChanged="RssProcessingEnabledChanged" />
</MudItem>
<MudItem xs="12">
<MudNumericField T="int" Label="Feeds refresh interval" Value="RssRefreshInterval" ValueChanged="RssRefreshIntervalChanged" Min="0" Variant="Variant.Outlined" Adornment="Adornment.End" AdornmentText="min" />
@@ -30,7 +30,7 @@
<MudCardContent Class="pt-0">
<MudGrid>
<MudItem xs="12">
<MudFieldSwitch Label="Enable auto downloading of RSS torrents" Value="RssAutoDownloadingEnabled" ValueChanged="RssAutoDownloadingEnabledChanged" />
<FieldSwitch Label="Enable auto downloading of RSS torrents" Value="RssAutoDownloadingEnabled" ValueChanged="RssAutoDownloadingEnabledChanged" />
</MudItem>
<MudItem xs="12">
<MudButton OnClick="OpenRssRulesDialog" Variant="Variant.Filled">Edit auto downloading rules</MudButton>
@@ -48,7 +48,7 @@
<MudCardContent Class="pt-0">
<MudGrid>
<MudItem xs="12">
<MudFieldSwitch Label="Download REPACK/PROPER episodes" Value="RssDownloadRepackProperEpisodes" ValueChanged="RssDownloadRepackProperEpisodesChanged" />
<FieldSwitch Label="Download REPACK/PROPER episodes" Value="RssDownloadRepackProperEpisodes" ValueChanged="RssDownloadRepackProperEpisodesChanged" />
</MudItem>
<MudItem xs="12">
<MudTextField T="string" Label="Filters" Value="RssSmartEpisodeFilters" ValueChanged="RssSmartEpisodeFiltersChanged" Lines="5" Variant="Variant.Outlined" />

View File

@@ -33,7 +33,7 @@
<MudNumericField T="int" Label="Download" Value="AltDlLimit" ValueChanged="AltDlLimitChanged" Min="0" Variant="Variant.Outlined" Adornment="Adornment.End" AdornmentText="KiB/s" HelperText="0 means unlimited" Validation="AltDlLimitValidation" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Schedule the use of alternative rate limits" Value="SchedulerEnabled" ValueChanged="SchedulerEnabledChanged" />
<FieldSwitch Label="Schedule the use of alternative rate limits" Value="SchedulerEnabled" ValueChanged="SchedulerEnabledChanged" />
</MudItem>
<MudItem xs="12" md="6">
<MudTimePicker Label="From" Editable="true" Time="ScheduleFrom" TimeChanged="ScheduleFromChanged" Disabled="@(!SchedulerEnabled)" Variant="Variant.Outlined" />
@@ -68,13 +68,13 @@
<MudCardContent Class="pt-0">
<MudGrid>
<MudItem xs="12">
<MudFieldSwitch Label="Apply rate limit to µTP protocol" Value="LimitUtpRate" ValueChanged="LimitUtpRateChanged" />
<FieldSwitch Label="Apply rate limit to µTP protocol" Value="LimitUtpRate" ValueChanged="LimitUtpRateChanged" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Apply rate limit to transport overhead" Value="LimitTcpOverhead" ValueChanged="LimitTcpOverheadChanged" />
<FieldSwitch Label="Apply rate limit to transport overhead" Value="LimitTcpOverhead" ValueChanged="LimitTcpOverheadChanged" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Apply rate limit to peers on LAN" Value="LimitLanPeers" ValueChanged="LimitLanPeersChanged" />
<FieldSwitch Label="Apply rate limit to peers on LAN" Value="LimitLanPeers" ValueChanged="LimitLanPeersChanged" />
</MudItem>
</MudGrid>
</MudCardContent>

View File

@@ -15,7 +15,7 @@
<MudNumericField T="int" Label="Port" Value="WebUiPort" ValueChanged="WebUiPortChanged" Min="1" Max="@Options.MaxPortValue" Variant="Variant.Outlined" Validation="WebUiPortValidation" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Use UPnP / NAT-PMP to forward the port from my router" Value="WebUiUpnp" ValueChanged="WebUiUpnpChanged" />
<FieldSwitch Label="Use UPnP / NAT-PMP to forward the port from my router" Value="WebUiUpnp" ValueChanged="WebUiUpnpChanged" />
</MudItem>
</MudGrid>
</MudCardContent>
@@ -30,7 +30,7 @@
<MudCardContent Class="pt-0">
<MudGrid>
<MudItem xs="12">
<MudFieldSwitch Label="Use HTTPS instead of HTTP" Value="UseHttps" ValueChanged="UseHttpsChanged" />
<FieldSwitch Label="Use HTTPS instead of HTTP" Value="UseHttps" ValueChanged="UseHttpsChanged" />
</MudItem>
<MudItem xs="12">
<MudTextField T="string" Label="Certificate" Value="WebUiHttpsCertPath" ValueChanged="WebUiHttpsCertPathChanged" Disabled="@(!UseHttps)" Variant="Variant.Outlined" Validation="WebUiHttpsCertPathValidation" />
@@ -57,10 +57,10 @@
<MudTextField T="string" Label="Password" Value="WebUiPassword" ValueChanged="WebUiPasswordChanged" InputType="InputType.Password" Variant="Variant.Outlined" Validation="WebUiPasswordValidation" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Bypass authentication for clients on localhost" Value="BypassLocalAuth" ValueChanged="BypassLocalAuthChanged" />
<FieldSwitch Label="Bypass authentication for clients on localhost" Value="BypassLocalAuth" ValueChanged="BypassLocalAuthChanged" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Bypass authentication for clients in whitelisted IP subnets" Value="BypassAuthSubnetWhitelistEnabled" ValueChanged="BypassAuthSubnetWhitelistEnabledChanged" />
<FieldSwitch Label="Bypass authentication for clients in whitelisted IP subnets" Value="BypassAuthSubnetWhitelistEnabled" ValueChanged="BypassAuthSubnetWhitelistEnabledChanged" />
</MudItem>
<MudItem xs="12">
<MudTextField T="string" Label="Trackers" Value="BypassAuthSubnetWhitelist" ValueChanged="BypassAuthSubnetWhitelistChanged" Lines="5" Disabled="@(!BypassAuthSubnetWhitelistEnabled)" Variant="Variant.Outlined" />
@@ -87,7 +87,7 @@
<MudCardContent Class="pt-0">
<MudGrid>
<MudItem xs="12">
<MudFieldSwitch Label="Use alternative Web UI" Value="AlternativeWebuiEnabled" ValueChanged="AlternativeWebuiEnabledChanged" />
<FieldSwitch Label="Use alternative Web UI" Value="AlternativeWebuiEnabled" ValueChanged="AlternativeWebuiEnabledChanged" />
</MudItem>
<MudItem xs="12">
<MudTextField T="string" Label="Files location" Value="AlternativeWebuiPath" ValueChanged="AlternativeWebuiPathChanged" Variant="Variant.Outlined" Validation="AlternativeWebuiPathValidation" />
@@ -105,16 +105,16 @@
<MudCardContent Class="pt-0">
<MudGrid>
<MudItem xs="12">
<MudFieldSwitch Label="Enable clickjacking protection" Value="WebUiClickjackingProtectionEnabled" ValueChanged="WebUiClickjackingProtectionEnabledChanged" />
<FieldSwitch Label="Enable clickjacking protection" Value="WebUiClickjackingProtectionEnabled" ValueChanged="WebUiClickjackingProtectionEnabledChanged" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Enable Cross-Site Request Forgery (CSRF) protection" Value="WebUiCsrfProtectionEnabled" ValueChanged="WebUiCsrfProtectionEnabledChanged" />
<FieldSwitch Label="Enable Cross-Site Request Forgery (CSRF) protection" Value="WebUiCsrfProtectionEnabled" ValueChanged="WebUiCsrfProtectionEnabledChanged" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Enable cookie Secure flag (requires HTTPS)" Value="WebUiSecureCookieEnabled" ValueChanged="WebUiSecureCookieEnabledChanged" />
<FieldSwitch Label="Enable cookie Secure flag (requires HTTPS)" Value="WebUiSecureCookieEnabled" ValueChanged="WebUiSecureCookieEnabledChanged" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Enable Host header validation" Value="WebUiHostHeaderValidationEnabled" ValueChanged="WebUiHostHeaderValidationEnabledChanged" />
<FieldSwitch Label="Enable Host header validation" Value="WebUiHostHeaderValidationEnabled" ValueChanged="WebUiHostHeaderValidationEnabledChanged" />
</MudItem>
<MudItem xs="12">
<MudTextField T="string" Label="Server domains" Value="WebUiDomainList" ValueChanged="WebUiDomainListChanged" Lines="5" Disabled="@(!WebUiHostHeaderValidationEnabled)" Variant="Variant.Outlined" />
@@ -132,16 +132,16 @@
<MudCardContent Class="pt-0">
<MudGrid>
<MudItem xs="12">
<MudFieldSwitch Label="Enable clickjacking protection" Value="WebUiClickjackingProtectionEnabled" ValueChanged="WebUiClickjackingProtectionEnabledChanged" />
<FieldSwitch Label="Enable clickjacking protection" Value="WebUiClickjackingProtectionEnabled" ValueChanged="WebUiClickjackingProtectionEnabledChanged" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Enable Cross-Site Request Forgery (CSRF) protection" Value="WebUiCsrfProtectionEnabled" ValueChanged="WebUiCsrfProtectionEnabledChanged" />
<FieldSwitch Label="Enable Cross-Site Request Forgery (CSRF) protection" Value="WebUiCsrfProtectionEnabled" ValueChanged="WebUiCsrfProtectionEnabledChanged" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Enable cookie Secure flag (requires HTTPS)" Value="WebUiSecureCookieEnabled" ValueChanged="WebUiSecureCookieEnabledChanged" />
<FieldSwitch Label="Enable cookie Secure flag (requires HTTPS)" Value="WebUiSecureCookieEnabled" ValueChanged="WebUiSecureCookieEnabledChanged" />
</MudItem>
<MudItem xs="12">
<MudFieldSwitch Label="Enable Host header validation" Value="WebUiHostHeaderValidationEnabled" ValueChanged="WebUiHostHeaderValidationEnabledChanged" />
<FieldSwitch Label="Enable Host header validation" Value="WebUiHostHeaderValidationEnabled" ValueChanged="WebUiHostHeaderValidationEnabledChanged" />
</MudItem>
<MudItem xs="12">
<MudTextField T="string" Label="Server domains" Value="WebUiDomainList" ValueChanged="WebUiDomainListChanged" Lines="5" Disabled="@(!WebUiHostHeaderValidationEnabled)" Variant="Variant.Outlined" />
@@ -159,7 +159,7 @@
<MudCardContent Class="pt-0">
<MudGrid>
<MudItem xs="12">
<MudFieldSwitch Label="Add custom HTTP headers" Value="WebUiUseCustomHttpHeadersEnabled" ValueChanged="WebUiUseCustomHttpHeadersEnabledChanged" />
<FieldSwitch Label="Add custom HTTP headers" Value="WebUiUseCustomHttpHeadersEnabled" ValueChanged="WebUiUseCustomHttpHeadersEnabledChanged" />
</MudItem>
<MudItem xs="12">
<MudTextField T="string" Label="Server domains" Value="WebUiCustomHttpHeaders" ValueChanged="WebUiCustomHttpHeadersChanged" Lines="5" Disabled="@(!WebUiUseCustomHttpHeadersEnabled)" Variant="Variant.Outlined" />
@@ -177,7 +177,7 @@
<MudCardContent Class="pt-0">
<MudGrid>
<MudItem xs="12">
<MudFieldSwitch Label="Enable reverse proxy support" Value="WebUiReverseProxyEnabled" ValueChanged="WebUiReverseProxyEnabledChanged" />
<FieldSwitch Label="Enable reverse proxy support" Value="WebUiReverseProxyEnabled" ValueChanged="WebUiReverseProxyEnabledChanged" />
</MudItem>
<MudItem xs="12">
<MudTextField T="string" Label="Trusted proxies list" Value="WebUiReverseProxiesList" ValueChanged="WebUiReverseProxiesListChanged" Lines="5" Disabled="@(!WebUiReverseProxyEnabled)" Variant="Variant.Outlined" />
@@ -195,7 +195,7 @@
<MudCardContent Class="pt-0">
<MudGrid>
<MudItem xs="12">
<MudFieldSwitch Label="Update my dynamic domain name" Value="DyndnsEnabled" ValueChanged="DyndnsEnabledChanged" />
<FieldSwitch Label="Update my dynamic domain name" Value="DyndnsEnabled" ValueChanged="DyndnsEnabledChanged" />
</MudItem>
<MudItem xs="8">
<MudSelect T="int" Value="DyndnsService" ValueChanged="DyndnsServiceChanged" Disabled="@(!DyndnsEnabled)" Variant="Variant.Outlined">

View File

@@ -1,5 +1,24 @@
<DynamicTable T="Peer"
<ContextMenu @ref="ContextMenu" Dense="true">
<MudMenuItem Icon="@Icons.Material.Filled.AddCircle" IconColor="Color.Info" OnClick="AddPeer">Add peer</MudMenuItem>
@if (ContextMenuItem is not null)
{
<MudMenuItem Icon="@Icons.Material.Filled.DisabledByDefault" IconColor="Color.Info" OnClick="BanPeerContextMenu">Ban peer</MudMenuItem>
}
</ContextMenu>
<MudToolBar Gutters="false" Dense="true">
<MudIconButton Icon="@Icons.Material.Filled.AddCircle" Color="Color.Info" OnClick="AddPeer">Add peer</MudIconButton>
<MudIconButton Icon="@Icons.Material.Filled.DisabledByDefault" Color="Color.Error" OnClick="BanPeerToolbar" Disabled="@(SelectedItem is null)">Ban peer</MudIconButton>
<MudDivider Vertical="true" />
<MudIconButton Icon="@Icons.Material.Outlined.ViewColumn" Color="Color.Inherit" OnClick="ColumnOptions" title="Choose Columns" />
</MudToolBar>
<DynamicTable T="Peer"
ColumnDefinitions="Columns"
Items="Peers"
MultiSelection="false"
SelectOnRowClick="true"
OnTableDataLongPress="TableDataLongPress"
OnTableDataContextMenu="TableDataContextMenu"
SelectedItemChanged="SelectedItemChanged"
Class="details-list" />

View File

@@ -1,4 +1,5 @@
using Lantean.QBitTorrentClient;
using Lantean.QBTMudBlade.Components.UI;
using Lantean.QBTMudBlade.Models;
using Lantean.QBTMudBlade.Services;
using Microsoft.AspNetCore.Components;
@@ -16,6 +17,9 @@ namespace Lantean.QBTMudBlade.Components
private readonly CancellationTokenSource _timerCancellationToken = new();
private bool? _showFlags;
private const string _toolbar = nameof(_toolbar);
private const string _context = nameof(_context);
[Parameter, EditorRequired]
public string? Hash { get; set; }
@@ -28,6 +32,9 @@ namespace Lantean.QBTMudBlade.Components
[CascadingParameter]
public QBitTorrentClient.Models.Preferences? Preferences { get; set; }
[Inject]
protected IDialogService DialogService { get; set; } = default!;
[Inject]
protected IApiClient ApiClient { get; set; } = default!;
@@ -40,6 +47,14 @@ namespace Lantean.QBTMudBlade.Components
protected bool ShowFlags => _showFlags.GetValueOrDefault();
protected Peer? ContextMenuItem { get; set; }
protected Peer? SelectedItem { get; set; }
protected ContextMenu? ContextMenu { get; set; }
protected DynamicTable<Peer>? Table { get; set; }
protected override async Task OnParametersSetAsync()
{
if (Hash is null)
@@ -83,6 +98,78 @@ namespace Lantean.QBTMudBlade.Components
await InvokeAsync(StateHasChanged);
}
protected async Task AddPeer()
{
if (Hash is null)
{
return;
}
var peers = await DialogService.ShowAddPeersDialog();
if (peers is null || peers.Count == 0)
{
return;
}
await ApiClient.AddPeers([Hash], peers);
}
protected Task BanPeerToolbar()
{
return BanPeer(SelectedItem);
}
protected Task BanPeerContextMenu()
{
return BanPeer(ContextMenuItem);
}
private async Task BanPeer(Peer? peer)
{
if (Hash is null || peer is null)
{
return;
}
await ApiClient.BanPeers([new QBitTorrentClient.Models.PeerId(peer.IPAddress, peer.Port)]);
}
protected Task TableDataContextMenu(TableDataContextMenuEventArgs<Peer> eventArgs)
{
return ShowContextMenu(eventArgs.Item, eventArgs.MouseEventArgs);
}
protected Task TableDataLongPress(TableDataLongPressEventArgs<Peer> eventArgs)
{
return ShowContextMenu(eventArgs.Item, eventArgs.LongPressEventArgs);
}
protected async Task ShowContextMenu(Peer? peer, EventArgs eventArgs)
{
ContextMenuItem = peer;
if (ContextMenu is null)
{
return;
}
await ContextMenu.ToggleMenuAsync(eventArgs);
}
protected void SelectedItemChanged(Peer peer)
{
SelectedItem = peer;
}
protected async Task ColumnOptions()
{
if (Table is null)
{
return;
}
await Table.ShowColumnOptionsDialog();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
@@ -141,10 +228,9 @@ namespace Lantean.QBTMudBlade.Components
new ColumnDefinition<Peer>("Downloaded", p => p.Downloaded, p => @DisplayHelpers.Size(p.Downloaded)),
new ColumnDefinition<Peer>("Uploaded", p => p.Uploaded, p => @DisplayHelpers.Size(p.Uploaded)),
new ColumnDefinition<Peer>("Relevance", p => p.Relevance, p => @DisplayHelpers.Percentage(p.Relevance)),
new ColumnDefinition<Peer>("Files", p => p.Files, p => p.Files),
new ColumnDefinition<Peer>("Files", p => p.Files),
];
protected virtual async Task DisposeAsync(bool disposing)
{
if (!_disposedValue)

View File

@@ -9,8 +9,10 @@ using MudBlazor;
namespace Lantean.QBTMudBlade.Components
{
public partial class TorrentActions
public partial class TorrentActions : IAsyncDisposable
{
private bool _disposedValue;
private List<TorrentAction>? _actions;
[Inject]
@@ -31,6 +33,9 @@ namespace Lantean.QBTMudBlade.Components
[Inject]
public IJSRuntime JSRuntime { get; set; } = default!;
[Inject]
protected IKeyboardService KeyboardService { get; set; } = default!;
[Parameter]
[EditorRequired]
public IEnumerable<string> Hashes { get; set; } = default!;
@@ -103,6 +108,14 @@ namespace Lantean.QBTMudBlade.Components
];
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await KeyboardService.RegisterKeypressEvent("Delete", k => Remove());
}
}
public int CalculateMenuHeight()
{
var visibleActions = GetActions();
@@ -568,6 +581,27 @@ namespace Lantean.QBTMudBlade.Components
return EventCallback.Factory.Create(this, action);
}
}
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)
{
await KeyboardService.UnregisterKeypressEvent("Delete");
}
_disposedValue = true;
}
}
}
public enum RenderType

View File

@@ -2,19 +2,31 @@
<MudMenuItem Icon="@Icons.Material.Filled.AddCircle" IconColor="Color.Info" OnClick="AddTracker">Add trackers</MudMenuItem>
@if (ContextMenuItem is not null)
{
<MudMenuItem Icon="@Icons.Material.Filled.Edit" IconColor="Color.Info" OnClick="EditTracker">Edit tracker URL</MudMenuItem>
<MudMenuItem Icon="@Icons.Material.Filled.Delete" IconColor="Color.Error" OnClick="RemoveTracker">Remove tracker</MudMenuItem>
<MudMenuItem Icon="@Icons.Material.Filled.FolderCopy" IconColor="Color.Info" OnClick="CopyTrackerUrl">Copy tracker url</MudMenuItem>
<MudMenuItem Icon="@Icons.Material.Filled.Edit" IconColor="Color.Info" OnClick="EditTrackerToolbar">Edit tracker URL</MudMenuItem>
<MudMenuItem Icon="@Icons.Material.Filled.Delete" IconColor="Color.Error" OnClick="RemoveTrackerContextMenu">Remove tracker</MudMenuItem>
<MudMenuItem Icon="@Icons.Material.Filled.FolderCopy" IconColor="Color.Info" OnClick="CopyTrackerUrlContextMenu">Copy tracker url</MudMenuItem>
}
</ContextMenu>
<DynamicTable T="Lantean.QBitTorrentClient.Models.TorrentTracker"
<MudToolBar Gutters="false" Dense="true">
<MudIconButton Icon="@Icons.Material.Filled.AddCircle" Color="Color.Info" OnClick="AddTracker">Add trackers</MudIconButton>
<MudIconButton Icon="@Icons.Material.Filled.Edit" Color="Color.Info" OnClick="EditTrackerToolbar" Disabled="@(SelectedItem is null)">Edit tracker URL</MudIconButton>
<MudIconButton Icon="@Icons.Material.Filled.Delete" Color="Color.Error" OnClick="RemoveTrackerToolbar" Disabled="@(SelectedItem is null)">Remove tracker</MudIconButton>
<MudIconButton Icon="@Icons.Material.Filled.FolderCopy" Color="Color.Info" OnClick="CopyTrackerUrlToolbar" Disabled="@(SelectedItem is null)">Copy tracker url</MudIconButton>
<MudDivider Vertical="true" />
<MudIconButton Icon="@Icons.Material.Outlined.ViewColumn" Color="Color.Inherit" OnClick="ColumnOptions" title="Choose Columns" />
</MudToolBar>
<DynamicTable @ref="Table"
T="Lantean.QBitTorrentClient.Models.TorrentTracker"
ColumnDefinitions="Columns"
Items="Trackers"
MultiSelection="false"
SelectOnRowClick="false"
PreSorted="true"
SortDirectionChanged="SortDirectionChanged"
SortColumnChanged="SortColumnChanged"
OnTableDataLongPress="TableDataLongPress"
OnTableDataContextMenu="TableDataContextMenu"
Class="details-list" />
SelectedItemChanged="SelectedItemChanged"
Class="file-list" />

View File

@@ -1,5 +1,6 @@
using Lantean.QBitTorrentClient;
using Lantean.QBitTorrentClient.Models;
using Lantean.QBTMudBlade.Components.UI;
using Lantean.QBTMudBlade.Interop;
using Lantean.QBTMudBlade.Services;
using Microsoft.AspNetCore.Components;
@@ -17,6 +18,9 @@ namespace Lantean.QBTMudBlade.Components
private string? _sortColumn;
private SortDirection _sortDirection;
private const string _toolbar = nameof(_toolbar);
private const string _context = nameof(_context);
[Parameter, EditorRequired]
public string? Hash { get; set; }
@@ -44,8 +48,12 @@ namespace Lantean.QBTMudBlade.Components
protected TorrentTracker? ContextMenuItem { get; set; }
protected TorrentTracker? SelectedItem { get; set; }
protected ContextMenu? ContextMenu { get; set; }
protected DynamicTable<TorrentTracker>? Table { get; set; }
protected override async Task OnParametersSetAsync()
{
if (Hash is null)
@@ -102,6 +110,16 @@ namespace Lantean.QBTMudBlade.Components
StateHasChanged();
}
protected async Task ColumnOptions()
{
if (Table is null)
{
return;
}
await Table.ShowColumnOptionsDialog();
}
protected Task TableDataContextMenu(TableDataContextMenuEventArgs<TorrentTracker> eventArgs)
{
return ShowContextMenu(eventArgs.Item, eventArgs.MouseEventArgs);
@@ -131,6 +149,11 @@ namespace Lantean.QBTMudBlade.Components
await ContextMenu.ToggleMenuAsync(eventArgs);
}
protected void SelectedItemChanged(TorrentTracker torrentTracker)
{
SelectedItem = torrentTracker;
}
protected async Task AddTracker()
{
if (Hash is null)
@@ -147,34 +170,69 @@ namespace Lantean.QBTMudBlade.Components
await ApiClient.AddTrackersToTorrent(Hash, trackers);
}
protected async Task EditTracker()
protected Task EditTrackerToolbar()
{
if (Hash is null || ContextMenuItem is null)
{
return;
}
await DialogService.ShowSingleFieldDialog("Edit Tracker", "Tracker URL", ContextMenuItem.Url, async (value) => await ApiClient.EditTracker(Hash, ContextMenuItem.Url, value));
return EditTracker(SelectedItem);
}
protected async Task RemoveTracker()
protected Task EditTrackerContextMenu()
{
if (Hash is null || ContextMenuItem is null)
{
return;
}
await ApiClient.RemoveTrackers(Hash, [ContextMenuItem.Url]);
return EditTracker(ContextMenuItem);
}
protected async Task CopyTrackerUrl()
protected async Task EditTracker(TorrentTracker? tracker)
{
if (Hash is null || ContextMenuItem is null)
if (Hash is null || tracker is null)
{
return;
}
await JSRuntime.WriteToClipboard(ContextMenuItem.Url);
await DialogService.ShowSingleFieldDialog("Edit Tracker", "Tracker URL", tracker.Url, async (value) => await ApiClient.EditTracker(Hash, tracker.Url, value));
}
protected Task RemoveTrackerToolbar()
{
return RemoveTracker(SelectedItem);
}
protected Task RemoveTrackerContextMenu()
{
return RemoveTracker(ContextMenuItem);
}
protected async Task RemoveTracker(TorrentTracker? tracker)
{
if (Hash is null || tracker is null)
{
return;
}
await ApiClient.RemoveTrackers(Hash, [tracker.Url]);
}
protected Task CopyTrackerUrlToolbar()
{
return CopyTrackerUrl(SelectedItem);
}
protected Task CopyTrackerUrlContextMenu()
{
return CopyTrackerUrl(ContextMenuItem);
}
protected async Task CopyTrackerUrl(TorrentTracker? tracker)
{
if (Hash is null)
{
return;
}
if (tracker is null)
{
return;
}
await JSRuntime.WriteToClipboard(tracker.Url);
}
protected override async Task OnAfterRenderAsync(bool firstRender)

View File

@@ -1,9 +1,11 @@
@inherits MudMenu
@inherits MudComponentBase
<MudMenu @ref="FakeMenu" Style="display: none" OpenChanged="FakeOpenChanged"></MudMenu>
@* The portal has to include the cascading values inside, because it's not able to teletransport the cascade *@
<MudPopover tracker="@Id"
Open="@_open"
Class="@PopoverClass"
Class="unselectable"
MaxHeight="@MaxHeight"
AnchorOrigin="@AnchorOrigin"
TransformOrigin="TransformOrigin"
@@ -11,13 +13,16 @@
OverflowBehavior="OverflowBehavior.FlipAlways"
Style="@_popoverStyle"
@ontouchend:preventDefault>
<CascadingValue Value="@((MudMenu)this)">
<MudList T="object"
Class="@ListClass"
<CascadingValue Value="@(FakeMenu)">
@if (_showChildren)
{
<MudList T="object"
Class="unselectable"
Dense="@Dense">
@ChildContent
</MudList>
}
</CascadingValue>
</MudPopover>
<MudOverlay Visible="@(_open)" OnClick="@ToggleMenuAsync" LockScroll="@LockScroll" />
<MudOverlay Visible="@(_open)" LockScroll="@LockScroll" AutoClose="true" OnClosed="@CloseMenuAsync" />

View File

@@ -5,17 +5,18 @@ using Microsoft.JSInterop;
using MudBlazor;
using MudBlazor.Utilities;
namespace Lantean.QBTMudBlade.Components
namespace Lantean.QBTMudBlade.Components.UI
{
// This is a very hacky approach but works for now.
// This needs to inherit from MudMenu because MudMenuItem needs a MudMenu passed to it to control the close of the menu when an item is clicked.
// MudPopover isn't ideal for this because that is designed to be used relative to an activator which in these cases it isn't.
// Ideally this should be changed to use something like the way the DialogService works.
public partial class ContextMenu : MudMenu
{
private const double _diff = 64;
// Ideally this should be changed to use something like the way the DialogService works.
// Or - rework this to have a hidden MudMenu and hook into the OpenChanged event to monitor when the MudMenuItem closes it.
public partial class ContextMenu : MudComponentBase
{
private bool _open;
private bool _showChildren;
private string? _popoverStyle;
private string? _id;
@@ -23,7 +24,7 @@ namespace Lantean.QBTMudBlade.Components
private double _y;
private bool _isResized = false;
private const double _drawerWidth = 235;
private const double _diff = 64;
private string Id
{
@@ -41,44 +42,99 @@ namespace Lantean.QBTMudBlade.Components
[Inject]
public IPopoverService PopoverService { get; set; } = default!;
[CascadingParameter(Name = "DrawerOpen")]
public bool DrawerOpen { get; set; }
/// <summary>
/// If true, compact vertical padding will be applied to all menu items.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.PopupAppearance)]
public bool Dense { get; set; }
/// <summary>
/// Set to true if you want to prevent page from scrolling when the menu is open
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.PopupAppearance)]
public bool LockScroll { get; set; }
/// <summary>
/// If true, the list menu will be same width as the parent.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.PopupAppearance)]
public bool FullWidth { get; set; }
/// <summary>
/// Sets the max height the menu can have when open.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.PopupAppearance)]
public int? MaxHeight { get; set; }
/// <summary>
/// Set the anchor origin point to determine where the popover will open from.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.PopupAppearance)]
public Origin AnchorOrigin { get; set; } = Origin.TopLeft;
/// <summary>
/// Sets the transform origin point for the popover.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.PopupAppearance)]
public Origin TransformOrigin { get; set; } = Origin.TopLeft;
/// <summary>
/// If true, menu will be disabled.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.Behavior)]
public bool Disabled { get; set; }
/// <summary>
/// Gets or sets whether to show a ripple effect when the user clicks the button. Default is true.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.Appearance)]
public bool Ripple { get; set; } = true;
/// <summary>
/// Determines whether the component has a drop-shadow. Default is true
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.Appearance)]
public bool DropShadow { get; set; } = true;
/// <summary>
/// Add menu items here
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.PopupBehavior)]
public RenderFragment? ChildContent { get; set; }
/// <summary>
/// Fired when the menu <see cref="Open"/> property changes.
/// </summary>
[Parameter]
[Category(CategoryTypes.Menu.PopupBehavior)]
public EventCallback<bool> OpenChanged { get; set; }
[Parameter]
public bool InsideDrawer { get; set; }
public int AdjustmentX { get; set; }
public new string? Label { get; }
[Parameter]
public int AdjustmentY { get; set; }
public new string? AriaLabel { get; }
protected MudMenu? FakeMenu { get; set; }
public new string? Icon { get; }
public new Color IconColor { get; } = Color.Inherit;
public new string? StartIcon { get; }
public new string? EndIcon { get; }
public new Color Color { get; } = Color.Default;
public new Size Size { get; } = Size.Medium;
public new Variant Variant { get; } = Variant.Text;
public new bool PositionAtCursor { get; } = true;
public new RenderFragment? ActivatorContent { get; } = null;
public new MouseEvent ActivationEvent { get; } = MouseEvent.LeftClick;
public new string? ListClass { get; } = "unselectable";
public new string? PopoverClass { get; } = "unselectable";
public ContextMenu()
protected void FakeOpenChanged(bool value)
{
AnchorOrigin = Origin.TopLeft;
TransformOrigin = Origin.TopLeft;
if (!value)
{
_open = false;
}
StateHasChanged();
}
/// <summary>
@@ -86,9 +142,8 @@ namespace Lantean.QBTMudBlade.Components
/// </summary>
/// <param name="args">
/// The arguments of the calling mouse/pointer event.
/// If <see cref="PositionAtCursor"/> is true, the menu will be positioned using the coordinates in this parameter.
/// </param>
public new async Task OpenMenuAsync(EventArgs args)
public async Task OpenMenuAsync(EventArgs args)
{
if (Disabled)
{
@@ -98,6 +153,11 @@ namespace Lantean.QBTMudBlade.Components
// long press on iOS triggers selection, so clear it
await JSRuntime.ClearSelection();
if (args is not LongPressEventArgs)
{
_showChildren = true;
}
_open = true;
_isResized = false;
StateHasChanged();
@@ -111,11 +171,29 @@ namespace Lantean.QBTMudBlade.Components
StateHasChanged();
await OpenChanged.InvokeAsync(_open);
// long press on iOS triggers selection, so clear it
await JSRuntime.ClearSelection();
if (args is LongPressEventArgs)
{
await Task.Delay(1000);
_showChildren = true;
}
}
/// <summary>
/// Sets the popover style ONLY when there is an activator.
/// Closes the menu.
/// </summary>
public Task CloseMenuAsync()
{
_open = false;
_popoverStyle = null;
StateHasChanged();
return OpenChanged.InvokeAsync(_open);
}
private void SetPopoverStyle(double x, double y)
{
_popoverStyle = $"margin-top: {y.ToPx()}; margin-left: {x.ToPx()};";
@@ -124,7 +202,7 @@ namespace Lantean.QBTMudBlade.Components
/// <summary>
/// Toggle the visibility of the menu.
/// </summary>
public new async Task ToggleMenuAsync(EventArgs args)
public async Task ToggleMenuAsync(EventArgs args)
{
if (Disabled)
{
@@ -169,17 +247,13 @@ namespace Lantean.QBTMudBlade.Components
}
// the bottom position of the popover will be rendered off screen
if ((_y - _diff + contextMenuHeight.Value) >= (mainContentSize.Height))
if (_y - _diff + contextMenuHeight.Value >= mainContentSize.Height)
{
// adjust the top of the context menu
var overshoot = Math.Abs(mainContentSize.Height - (_y - _diff + contextMenuHeight.Value));
_y -= overshoot;
//if (_y < 70)
//{
// _y = 70;
//}
if ((_y - _diff + contextMenuHeight) >= mainContentSize.Height)
if (_y - _diff + contextMenuHeight >= mainContentSize.Height)
{
MaxHeight = (int)(mainContentSize.Height - _y + _diff);
}
@@ -214,7 +288,7 @@ namespace Lantean.QBTMudBlade.Components
throw new NotSupportedException("Invalid eventArgs type.");
}
return (x - (DrawerOpen && !InsideDrawer ? _drawerWidth : 0), y - (InsideDrawer ? _diff : 0));
return (x + AdjustmentX, y + AdjustmentY);
}
}
}

View File

@@ -3,9 +3,9 @@ using Microsoft.AspNetCore.Components.Web;
using MudBlazor;
using MudBlazor.Utilities;
namespace Lantean.QBTMudBlade.Components
namespace Lantean.QBTMudBlade.Components.UI
{
public partial class FakeNavLink
public partial class CustomNavLink
{
[Parameter]
public bool Active { get; set; }

View File

@@ -23,9 +23,13 @@
SelectedItemsChanged="SelectedItemsChangedInternal"
OnRowClick="OnRowClickInternal"
RowStyleFunc="RowStyleFuncInternal"
RowClassFunc="RowClassFuncInternal"
Class="@Class">
<ColGroup>
<col style="width: 30px" />
@if (MultiSelection)
{
<col style="width: 30px" />
}
@foreach (var column in GetColumns())
{
<col style="@(GetColumnStyle(column))" />
@@ -51,9 +55,9 @@
<RowTemplate>
@foreach (var column in GetColumns())
{
<MudTdExtended @ref="_tds[column.Id]" DataLabel="@column.Header" Class="@(GetColumnClass(column, context))" Style="@(GetColumnStyle(column))" OnLongPress="@(c => OnLongPressInternal(c, column.Id, context))" OnContextMenu="@(c => OnContextMenuInternal(c, column.Id, context))">
<TdExtended @ref="_tds[column.Id]" DataLabel="@column.Header" Class="@(GetColumnClass(column, context))" Style="@(GetColumnStyle(column))" OnLongPress="@(c => OnLongPressInternal(c, column.Id, context))" OnContextMenu="@(c => OnContextMenuInternal(c, column.Id, context))">
@column.RowTemplate(column.GetRowContext(context))
</MudTdExtended>
</TdExtended>
}
</RowTemplate>
</MudTable>

View File

@@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using MudBlazor;
namespace Lantean.QBTMudBlade.Components
namespace Lantean.QBTMudBlade.Components.UI
{
public partial class DynamicTable<T> : MudComponentBase
{
@@ -71,6 +71,9 @@ namespace Lantean.QBTMudBlade.Components
[Parameter]
public EventCallback<TableDataLongPressEventArgs<T>> OnTableDataLongPress { get; set; }
[Parameter]
public Func<T, int, string>? RowClassFunc { get; set; }
protected IEnumerable<T>? OrderedItems => GetOrderedItems();
protected HashSet<string> SelectedColumns { get; set; } = [];
@@ -81,7 +84,7 @@ namespace Lantean.QBTMudBlade.Components
private SortDirection _sortDirection;
private readonly Dictionary<string, MudTdExtended> _tds = [];
private readonly Dictionary<string, TdExtended> _tds = [];
protected override async Task OnInitializedAsync()
{
@@ -225,14 +228,11 @@ namespace Lantean.QBTMudBlade.Components
}
}
}
else
else if (SelectOnRowClick && !SelectedItems.Contains(eventArgs.Item))
{
if (!SelectedItems.Contains(eventArgs.Item))
{
SelectedItems.Clear();
SelectedItems.Add(eventArgs.Item);
await SelectedItemChanged.InvokeAsync(eventArgs.Item);
}
SelectedItems.Clear();
SelectedItems.Add(eventArgs.Item);
await SelectedItemChanged.InvokeAsync(eventArgs.Item);
}
await OnRowClick.InvokeAsync(eventArgs);
@@ -241,14 +241,23 @@ namespace Lantean.QBTMudBlade.Components
protected string RowStyleFuncInternal(T item, int index)
{
var style = "user-select: none; cursor: pointer;";
//EqualityComparer<T>.Default.Equals(item, SelectedItem) ||
if (SelectedItems.Contains(item))
if (SelectOnRowClick && SelectedItems.Contains(item))
{
style += " background-color: var(--mud-palette-gray-dark); color: var(--mud-palette-gray-light) !important;";
}
return style;
}
protected string RowClassFuncInternal(T item, int index)
{
if (RowClassFunc is not null)
{
return RowClassFunc(item, index);
}
return string.Empty;
}
protected async Task SelectedItemsChangedInternal(HashSet<T> selectedItems)
{
await SelectedItemsChanged.InvokeAsync(selectedItems);

View File

@@ -0,0 +1,3 @@
<MudField Variant="Variant.Outlined" InnerPadding="false" Label="@Label" HelperText="@HelperText">
<TickSwitch T="bool" Value="@Value" ValueChanged="ValueChangedCallback" Class="pt-1 pb-1" Disabled="Disabled" Validation="Validation" />
</MudField>

View File

@@ -1,8 +1,8 @@
using Microsoft.AspNetCore.Components;
namespace Lantean.QBTMudBlade.Components
namespace Lantean.QBTMudBlade.Components.UI
{
public partial class MudFieldSwitch
public partial class FieldSwitch
{
/// <inheritdoc cref="MudBlazor.MudBooleanInput{T}.Value"/>
[Parameter]

View File

@@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Components;
namespace Lantean.QBTMudBlade.Components
namespace Lantean.QBTMudBlade.Components.UI
{
/// <summary>
/// A simple razor wrapper that only renders the child content without any additonal html markup

View File

@@ -2,7 +2,7 @@
using MudBlazor;
using MudBlazor.Utilities;
namespace Lantean.QBTMudBlade.Components
namespace Lantean.QBTMudBlade.Components.UI
{
public partial class SortLabel : MudComponentBase
{

View File

@@ -1,7 +1,7 @@
using Microsoft.AspNetCore.Components.Web;
using MudBlazor;
namespace Lantean.QBTMudBlade.Components
namespace Lantean.QBTMudBlade.Components.UI
{
public class TableDataContextMenuEventArgs<T> : EventArgs
{

View File

@@ -1,6 +1,6 @@
using MudBlazor;
namespace Lantean.QBTMudBlade.Components
namespace Lantean.QBTMudBlade.Components.UI
{
public class TableDataLongPressEventArgs<T> : EventArgs
{

View File

@@ -2,9 +2,9 @@
using Microsoft.AspNetCore.Components.Web;
using MudBlazor;
namespace Lantean.QBTMudBlade.Components
namespace Lantean.QBTMudBlade.Components.UI
{
public partial class MudTdExtended : MudTd
public partial class TdExtended : MudTd
{
[Parameter]
public EventCallback<LongPressEventArgs> OnLongPress { get; set; }

View File

@@ -2,4 +2,5 @@
ColumnDefinitions="Columns"
Items="WebSeeds"
MultiSelection="false"
SelectOnRowClick="false"
Class="details-list" />

View File

@@ -98,6 +98,11 @@ namespace Lantean.QBTMudBlade
public static async Task InvokeDeleteTorrentDialog(this IDialogService dialogService, IApiClient apiClient, params string[] hashes)
{
if (hashes.Length == 0)
{
return;
}
var parameters = new DialogParameters
{
{ nameof(DeleteDialog.Count), hashes.Length }
@@ -118,9 +123,9 @@ namespace Lantean.QBTMudBlade
await Task.Delay(0);
}
public static async Task<string?> ShowAddCategoryDialog(this IDialogService dialogService, IApiClient apiClient)
public static async Task<string?> InvokeAddCategoryDialog(this IDialogService dialogService, IApiClient apiClient)
{
var reference = await dialogService.ShowAsync<CategoryPropertiesDialog>("New Category", NonBlurFormDialogOptions);
var reference = await dialogService.ShowAsync<CategoryPropertiesDialog>("Add Category", NonBlurFormDialogOptions);
var dialogResult = await reference.Result;
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
{
@@ -134,7 +139,7 @@ namespace Lantean.QBTMudBlade
return category.Name;
}
public static async Task<string?> ShowEditCategoryDialog(this IDialogService dialogService, IApiClient apiClient, string categoryName)
public static async Task<string?> InvokeEditCategoryDialog(this IDialogService dialogService, IApiClient apiClient, string categoryName)
{
var category = (await apiClient.GetAllCategories()).FirstOrDefault(c => c.Key == categoryName).Value;
var parameters = new DialogParameters
@@ -174,7 +179,7 @@ namespace Lantean.QBTMudBlade
public static async Task<HashSet<string>?> ShowAddTrackersDialog(this IDialogService dialogService)
{
var reference = await dialogService.ShowAsync<AddTrackerDialog>("Add Tags", NonBlurFormDialogOptions);
var reference = await dialogService.ShowAsync<AddTrackerDialog>("Add Tracker", NonBlurFormDialogOptions);
var dialogResult = await reference.Result;
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
@@ -187,6 +192,21 @@ namespace Lantean.QBTMudBlade
return tags;
}
public static async Task<HashSet<QBitTorrentClient.Models.PeerId>?> ShowAddPeersDialog(this IDialogService dialogService)
{
var reference = await dialogService.ShowAsync<AddPeerDialog>("Add Peer", NonBlurFormDialogOptions);
var dialogResult = await reference.Result;
if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
{
return null;
}
var peers = (HashSet<QBitTorrentClient.Models.PeerId>)dialogResult.Data;
return peers;
}
public static async Task<bool> ShowConfirmDialog(this IDialogService dialogService, string title, string content)
{
var parameters = new DialogParameters

View File

@@ -11,10 +11,10 @@
<ItemGroup>
<PackageReference Include="Blazored.LocalStorage" Version="4.5.0" />
<PackageReference Include="ByteSize" Version="2.1.2" />
<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.AspNetCore.Components.WebAssembly" Version="8.0.7" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.7" PrivateAssets="all" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="MudBlazor" Version="7.0.0" />
<PackageReference Include="MudBlazor" Version="7.5.0" />
<PackageReference Include="MudBlazor.ThemeManager" Version="2.0.0" />
</ItemGroup>
@@ -26,6 +26,9 @@
<Content Update="Components\Dialogs\ManageCategoriesDialog.razor">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
<Content Update="Pages\TagManagement.razor">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
<Content Update="Pages\Statistics.razor">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>

View File

@@ -3,7 +3,7 @@
<CascadingValue Value="DrawerOpen" Name="DrawerOpen">
<EnhancedErrorBoundary @ref="ErrorBoundary" OnClear="Cleared">
<MudThemeProvider @ref="MudThemeProvider" @bind-IsDarkMode="IsDarkMode" Theme="Theme" />
<MudDialogProvider />
<MudDialogProvider CloseButton="true" CloseOnEscapeKey="true" />
<MudSnackbarProvider />
<MudPopoverProvider />
@@ -20,7 +20,7 @@
<MudIconButton Icon="@Icons.Material.Filled.Error" Color="Color.Default" OnClick="ToggleErrorDrawer" />
</MudBadge>
}
<MudSwitch T="bool" Label="Dark Mode" LabelPosition="LabelPosition.End" Value="IsDarkMode" ValueChanged="DarkModeChanged" />
<MudSwitch T="bool" Label="Dark Mode" LabelPosition="LabelPosition.End" Value="IsDarkMode" ValueChanged="DarkModeChanged" Class="pl-3" />
@if (ShowMenu)
{
<Menu />

View File

@@ -3,7 +3,7 @@
<MudDrawer Open="DrawerOpen" ClipMode="DrawerClipMode.Always" Elevation="2" Overlay="false">
<MudNavMenu>
<MudNavLink Icon="@(Icons.Material.Outlined.NavigateBefore)" OnClick="NavigateBack">Back</MudNavLink>
<MudNavLink Icon="@(Icons.Material.Outlined.Navigation)" OnClick="NavigateBack">Torrents</MudNavLink>
<MudDivider />
<MudNavLink Icon="@Icons.Material.Filled.PieChart" Href="/statistics">Statistics</MudNavLink>
<MudNavLink Icon="@Icons.Material.Filled.Search" Href="/search">Search</MudNavLink>
@@ -11,6 +11,9 @@
<MudNavLink Icon="@Icons.Material.Filled.List" Href="/log">Execution Log</MudNavLink>
<MudNavLink Icon="@Icons.Material.Filled.DisabledByDefault" Href="/blocks">Blocked IPs</MudNavLink>
<MudDivider />
<MudNavLink Icon="@Icons.Material.Filled.Label" Href="/tags">Tag Management</MudNavLink>
<MudNavLink Icon="@Icons.Material.Filled.List" Href="/categories">Category Management</MudNavLink>
<MudDivider />
<MudNavLink Icon="@Icons.Material.Filled.Settings" Href="/settings">Settings</MudNavLink>
</MudNavMenu>
</MudDrawer>

View File

@@ -21,7 +21,7 @@ namespace Lantean.QBTMudBlade.Models
Index = index;
Priority = priority;
Progress = progress;
Size = priority == Priority.DoNotDownload ? 0 : size;
Size = size;
Availability = availability;
IsFolder = isFolder;
Level = level;

View File

@@ -0,0 +1,100 @@
using System.Text.Json.Serialization;
namespace Lantean.QBTMudBlade.Models
{
public class KeyboardEvent
{
public KeyboardEvent(string key)
{
Key = key;
Code = key;
}
[JsonConstructor]
public KeyboardEvent(string key, bool repeat, bool ctrlKey, bool shiftKey, bool altKey, bool metaKey, string code) : this(key)
{
Repeat = repeat;
CtrlKey = ctrlKey;
ShiftKey = shiftKey;
AltKey = altKey;
MetaKey = metaKey;
Code = code;
}
/// <summary>
/// The key value of the key represented by the event.
/// If the value has a printed representation, this attribute's value is the same as the char attribute.
/// Otherwise, it's one of the key value strings specified in 'Key values'.
/// If the key can't be identified, this is the string "Unidentified"
/// </summary>
[JsonPropertyName("key")]
public string Key { get; set; }
/// <summary>
/// true if a key has been depressed long enough to trigger key repetition, otherwise false.
/// </summary>
[JsonPropertyName("repeat")]
public bool Repeat { get; set; }
/// <summary>
/// true if the control key was down when the event was fired. false otherwise.
/// </summary>
[JsonPropertyName("ctrlKey")]
public bool CtrlKey { get; set; }
/// <summary>
/// true if the shift key was down when the event was fired. false otherwise.
/// </summary>
[JsonPropertyName("shiftKey")]
public bool ShiftKey { get; set; }
/// <summary>
/// true if the alt key was down when the event was fired. false otherwise.
/// </summary>
[JsonPropertyName("altKey")]
public bool AltKey { get; set; }
/// <summary>
/// true if the meta key was down when the event was fired. false otherwise.
/// </summary>
[JsonPropertyName("metaKey")]
public bool MetaKey { get; set; }
[JsonPropertyName("code")]
public string Code { get; }
public override bool Equals(object? obj)
{
return obj is KeyboardEvent @event &&
Key == @event.Key &&
Repeat == @event.Repeat &&
CtrlKey == @event.CtrlKey &&
ShiftKey == @event.ShiftKey &&
AltKey == @event.AltKey &&
MetaKey == @event.MetaKey;
}
public override string? ToString()
{
var modifiers = (CtrlKey ? "Ctrl" : "") + (ShiftKey ? "Shift" : "") + (AltKey ? "Alt" : "") + (MetaKey ? "Meta" : "");
return modifiers + (modifiers.Length == 0 ? "" : "+") + Key + (Repeat ? "-repeated" : "");
//return Key + (CtrlKey ? '1' : '0') + (ShiftKey ? '1' : '0') + (AltKey ? '1' : '0') + (MetaKey ? '1' : '0') + (Repeat ? '1' : '0');
}
public override int GetHashCode()
{
return HashCode.Combine(Key, Repeat, CtrlKey, ShiftKey, AltKey, MetaKey, Code);
}
public static implicit operator KeyboardEvent(string input)
{
return new KeyboardEvent(input);
}
public static implicit operator string(KeyboardEvent input)
{
return input.ToString()!;
}
}
}

View File

@@ -26,37 +26,11 @@
</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">
<NoRecordsContent>
<MudText Typo="Typo.h6">Nothing has been blocked.</MudText>
</NoRecordsContent>
<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>
<DynamicTable @ref="Table"
T="Lantean.QBitTorrentClient.Models.PeerLog"
ColumnDefinitions="Columns"
Items="Results"
MultiSelection="false"
SelectOnRowClick="false"
RowClassFunc="RowClass"
Class="search-list" />

View File

@@ -1,5 +1,7 @@
using Blazored.LocalStorage;
using Lantean.QBitTorrentClient;
using Lantean.QBitTorrentClient.Models;
using Lantean.QBTMudBlade.Components.UI;
using Lantean.QBTMudBlade.Models;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
@@ -33,10 +35,12 @@ namespace Lantean.QBTMudBlade.Pages
protected LogForm Model { get; set; } = new LogForm();
protected List<QBitTorrentClient.Models.PeerLog>? Results { get; private set; }
protected List<PeerLog>? Results { get; private set; }
protected MudSelect<string>? CategoryMudSelect { get; set; }
protected DynamicTable<PeerLog>? Table { get; set; }
protected override async Task OnInitializedAsync()
{
var selectedTypes = await LocalStorage.GetItemAsync<IEnumerable<string>>(_selectedTypesStorageKey);
@@ -90,7 +94,7 @@ namespace Lantean.QBTMudBlade.Pages
}
}
protected static string RowClass(QBitTorrentClient.Models.PeerLog log, int index)
protected static string RowClass(PeerLog log, int index)
{
return $"log-{(log.Blocked ? "critical" : "normal")}";
}
@@ -149,5 +153,15 @@ namespace Lantean.QBTMudBlade.Pages
}
}
protected IEnumerable<ColumnDefinition<PeerLog>> Columns => ColumnsDefinitions;
public static List<ColumnDefinition<PeerLog>> ColumnsDefinitions { get; } =
[
new ColumnDefinition<PeerLog>("Id", l => l.Id),
new ColumnDefinition<PeerLog>("Message", l => l.IPAddress),
new ColumnDefinition<PeerLog>("Timestamp", l => l.Timestamp, l => @DisplayHelpers.DateTime(l.Timestamp)),
new ColumnDefinition<PeerLog>("Blocked", l => l.Blocked ? "Blocked" : "Banned"),
new ColumnDefinition<PeerLog>("Reason", l => l.Reason),
];
}
}

View File

@@ -0,0 +1,39 @@
@page "/categories"
@layout OtherLayout
<MudToolBar Gutters="false" Dense="true">
@if (!DrawerOpen)
{
<MudIconButton Icon="@Icons.Material.Outlined.NavigateBefore" OnClick="NavigateBack" title="Back to torrent list" />
<MudDivider Vertical="true" />
}
<MudDivider Vertical="true" />
<MudIconButton Icon="@Icons.Material.Filled.PlaylistAdd" OnClick="AddCategory" title="Add Category" />
<MudText Class="pl-5 no-wrap">Categories</MudText>
</MudToolBar>
<DynamicTable @ref="Table"
T="Category"
ColumnDefinitions="Columns"
Items="Results"
MultiSelection="false"
SelectOnRowClick="false"
Class="details-list" />
@code {
private RenderFragment<RowContext<Category>> ActionsColumn
{
get
{
return context => __builder =>
{
var value = (Category?)context.GetValue();
<MudButtonGroup>
<MudIconButton Icon="@Icons.Material.Filled.Edit" Color="Color.Warning" OnClick="@(e => EditCategory(value?.Name))" />
<MudIconButton Icon="@Icons.Material.Filled.Delete" Color="Color.Error" OnClick="@(e => DeleteCategory(value?.Name))" />
</MudButtonGroup>
;
};
}
}
}

View File

@@ -0,0 +1,90 @@
using Blazored.LocalStorage;
using Lantean.QBitTorrentClient;
using Lantean.QBTMudBlade.Components.UI;
using Lantean.QBTMudBlade.Models;
using Microsoft.AspNetCore.Components;
using MudBlazor;
namespace Lantean.QBTMudBlade.Pages
{
public partial class CategoryManagement
{
private readonly Dictionary<string, RenderFragment<RowContext<Category>>> _columnRenderFragments = [];
[Inject]
protected IApiClient ApiClient { get; set; } = default!;
[Inject]
protected IDialogService DialogService { get; set; } = default!;
[Inject]
protected NavigationManager NavigationManager { get; set; } = default!;
[Inject]
protected ILocalStorageService LocalStorage { get; set; } = default!;
[CascadingParameter(Name = "DrawerOpen")]
public bool DrawerOpen { get; set; }
[CascadingParameter]
public MainData? MainData { get; set; }
protected IEnumerable<Category>? Results => MainData?.Categories.Values;
protected DynamicTable<Category>? Table { get; set; }
public CategoryManagement()
{
_columnRenderFragments.Add("Actions", ActionsColumn);
}
protected void NavigateBack()
{
NavigationManager.NavigateTo("/");
}
protected async Task DeleteCategory(string? name)
{
if (name is null)
{
return;
}
await ApiClient.RemoveCategories(name);
}
protected async Task AddCategory()
{
await DialogService.InvokeAddCategoryDialog(ApiClient);
}
protected async Task EditCategory(string? name)
{
if (name is null)
{
return;
}
await DialogService.InvokeEditCategoryDialog(ApiClient, name);
}
protected IEnumerable<ColumnDefinition<Category>> Columns => GetColumnDefinitions();
private IEnumerable<ColumnDefinition<Category>> GetColumnDefinitions()
{
foreach (var columnDefinition in ColumnsDefinitions)
{
if (_columnRenderFragments.TryGetValue(columnDefinition.Header, out var fragment))
{
columnDefinition.RowTemplate = fragment;
}
yield return columnDefinition;
}
}
public static List<ColumnDefinition<Category>> ColumnsDefinitions { get; } =
[
new ColumnDefinition<Category>("Name", l => l.Name),
new ColumnDefinition<Category>("Save path", l => l.SavePath)
];
}
}

View File

@@ -34,32 +34,11 @@
</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>
<DynamicTable @ref="Table"
T="Lantean.QBitTorrentClient.Models.Log"
ColumnDefinitions="Columns"
Items="Results"
MultiSelection="false"
SelectOnRowClick="false"
RowClassFunc="RowClass"
Class="search-list" />

View File

@@ -1,5 +1,7 @@
using Blazored.LocalStorage;
using Lantean.QBitTorrentClient;
using Lantean.QBitTorrentClient.Models;
using Lantean.QBTMudBlade.Components.UI;
using Lantean.QBTMudBlade.Models;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
@@ -37,6 +39,8 @@ namespace Lantean.QBTMudBlade.Pages
protected MudSelect<string>? CategoryMudSelect { get; set; }
protected DynamicTable<QBitTorrentClient.Models.Log>? Table { get; set; }
protected override async Task OnInitializedAsync()
{
var selectedTypes = await LocalStorage.GetItemAsync<IEnumerable<string>>(_selectedTypesStorageKey);
@@ -154,5 +158,14 @@ namespace Lantean.QBTMudBlade.Pages
}
}
protected IEnumerable<ColumnDefinition<QBitTorrentClient.Models.Log>> Columns => ColumnsDefinitions;
public static List<ColumnDefinition<QBitTorrentClient.Models.Log>> ColumnsDefinitions { get; } =
[
new ColumnDefinition<QBitTorrentClient.Models.Log>("Id", l => l.Id),
new ColumnDefinition<QBitTorrentClient.Models.Log>("Message", l => l.Message),
new ColumnDefinition<QBitTorrentClient.Models.Log>("Timestamp", l => l.Timestamp, l => @DisplayHelpers.DateTime(l.Timestamp)),
new ColumnDefinition<QBitTorrentClient.Models.Log>("Log type", l => l.Type),
];
}
}

View File

@@ -48,7 +48,7 @@ namespace Lantean.QBTMudBlade.Pages
#if DEBUG
protected override Task OnInitializedAsync()
{
return DoLogin("admin", "42jMKTW7C");
return DoLogin("admin", "ALKEVRPZP");
}
#endif
}

View File

@@ -49,34 +49,10 @@
</MudCardContent>
</MudCard>
<MudTable
Items="Results"
T="Lantean.QBitTorrentClient.Models.SearchResult"
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">
<HeaderContent>
<MudTh>Name</MudTh>
<MudTh>Size</MudTh>
<MudTh>Seeders</MudTh>
<MudTh>Leechers</MudTh>
<MudTh>Search engine</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="Name">@context.FileName</MudTd>
<MudTd DataLabel="Size">@DisplayHelpers.Size(context.FileSize)</MudTd>
<MudTd DataLabel="Seeders">@context.Seeders</MudTd>
<MudTd DataLabel="Leechers">@context.Leechers</MudTd>
<MudTd DataLabel="Search engine">@context.SiteUrl</MudTd>
</RowTemplate>
</MudTable>
<DynamicTable @ref="Table"
T="Lantean.QBitTorrentClient.Models.SearchResult"
ColumnDefinitions="Columns"
Items="Results"
MultiSelection="false"
SelectOnRowClick="false"
Class="search-list" />

View File

@@ -1,4 +1,5 @@
using Lantean.QBitTorrentClient;
using Lantean.QBTMudBlade.Components.UI;
using Lantean.QBTMudBlade.Models;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
@@ -42,6 +43,8 @@ namespace Lantean.QBTMudBlade.Pages
protected IEnumerable<QBitTorrentClient.Models.SearchResult>? Results => _searchResults?.Results;
protected DynamicTable<QBitTorrentClient.Models.SearchResult>? Table { get; set; }
protected override async Task OnInitializedAsync()
{
_plugins = await ApiClient.GetSearchPlugins();
@@ -146,6 +149,17 @@ namespace Lantean.QBTMudBlade.Pages
}
}
protected IEnumerable<ColumnDefinition<QBitTorrentClient.Models.SearchResult>> Columns => ColumnsDefinitions;
public static List<ColumnDefinition<QBitTorrentClient.Models.SearchResult>> ColumnsDefinitions { get; } =
[
new ColumnDefinition<QBitTorrentClient.Models.SearchResult>("Name", l => l.FileName),
new ColumnDefinition<QBitTorrentClient.Models.SearchResult>("Size", l => @DisplayHelpers.Size(l.FileSize)),
new ColumnDefinition<QBitTorrentClient.Models.SearchResult>("Seeders", l => l.Seeders),
new ColumnDefinition<QBitTorrentClient.Models.SearchResult>("Leechers", l => l.Leechers),
new ColumnDefinition<QBitTorrentClient.Models.SearchResult>("Search engine", l => l.SiteUrl),
];
protected virtual void Dispose(bool disposing)
{
if (!_disposedValue)

View File

@@ -0,0 +1,38 @@
@page "/tags"
@layout OtherLayout
<MudToolBar Gutters="false" Dense="true">
@if (!DrawerOpen)
{
<MudIconButton Icon="@Icons.Material.Outlined.NavigateBefore" OnClick="NavigateBack" title="Back to torrent list" />
<MudDivider Vertical="true" />
}
<MudDivider Vertical="true" />
<MudIconButton Icon="@Icons.Material.Filled.NewLabel" OnClick="AddTag" title="Add Tag" />
<MudText Class="pl-5 no-wrap">Tags</MudText>
</MudToolBar>
<DynamicTable @ref="Table"
T="string"
ColumnDefinitions="Columns"
Items="Results"
MultiSelection="false"
SelectOnRowClick="false"
Class="details-list" />
@code {
private RenderFragment<RowContext<string>> ActionsColumn
{
get
{
return context => __builder =>
{
var value = (string?)context.GetValue();
<MudButtonGroup>
<MudIconButton Icon="@Icons.Material.Filled.Delete" Color="Color.Error" OnClick="@(e => DeleteTag(value))" />
</MudButtonGroup>
;
};
}
}
}

View File

@@ -0,0 +1,88 @@
using Blazored.LocalStorage;
using Lantean.QBitTorrentClient;
using Lantean.QBTMudBlade.Components.UI;
using Lantean.QBTMudBlade.Models;
using Microsoft.AspNetCore.Components;
using MudBlazor;
namespace Lantean.QBTMudBlade.Pages
{
public partial class TagManagement
{
private readonly Dictionary<string, RenderFragment<RowContext<string>>> _columnRenderFragments = [];
[Inject]
protected IApiClient ApiClient { get; set; } = default!;
[Inject]
protected IDialogService DialogService { get; set; } = default!;
[Inject]
protected NavigationManager NavigationManager { get; set; } = default!;
[Inject]
protected ILocalStorageService LocalStorage { get; set; } = default!;
[CascadingParameter(Name = "DrawerOpen")]
public bool DrawerOpen { get; set; }
[CascadingParameter]
public MainData? MainData { get; set; }
protected IEnumerable<string>? Results => MainData?.Tags;
protected DynamicTable<string>? Table { get; set; }
public TagManagement()
{
_columnRenderFragments.Add("Actions", ActionsColumn);
}
protected void NavigateBack()
{
NavigationManager.NavigateTo("/");
}
protected async Task DeleteTag(string? tag)
{
if (tag is null)
{
return;
}
await ApiClient.DeleteTags(tag);
}
protected async Task AddTag()
{
var tags = await DialogService.ShowAddTagsDialog();
if (tags is null || tags.Count == 0)
{
return;
}
await ApiClient.CreateTags(tags);
}
protected IEnumerable<ColumnDefinition<string>> Columns => GetColumnDefinitions();
private IEnumerable<ColumnDefinition<string>> GetColumnDefinitions()
{
foreach (var columnDefinition in ColumnsDefinitions)
{
if (_columnRenderFragments.TryGetValue(columnDefinition.Header, out var fragment))
{
columnDefinition.RowTemplate = fragment;
}
yield return columnDefinition;
}
}
public static List<ColumnDefinition<string>> ColumnsDefinitions { get; } =
[
new ColumnDefinition<string>("Id", l => l),
new ColumnDefinition<string>("Actions", l => l)
];
}
}

View File

@@ -1,22 +1,24 @@
@page "/"
@layout ListLayout
<ContextMenu @ref="ContextMenu" Dense="true" AdjustmentX="@(DrawerOpen ? -235 : 0)">
<MudMenuItem Icon="@Icons.Material.Outlined.Info" IconColor="Color.Inherit" OnClick="ShowTorrentContextMenu">View torrent details</MudMenuItem>
<MudDivider />
<TorrentActions RenderType="RenderType.MenuItems" Hashes="GetContextMenuTargetHashes()" PrimaryHash="@(ContextMenuItem?.Hash)" Torrents="MainData.Torrents" Preferences="Preferences" />
</ContextMenu>
<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="GetSelectedTorrentsHashes()" Torrents="MainData.Torrents" Preferences="Preferences" />
<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.Info" Color="Color.Inherit" Disabled="@(!ToolbarButtonsEnabled)" OnClick="ShowTorrentToolbar" 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>
<ContextMenu @ref="ContextMenu" Dense="true">
<TorrentActions RenderType="RenderType.MenuItems" Hashes="GetContextMenuTargetHashes()" PrimaryHash="@(ContextMenuItem?.Hash)" Torrents="MainData.Torrents" Preferences="Preferences" />
</ContextMenu>
<MudContainer MaxWidth="MaxWidth.ExtraExtraLarge" Class="ma-0 pa-0">
<DynamicTable
@ref="Table"
@@ -26,6 +28,7 @@
Items="Torrents"
OnRowClick="RowClick"
MultiSelection="true"
SelectOnRowClick="true"
SelectedItemsChanged="SelectedItemsChanged"
SortColumnChanged="SortColumnChangedHandler"
SortDirectionChanged="SortDirectionChangedHandler"

View File

@@ -1,7 +1,7 @@
using Lantean.QBitTorrentClient;
using Lantean.QBTMudBlade.Components;
using Lantean.QBTMudBlade.Interop;
using Lantean.QBTMudBlade.Components.UI;
using Lantean.QBTMudBlade.Models;
using Lantean.QBTMudBlade.Services;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;
@@ -9,8 +9,14 @@ using MudBlazor;
namespace Lantean.QBTMudBlade.Pages
{
public partial class TorrentList
public partial class TorrentList : IAsyncDisposable
{
private bool _disposedValue;
private static KeyboardEvent _addTorrentFileKey = new KeyboardEvent("a") { AltKey = true };
private static KeyboardEvent _addTorrentLinkKey = new KeyboardEvent("l") { AltKey = true };
[Inject]
protected IApiClient ApiClient { get; set; } = default!;
@@ -23,6 +29,9 @@ namespace Lantean.QBTMudBlade.Pages
[Inject]
protected IJSRuntime JSRuntime { get; set; } = default!;
[Inject]
protected IKeyboardService KeyboardService { get; set; } = default!;
[CascadingParameter]
public QBitTorrentClient.Models.Preferences? Preferences { get; set; }
@@ -41,6 +50,9 @@ namespace Lantean.QBTMudBlade.Pages
[CascadingParameter(Name = "SortDirectionChanged")]
public EventCallback<SortDirection> SortDirectionChanged { get; set; }
[CascadingParameter(Name = "DrawerOpen")]
public bool DrawerOpen { get; set; }
protected string? SearchText { get; set; }
protected HashSet<Torrent> SelectedItems { get; set; } = [];
@@ -53,6 +65,15 @@ namespace Lantean.QBTMudBlade.Pages
protected ContextMenu? ContextMenu { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await KeyboardService.RegisterKeypressEvent(_addTorrentFileKey, k => AddTorrentFile());
await KeyboardService.RegisterKeypressEvent(_addTorrentLinkKey, k => AddTorrentLink());
}
}
protected void SelectedItemsChanged(HashSet<Torrent> selectedItems)
{
SelectedItems = selectedItems;
@@ -122,9 +143,20 @@ namespace Lantean.QBTMudBlade.Pages
await Table.ShowColumnOptionsDialog();
}
protected void ShowTorrent()
protected void ShowTorrentToolbar()
{
var torrent = SelectedItems.FirstOrDefault();
NavigateToTorrent(torrent);
}
protected void ShowTorrentContextMenu()
{
NavigateToTorrent(ContextMenuItem);
}
protected void NavigateToTorrent(Torrent? torrent)
{
if (torrent is null)
{
return;
@@ -162,11 +194,11 @@ namespace Lantean.QBTMudBlade.Pages
public static List<ColumnDefinition<Torrent>> ColumnsDefinitions { get; } =
[
CreateColumnDefinition("#", t => t.Priority),
CreateColumnDefinition("Icon", t => t.State, IconColumn, iconOnly: true),
CreateColumnDefinition("Icon", t => t.State, IconColumn, iconOnly: true, width: 25),
CreateColumnDefinition("Name", t => t.Name, width: 400),
CreateColumnDefinition("Size", t => t.Size, t => DisplayHelpers.Size(t.Size)),
CreateColumnDefinition("Total Size", t => t.TotalSize, t => DisplayHelpers.Size(t.TotalSize), enabled: false),
CreateColumnDefinition("Done", t => t.Progress, ProgressBarColumn, tdClass: "table-progress pl-1 pr-1"),
CreateColumnDefinition("Done", t => t.Progress, ProgressBarColumn, tdClass: "table-progress"),
CreateColumnDefinition("Status", t => t.State, t => DisplayHelpers.State(t.State)),
CreateColumnDefinition("Seeds", t => t.NumberSeeds),
CreateColumnDefinition("Peers", t => t.NumberLeeches),
@@ -199,7 +231,7 @@ namespace Lantean.QBTMudBlade.Pages
private static ColumnDefinition<Torrent> CreateColumnDefinition(string name, Func<Torrent, object?> selector, RenderFragment<RowContext<Torrent>> rowTemplate, int? width = null, string? tdClass = null, bool enabled = true, bool iconOnly = false)
{
var cd = new ColumnDefinition<Torrent>(name, selector, rowTemplate);
cd.Class = "no-wrap";
cd.Class = iconOnly ? "icon-cell" : "no-wrap";
if (tdClass is not null)
{
cd.Class += " " + tdClass;
@@ -214,7 +246,7 @@ namespace Lantean.QBTMudBlade.Pages
private static ColumnDefinition<Torrent> CreateColumnDefinition(string name, Func<Torrent, object?> selector, Func<Torrent, string>? formatter = null, int? width = null, string? tdClass = null, bool enabled = true, bool iconOnly = false)
{
var cd = new ColumnDefinition<Torrent>(name, selector, formatter);
cd.Class = "no-wrap";
cd.Class = iconOnly ? "icon-cell" : "no-wrap";
if (tdClass is not null)
{
cd.Class += " " + tdClass;
@@ -225,5 +257,26 @@ namespace Lantean.QBTMudBlade.Pages
return cd;
}
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)
{
await KeyboardService.UnregisterKeypressEvent(_addTorrentFileKey);
await KeyboardService.UnregisterKeypressEvent(_addTorrentLinkKey);
}
_disposedValue = true;
}
}
}
}

View File

@@ -42,6 +42,7 @@ namespace Lantean.QBTMudBlade
builder.Services.AddSingleton<IDataManager, DataManager>();
builder.Services.AddBlazoredLocalStorage();
builder.Services.AddSingleton<IClipboardService, ClipboardService>();
builder.Services.AddSingleton<IKeyboardService, KeyboardService>();
#if DEBUG
builder.Logging.SetMinimumLevel(LogLevel.Information);

View File

@@ -0,0 +1,11 @@
using Lantean.QBTMudBlade.Models;
namespace Lantean.QBTMudBlade.Services
{
public interface IKeyboardService
{
Task RegisterKeypressEvent(KeyboardEvent criteria, Func<KeyboardEvent, Task> onKeyPress);
Task UnregisterKeypressEvent(KeyboardEvent criteria);
}
}

View File

@@ -0,0 +1,50 @@
using Lantean.QBTMudBlade.Models;
using Microsoft.JSInterop;
using System.Collections.Concurrent;
namespace Lantean.QBTMudBlade.Services
{
public class KeyboardService : IKeyboardService
{
private readonly IJSRuntime _jSRuntime;
private DotNetObjectReference<KeyboardService>? _dotNetObjectReference;
private readonly ConcurrentDictionary<string, Func<KeyboardEvent, Task>> _keyboardHandlers = new();
public KeyboardService(IJSRuntime jSRuntime)
{
_jSRuntime = jSRuntime;
}
public async Task RegisterKeypressEvent(KeyboardEvent criteria, Func<KeyboardEvent, Task> onKeyPress)
{
await _jSRuntime.InvokeVoidAsync("qbt.registerKeypressEvent", criteria, GetObjectReference());
_keyboardHandlers.TryAdd(criteria, onKeyPress);
}
private DotNetObjectReference<KeyboardService> GetObjectReference()
{
_dotNetObjectReference ??= DotNetObjectReference.Create(this);
return _dotNetObjectReference;
}
[JSInvokable]
public async Task HandleKeyPressEvent(KeyboardEvent keyboardEvent)
{
if (!_keyboardHandlers.TryGetValue(keyboardEvent, out var handler))
{
return;
}
await handler(keyboardEvent);
}
public async Task UnregisterKeypressEvent(KeyboardEvent criteria)
{
await _jSRuntime.InvokeVoidAsync("qbt.unregisterKeypressEvent", criteria, GetObjectReference());
_keyboardHandlers.Remove(criteria, out var _);
}
}
}

View File

@@ -12,4 +12,5 @@
@using Lantean.QBTMudBlade.Models
@using Lantean.QBTMudBlade.Components
@using Lantean.QBTMudBlade.Components.Dialogs
@using Lantean.QBTMudBlade.Components.Options
@using Lantean.QBTMudBlade.Components.Options
@using Lantean.QBTMudBlade.Components.UI

View File

@@ -5,10 +5,10 @@
- Rename multiple files dialog
- RSS feeds and dialogs
- About
- Context menu for files list/trackers list/peers list
- Tag management page
- Category management page
- Update all tables to use DynamicTable
- Log
- Blocks
- Search
- ~~Context menu for files list/trackers list/peers list~~
- ~~Tag management page~~
- ~~Category management page~~
- ~~Update all tables to use DynamicTable~~
- ~~Log~~
- ~~Blocks~~
- ~~Search~~

View File

@@ -81,8 +81,7 @@ code {
}
.table-progress {
padding-top: 0 !important;
padding-bottom: 0 !important;
padding: 0 !important;
}
.table-progress .progress-expand {
@@ -211,4 +210,12 @@ tr.log-critical td {
.filter-menu-item .mud-nav-link {
padding-left: 0px !important;
padding-inline-start: 10px !important;
}
td.icon-cell {
padding: 2px 0 0 7px !important;
}
td .folder-button {
padding: 6px 16px 6px 16px !important;
}

View File

@@ -102,6 +102,66 @@ window.qbt.clearSelection = () => {
}
}
let supportedEvents = new Map();
document.addEventListener('keyup', event => {
const key = getKey(event);
console.log(key);
console.log(event);
const references = supportedEvents.get(key);
if (!references) {
return;
}
console.log(references);
references.forEach(dotNetObjectReference => {
dotNetObjectReference.invokeMethodAsync('HandleKeyPressEvent', {
key: event.key,
code: event.code,
altKey: event.altKey,
ctrlKey: event.ctrlKey,
metaKey: event.metaKey,
shiftKey: event.shiftKey,
}).catch(error => {
console.error("Error handling key press:", error);
});
});
});
window.qbt.registerKeypressEvent = (keyboardEventArgs, dotNetObjectReference) => {
const key = getKey(keyboardEventArgs);
const references = supportedEvents.get(key);
if (references) {
references.set(dotNetObjectReference._id, dotNetObjectReference);
}
else {
const references = new Map();
references.set(dotNetObjectReference._id, dotNetObjectReference);
supportedEvents.set(key, references);
}
}
window.qbt.unregisterKeypressEvent = (keyboardEventArgs, dotNetObjectReference) => {
const key = getKey(keyboardEventArgs);
const references = supportedEvents.get(key);
if (!references) {
return;
}
references.delete(dotNetObjectReference._id);
}
function getKey(keyboardEvent) {
return keyboardEvent.key + (keyboardEvent.ctrlKey ? '1' : '0') + (keyboardEvent.shiftKey ? '1' : '0') + (keyboardEvent.altKey ? '1' : '0') + (keyboardEvent.metaKey ? '1' : '0') + (keyboardEvent.repeat ? '1' : '0');
}
function fallbackCopyTextToClipboard(text) {
const textArea = document.createElement("textarea");
textArea.value = text;

View File

@@ -1,442 +0,0 @@
using Lantean.QBitTorrentClient.Models;
namespace Lantean.QBitTorrentClient
{
public class MockApiClient : IApiClient
{
private readonly ApiClient _apiClient;
public MockApiClient(ApiClient apiClient)
{
_apiClient = apiClient;
}
public Task AddCategory(string category, string savePath)
{
return _apiClient.AddCategory(category, savePath);
}
public Task AddPeers(IEnumerable<string> hashes, IEnumerable<PeerId> peers)
{
return _apiClient.AddPeers(hashes, peers);
}
public Task AddTorrent(IEnumerable<string>? urls = null, Dictionary<string, Stream>? torrents = null, string? savePath = null, string? cookie = null, string? category = null, IEnumerable<string>? tags = null, bool? skipChecking = null, bool? paused = null, string? contentLayout = null, string? renameTorrent = null, long? uploadLimit = null, long? downloadLimit = null, float? ratioLimit = null, int? seedingTimeLimit = null, bool? autoTorrentManagement = null, bool? sequentialDownload = null, bool? firstLastPiecePriority = null)
{
return _apiClient.AddTorrent(urls, torrents, savePath, cookie, category, tags, skipChecking, paused, contentLayout, renameTorrent, uploadLimit, downloadLimit, ratioLimit, seedingTimeLimit, autoTorrentManagement, sequentialDownload, firstLastPiecePriority);
}
public Task AddTorrentTags(IEnumerable<string> tags, bool? all = null, params string[] hashes)
{
return _apiClient.AddTorrentTags(tags, all, hashes);
}
public Task AddTrackersToTorrent(string hash, IEnumerable<string> urls)
{
return _apiClient.AddTrackersToTorrent(hash, urls);
}
public Task BanPeers(IEnumerable<PeerId> peers)
{
return _apiClient.BanPeers(peers);
}
public Task<bool> CheckAuthState()
{
return _apiClient.CheckAuthState();
}
public Task CreateTags(IEnumerable<string> tags)
{
return _apiClient.CreateTags(tags);
}
public Task DecreaseTorrentPriority(bool? all = null, params string[] hashes)
{
return _apiClient.DecreaseTorrentPriority(all, hashes);
}
public Task DeleteTags(params string[] tags)
{
return _apiClient.DeleteTags(tags);
}
public Task DeleteTorrents(bool? all = null, bool deleteFiles = false, params string[] hashes)
{
return _apiClient.DeleteTorrents(all, deleteFiles, hashes);
}
public Task EditCategory(string category, string savePath)
{
return _apiClient.EditCategory(category, savePath);
}
public Task EditTracker(string hash, string originalUrl, string newUrl)
{
return _apiClient.EditTracker(hash, originalUrl, newUrl);
}
public Task<string> GetExportUrl(string hash)
{
return _apiClient.GetExportUrl(hash);
}
public Task<IReadOnlyDictionary<string, Category>> GetAllCategories()
{
return _apiClient.GetAllCategories();
}
public Task<IReadOnlyList<string>> GetAllTags()
{
return _apiClient.GetAllTags();
}
public Task<bool> GetAlternativeSpeedLimitsState()
{
return _apiClient.GetAlternativeSpeedLimitsState();
}
public Task<string> GetAPIVersion()
{
return _apiClient.GetAPIVersion();
}
public Task<Preferences> GetApplicationPreferences()
{
return _apiClient.GetApplicationPreferences();
}
public Task<string> GetApplicationVersion()
{
return _apiClient.GetApplicationVersion();
}
public Task<BuildInfo> GetBuildInfo()
{
return _apiClient.GetBuildInfo();
}
public Task<string> GetDefaultSavePath()
{
return _apiClient.GetDefaultSavePath();
}
public Task<IReadOnlyList<NetworkInterface>> GetNetworkInterfaces()
{
return _apiClient.GetNetworkInterfaces();
}
public Task<IReadOnlyList<string>> GetNetworkInterfaceAddressList(string @interface)
{
return _apiClient.GetNetworkInterfaceAddressList(@interface);
}
public Task<long> GetGlobalDownloadLimit()
{
return _apiClient.GetGlobalDownloadLimit();
}
public Task<GlobalTransferInfo> GetGlobalTransferInfo()
{
return _apiClient.GetGlobalTransferInfo();
}
public Task<long> GetGlobalUploadLimit()
{
return _apiClient.GetGlobalUploadLimit();
}
public Task<IReadOnlyList<Log>> GetLog(bool? normal = null, bool? info = null, bool? warning = null, bool? critical = null, int? lastKnownId = null)
{
return _apiClient.GetLog(normal, info, warning, critical, lastKnownId);
}
public Task<MainData> GetMainData(int requestId)
{
return _apiClient.GetMainData(requestId);
}
public Task<IReadOnlyList<PeerLog>> GetPeerLog(int? lastKnownId = null)
{
return _apiClient.GetPeerLog(lastKnownId);
}
public Task<IReadOnlyList<FileData>> GetTorrentContents(string hash, params int[] indexes)
{
var list = new List<FileData>();
list.Add(new FileData(2, "slackware-14.2-iso/slackware-14.2-source-d6.iso", 500, 0f, Priority.Normal, false, [1, 2], 0f));
list.Add(new FileData(3, "slackware-14.2-iso/slackware-14.2-source-d6.iso.asc", 500, 0f, Priority.Normal, false, [1, 2], 0f));
list.Add(new FileData(4, "slackware-14.2-iso/slackware-14.2-source-d6.iso.md5", 500, 0f, Priority.Normal, false, [1, 2], 0f));
list.Add(new FileData(5, "slackware-14.2-iso/slackware-14.2-source-d6.iso.txt", 500, 0f, Priority.Normal, false, [1, 2], 0f));
list.Add(new FileData(6, "slackware-14.2-iso/temp/slackware-14.2-source-d6.iso.md5", 500, 0f, Priority.Normal, false, [1, 2], 0f));
list.Add(new FileData(7, "slackware-14.2-iso/temp/slackware-14.2-source-d6.iso.txt", 500, 0f, Priority.Normal, false, [1, 2], 0f));
list.Add(new FileData(8, "slackware-14.2-iso2/slackware-14.2-source-d6.iso2", 500, 0f, Priority.Normal, false, [1, 2], 0f));
list.Add(new FileData(9, "slackware-14.2-iso2/slackware-14.2-source-d6.iso2.asc", 500, 0f, Priority.Normal, false, [1, 2], 0f));
list.Add(new FileData(10, "slackware-14.2-iso2/slackware-14.2-source-d6.iso2.md5", 500, 0f, Priority.Normal, false, [1, 2], 0f));
list.Add(new FileData(11, "slackware-14.2-iso2/slackware-14.2-source-d6.iso2.txt", 500, 0f, Priority.Normal, false, [1, 2], 0f));
list.Add(new FileData(12, "really/long/directory/path/is/here/file.txt", 500, 0f, Priority.Normal, false, [1, 2], 0f));
list.Add(new FileData(13, "other.txt", 500, 0f, Priority.Normal, false, [1, 2], 0f));
return Task.FromResult<IReadOnlyList<FileData>>(list);
}
public Task<IReadOnlyDictionary<string, long>> GetTorrentDownloadLimit(bool? all = null, params string[] hashes)
{
return _apiClient.GetTorrentDownloadLimit(all, hashes);
}
public Task<IReadOnlyList<Torrent>> GetTorrentList(string? filter = null, string? category = null, string? tag = null, string? sort = null, bool? reverse = null, int? limit = null, int? offset = null, params string[] hashes)
{
return _apiClient.GetTorrentList(filter, category, tag, sort, reverse, limit, offset, hashes);
}
public Task<TorrentPeers> GetTorrentPeersData(string hash, int requestId)
{
return _apiClient.GetTorrentPeersData(hash, requestId);
}
public Task<IReadOnlyList<string>> GetTorrentPieceHashes(string hash)
{
return _apiClient.GetTorrentPieceHashes(hash);
}
public Task<IReadOnlyList<PieceState>> GetTorrentPieceStates(string hash)
{
return _apiClient.GetTorrentPieceStates(hash);
}
public Task<TorrentProperties> GetTorrentProperties(string hash)
{
return _apiClient.GetTorrentProperties(hash);
}
public Task<IReadOnlyList<TorrentTracker>> GetTorrentTrackers(string hash)
{
return _apiClient.GetTorrentTrackers(hash);
}
public Task<IReadOnlyDictionary<string, long>> GetTorrentUploadLimit(bool? all = null, params string[] hashes)
{
return _apiClient.GetTorrentUploadLimit(all, hashes);
}
public Task<IReadOnlyList<WebSeed>> GetTorrentWebSeeds(string hash)
{
return _apiClient.GetTorrentWebSeeds(hash);
}
public Task IncreaseTorrentPriority(bool? all = null, params string[] hashes)
{
return _apiClient.IncreaseTorrentPriority(all, hashes);
}
public Task Login(string username, string password)
{
return _apiClient.Login(username, password);
}
public Task Logout()
{
return _apiClient.Logout();
}
public Task MaximalTorrentPriority(bool? all = null, params string[] hashes)
{
return _apiClient.MaximalTorrentPriority(all, hashes);
}
public Task MinimalTorrentPriority(bool? all = null, params string[] hashes)
{
return _apiClient.MinimalTorrentPriority(all, hashes);
}
public Task PauseTorrents(bool? all = null, params string[] hashes)
{
return _apiClient.PauseTorrents(all, hashes);
}
public Task ReannounceTorrents(bool? all = null, params string[] hashes)
{
return _apiClient.ReannounceTorrents(all, hashes);
}
public Task RecheckTorrents(bool? all = null, params string[] hashes)
{
return _apiClient.ReannounceTorrents(all, hashes);
}
public Task RemoveCategories(params string[] categories)
{
return _apiClient.RemoveCategories(categories);
}
public Task RemoveTorrentTags(IEnumerable<string> tags, bool? all = null, params string[] hashes)
{
return _apiClient.RemoveTorrentTags(tags, all, hashes);
}
public Task RemoveTrackers(string hash, IEnumerable<string> urls)
{
return _apiClient.RemoveTrackers(hash, urls);
}
public Task RenameFile(string hash, string oldPath, string newPath)
{
return _apiClient.RenameFile(hash, oldPath, newPath);
}
public Task RenameFolder(string hash, string oldPath, string newPath)
{
return _apiClient.RenameFolder(hash, oldPath, newPath);
}
public Task ResumeTorrents(bool? all = null, params string[] hashes)
{
return _apiClient.ResumeTorrents(all, hashes);
}
public Task SetApplicationPreferences(UpdatePreferences preferences)
{
return _apiClient.SetApplicationPreferences(preferences);
}
public Task SetAutomaticTorrentManagement(bool enable, bool? all = null, params string[] hashes)
{
return _apiClient.SetAutomaticTorrentManagement(enable, all, hashes);
}
public Task SetFilePriority(string hash, IEnumerable<int> id, Priority priority)
{
return _apiClient.SetFilePriority(hash, id, priority);
}
public Task SetFirstLastPiecePriority(bool? all = null, params string[] hashes)
{
return _apiClient.SetFirstLastPiecePriority(all, hashes);
}
public Task SetForceStart(bool value, bool? all = null, params string[] hashes)
{
return _apiClient.SetForceStart(value, all, hashes);
}
public Task SetGlobalDownloadLimit(long limit)
{
return _apiClient.SetGlobalDownloadLimit(limit);
}
public Task SetGlobalUploadLimit(long limit)
{
return _apiClient.SetGlobalDownloadLimit(limit);
}
public Task SetSuperSeeding(bool value, bool? all = null, params string[] hashes)
{
return _apiClient.SetSuperSeeding(value, all, hashes);
}
public Task SetTorrentCategory(string category, bool? all = null, params string[] hashes)
{
return _apiClient.SetTorrentCategory(category, all, hashes);
}
public Task SetTorrentDownloadLimit(long limit, bool? all = null, params string[] hashes)
{
return _apiClient.SetTorrentDownloadLimit(limit, all, hashes);
}
public Task SetTorrentLocation(string location, bool? all = null, params string[] hashes)
{
return _apiClient.SetTorrentLocation(location, all, hashes);
}
public Task SetTorrentName(string name, string hash)
{
return _apiClient.SetTorrentName(name, hash);
}
public Task SetTorrentShareLimit(float ratioLimit, float seedingTimeLimit, bool? all = null, params string[] hashes)
{
return _apiClient.SetTorrentShareLimit(ratioLimit, seedingTimeLimit, all, hashes);
}
public Task SetTorrentUploadLimit(long limit, bool? all = null, params string[] hashes)
{
return _apiClient.SetTorrentUploadLimit(limit, all, hashes);
}
public Task Shutdown()
{
return _apiClient.Shutdown();
}
public Task ToggleAlternativeSpeedLimits()
{
return _apiClient.ToggleAlternativeSpeedLimits();
}
public Task ToggleSequentialDownload(bool? all = null, params string[] hashes)
{
return _apiClient.ToggleSequentialDownload(all, hashes);
}
public Task<int> StartSearch(string pattern, IEnumerable<string> plugins, string category = "all")
{
return _apiClient.StartSearch(pattern, plugins, category);
}
public Task StopSearch(int id)
{
return _apiClient.StopSearch(id);
}
public Task<SearchStatus?> GetSearchStatus(int id)
{
return _apiClient.GetSearchStatus(id);
}
public Task<IReadOnlyList<SearchStatus>> GetSearchesStatus()
{
return _apiClient.GetSearchesStatus();
}
public Task<SearchResults> GetSearchResults(int id, int? limit = null, int? offset = null)
{
return _apiClient.GetSearchResults(id, limit, offset);
}
public Task DeleteSearch(int id)
{
return _apiClient.DeleteSearch(id);
}
public Task<IReadOnlyList<SearchPlugin>> GetSearchPlugins()
{
return _apiClient.GetSearchPlugins();
}
public Task InstallSearchPlugins(params string[] sources)
{
return _apiClient.InstallSearchPlugins(sources);
}
public Task UninstallSearchPlugins(params string[] names)
{
return _apiClient.UninstallSearchPlugins(names);
}
public Task EnableSearchPlugins(params string[] names)
{
return _apiClient.EnableSearchPlugins(names);
}
public Task DisableSearchPlugins(params string[] names)
{
return _apiClient.DisableSearchPlugins(names);
}
public Task UpdateSearchPlugins()
{
return _apiClient.UpdateSearchPlugins();
}
}
}