mirror of
https://github.com/lantean-code/qbtmud.git
synced 2025-11-02 04:53:19 +00:00
Update tag and category management and improve multiselection for DynamicTable
This commit is contained in:
@@ -2,10 +2,10 @@
|
||||
<DialogContent>
|
||||
<MudGrid>
|
||||
<MudItem xs="12">
|
||||
<MudTextField Label="Category" @bind-Value="Category" />
|
||||
<MudTextField Label="Category" @bind-Value="Category" Required ShrinkLabel Variant="Variant.Outlined" />
|
||||
</MudItem>
|
||||
<MudItem xs="12">
|
||||
<MudTextField Label="Save Path" @bind-Value="SavePath" />
|
||||
<MudTextField Label="Save Path" @bind-Value="SavePath" Required ShrinkLabel Variant="Variant.Outlined" />
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
</DialogContent>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Lantean.QBTMudBlade.Models;
|
||||
using Lantean.QBitTorrentClient;
|
||||
using Lantean.QBTMudBlade.Models;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using MudBlazor;
|
||||
@@ -10,10 +11,19 @@ namespace Lantean.QBTMudBlade.Components.Dialogs
|
||||
[CascadingParameter]
|
||||
public MudDialogInstance MudDialog { get; set; } = default!;
|
||||
|
||||
[Inject]
|
||||
protected IApiClient ApiClient { get; set; } = default!;
|
||||
|
||||
protected string? Category { get; set; }
|
||||
|
||||
protected string SavePath { get; set; } = "";
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var preferences = await ApiClient.GetApplicationPreferences();
|
||||
SavePath = preferences.SavePath;
|
||||
}
|
||||
|
||||
protected void Cancel(MouseEventArgs args)
|
||||
{
|
||||
MudDialog.Cancel();
|
||||
|
||||
24
Lantean.QBTMudBlade/Components/Dialogs/AddTagDialog.razor
Normal file
24
Lantean.QBTMudBlade/Components/Dialogs/AddTagDialog.razor
Normal file
@@ -0,0 +1,24 @@
|
||||
<MudDialog>
|
||||
<DialogContent>
|
||||
<table width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="width: 100%"><MudTextField T="string" Label="Tag" Value="@Tag" ValueChanged="SetTag" ShrinkLabel Required Variant="Variant.Outlined" /></td>
|
||||
<td><MudIconButton Icon="@Icons.Material.Filled.Add" OnClick="AddTag" /></td>
|
||||
</tr>
|
||||
@foreach (var tag in Tags)
|
||||
{
|
||||
var tagRef = tag;
|
||||
<tr>
|
||||
<td>@tag</td>
|
||||
<td><MudIconButton Icon="@Icons.Material.Filled.Delete" /></td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<MudButton OnClick="Cancel">Cancel</MudButton>
|
||||
<MudButton Color="Color.Primary" OnClick="Submit">Save</MudButton>
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
47
Lantean.QBTMudBlade/Components/Dialogs/AddTagDialog.razor.cs
Normal file
47
Lantean.QBTMudBlade/Components/Dialogs/AddTagDialog.razor.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using Lantean.QBitTorrentClient;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MudBlazor;
|
||||
|
||||
namespace Lantean.QBTMudBlade.Components.Dialogs
|
||||
{
|
||||
public partial class AddTagDialog
|
||||
{
|
||||
[Inject]
|
||||
protected IApiClient ApiClient { get; set; } = default!;
|
||||
|
||||
[Inject]
|
||||
protected IDialogService DialogService { get; set; } = default!;
|
||||
|
||||
[CascadingParameter]
|
||||
public MudDialogInstance MudDialog { get; set; } = default!;
|
||||
|
||||
protected HashSet<string> Tags { get; } = [];
|
||||
|
||||
protected string? Tag { get; set; }
|
||||
|
||||
protected void AddTag()
|
||||
{
|
||||
if (string.IsNullOrEmpty(Tag))
|
||||
{
|
||||
return;
|
||||
}
|
||||
Tags.Add(Tag);
|
||||
Tag = null;
|
||||
}
|
||||
|
||||
protected void SetTag(string tag)
|
||||
{
|
||||
Tag = tag;
|
||||
}
|
||||
|
||||
protected void Cancel()
|
||||
{
|
||||
MudDialog.Cancel();
|
||||
}
|
||||
|
||||
protected void Submit()
|
||||
{
|
||||
MudDialog.Close(Tags);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<MudDialog>
|
||||
<DialogContent>
|
||||
<MudGrid>
|
||||
<MudItem xs="12">
|
||||
<MudList Clickable="true">
|
||||
<MudListItem Icon="@Icons.Material.Filled.Add" IconColor="Color.Info" OnClick="AddCategory">Add</MudListItem>
|
||||
<MudListItem Icon="@Icons.Material.Filled.Remove" IconColor="Color.Error" OnClick="RemoveCategory">Remove</MudListItem>
|
||||
<MudDivider />
|
||||
@foreach (var category in Categories)
|
||||
{
|
||||
var categoryRef = category;
|
||||
<MudListItem Icon="@GetIcon(categoryRef)" IconColor="Color.Default" OnClick="@(e => SetCategory(categoryRef))">@categoryRef</MudListItem>
|
||||
}
|
||||
</MudList>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
@@ -0,0 +1,137 @@
|
||||
using Lantean.QBitTorrentClient;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MudBlazor;
|
||||
|
||||
namespace Lantean.QBTMudBlade.Components.Dialogs
|
||||
{
|
||||
public partial class ManageCategoriesDialog
|
||||
{
|
||||
[Inject]
|
||||
protected IApiClient ApiClient { get; set; } = default!;
|
||||
|
||||
[Inject]
|
||||
protected IDialogService DialogService { get; set; } = default!;
|
||||
|
||||
[CascadingParameter]
|
||||
public MudDialogInstance MudDialog { get; set; } = default!;
|
||||
|
||||
[Parameter]
|
||||
public IEnumerable<string> Hashes { get; set; } = [];
|
||||
|
||||
protected HashSet<string> Categories { get; set; } = [];
|
||||
|
||||
protected IList<string> TorrentCategories { get; private set; } = [];
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
Categories = [.. (await ApiClient.GetAllCategories()).Select(c => c.Key)];
|
||||
if (!Hashes.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await GetTorrentCategories();
|
||||
}
|
||||
|
||||
private async Task GetTorrentCategories()
|
||||
{
|
||||
var torrents = await ApiClient.GetTorrentList(hashes: Hashes.ToArray());
|
||||
TorrentCategories = torrents.Where(t => !string.IsNullOrEmpty(t.Category)).Select(t => t.Category!).ToList();
|
||||
}
|
||||
|
||||
protected string GetIcon(string tag)
|
||||
{
|
||||
var state = GetCategoryState(tag);
|
||||
return state switch
|
||||
{
|
||||
CategoryState.All => Icons.Material.Filled.RadioButtonChecked,
|
||||
CategoryState.Partial => CustomIcons.RadioIndeterminate,
|
||||
_ => Icons.Material.Filled.RadioButtonUnchecked
|
||||
};
|
||||
}
|
||||
|
||||
private enum CategoryState
|
||||
{
|
||||
All,
|
||||
Partial,
|
||||
None,
|
||||
}
|
||||
|
||||
private CategoryState GetCategoryState(string category)
|
||||
{
|
||||
if (category == string.Empty || TorrentCategories.Count == 0)
|
||||
{
|
||||
return CategoryState.None;
|
||||
}
|
||||
if (TorrentCategories.All(c => c == category))
|
||||
{
|
||||
return CategoryState.All;
|
||||
}
|
||||
else if (TorrentCategories.Any(c => c == category))
|
||||
{
|
||||
return CategoryState.Partial;
|
||||
}
|
||||
else
|
||||
{
|
||||
return CategoryState.None;
|
||||
}
|
||||
}
|
||||
|
||||
protected async Task SetCategory(string category)
|
||||
{
|
||||
var state = GetCategoryState(category);
|
||||
|
||||
var nextState = state switch
|
||||
{
|
||||
CategoryState.All => CategoryState.None,
|
||||
CategoryState.Partial => CategoryState.All,
|
||||
CategoryState.None => CategoryState.All,
|
||||
_ => CategoryState.None,
|
||||
};
|
||||
|
||||
if (nextState == CategoryState.All)
|
||||
{
|
||||
await ApiClient.SetTorrentCategory(category, Hashes);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ApiClient.RemoveTorrentCategory(Hashes);
|
||||
}
|
||||
|
||||
await GetTorrentCategories();
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
protected async Task AddCategory()
|
||||
{
|
||||
var addedCategoy = await DialogService.ShowAddCategoryDialog(ApiClient);
|
||||
if (addedCategoy is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await ApiClient.SetTorrentCategory(addedCategoy, Hashes);
|
||||
Categories.Add(addedCategoy);
|
||||
await GetTorrentCategories();
|
||||
}
|
||||
|
||||
protected async Task RemoveCategory()
|
||||
{
|
||||
await ApiClient.RemoveTorrentCategory(Hashes);
|
||||
await GetTorrentCategories();
|
||||
}
|
||||
|
||||
protected Task CloseDialog()
|
||||
{
|
||||
MudDialog.Close();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
protected void Cancel()
|
||||
{
|
||||
MudDialog.Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<MudDialog>
|
||||
<DialogContent>
|
||||
<MudGrid>
|
||||
<MudItem xs="12">
|
||||
<MudList Clickable="true">
|
||||
<MudListItem Icon="@Icons.Material.Filled.Add" IconColor="Color.Info" OnClick="AddTag">Add</MudListItem>
|
||||
<MudListItem Icon="@Icons.Material.Filled.Remove" IconColor="Color.Error" OnClick="RemoveAllTags">Remove All</MudListItem>
|
||||
<MudDivider />
|
||||
@foreach (var tag in Tags)
|
||||
{
|
||||
var tagRef = tag;
|
||||
<MudListItem Icon="@GetIcon(tagRef)" IconColor="Color.Default" OnClick="@(e => SetTag(tagRef))">@tag</MudListItem>
|
||||
}
|
||||
</MudList>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
139
Lantean.QBTMudBlade/Components/Dialogs/ManageTagsDialog.razor.cs
Normal file
139
Lantean.QBTMudBlade/Components/Dialogs/ManageTagsDialog.razor.cs
Normal file
@@ -0,0 +1,139 @@
|
||||
using Lantean.QBitTorrentClient;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MudBlazor;
|
||||
using static MudBlazor.CategoryTypes;
|
||||
|
||||
namespace Lantean.QBTMudBlade.Components.Dialogs
|
||||
{
|
||||
public partial class ManageTagsDialog
|
||||
{
|
||||
[Inject]
|
||||
protected IApiClient ApiClient { get; set; } = default!;
|
||||
|
||||
[Inject]
|
||||
protected IDialogService DialogService { get; set; } = default!;
|
||||
|
||||
[CascadingParameter]
|
||||
public MudDialogInstance MudDialog { get; set; } = default!;
|
||||
|
||||
[Parameter]
|
||||
public IEnumerable<string> Hashes { get; set; } = [];
|
||||
|
||||
protected HashSet<string> Tags { get; set; } = [];
|
||||
|
||||
protected IList<IReadOnlyList<string>> TorrentTags { get; private set; } = [];
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
Tags = [.. (await ApiClient.GetAllTags())];
|
||||
if (!Hashes.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await GetTorrentTags();
|
||||
}
|
||||
|
||||
private async Task GetTorrentTags()
|
||||
{
|
||||
var torrents = await ApiClient.GetTorrentList(hashes: Hashes.ToArray());
|
||||
TorrentTags = torrents.Select(t => t.Tags ?? []).ToList();
|
||||
}
|
||||
|
||||
protected string GetIcon(string tag)
|
||||
{
|
||||
var state = GetTagState(tag);
|
||||
return state switch
|
||||
{
|
||||
TagState.All => Icons.Material.Filled.CheckBox,
|
||||
TagState.Partial => Icons.Material.Filled.IndeterminateCheckBox,
|
||||
_ => Icons.Material.Filled.CheckBoxOutlineBlank
|
||||
};
|
||||
}
|
||||
|
||||
private enum TagState
|
||||
{
|
||||
All,
|
||||
Partial,
|
||||
None,
|
||||
}
|
||||
|
||||
private TagState GetTagState(string tag)
|
||||
{
|
||||
if (TorrentTags.All(t => t.Contains(tag)))
|
||||
{
|
||||
return TagState.All;
|
||||
}
|
||||
else if (TorrentTags.Any(t => t.Contains(tag)))
|
||||
{
|
||||
return TagState.Partial;
|
||||
}
|
||||
else
|
||||
{
|
||||
return TagState.None;
|
||||
}
|
||||
}
|
||||
|
||||
protected async Task SetTag(string tag)
|
||||
{
|
||||
var state = GetTagState(tag);
|
||||
|
||||
var nextState = state switch
|
||||
{
|
||||
TagState.All => TagState.None,
|
||||
TagState.Partial => TagState.All,
|
||||
TagState.None => TagState.All,
|
||||
_ => TagState.None,
|
||||
};
|
||||
|
||||
if (nextState == TagState.All)
|
||||
{
|
||||
await ApiClient.AddTorrentTag(tag, Hashes);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ApiClient.RemoveTorrentTag(tag, Hashes);
|
||||
}
|
||||
|
||||
await GetTorrentTags();
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
protected async Task AddTag()
|
||||
{
|
||||
var addedTags = await DialogService.ShowAddTagsDialog(ApiClient);
|
||||
|
||||
if (addedTags is null || addedTags.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await ApiClient.AddTorrentTags(addedTags, Hashes);
|
||||
|
||||
foreach (var tag in addedTags)
|
||||
{
|
||||
Tags.Add(tag);
|
||||
}
|
||||
await GetTorrentTags();
|
||||
}
|
||||
|
||||
protected async Task RemoveAllTags()
|
||||
{
|
||||
await ApiClient.RemoveTorrentTags(Tags, Hashes);
|
||||
await GetTorrentTags();
|
||||
}
|
||||
|
||||
protected Task CloseDialog()
|
||||
{
|
||||
MudDialog.Close();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
protected void Cancel()
|
||||
{
|
||||
MudDialog.Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,19 @@
|
||||
<MudDialog ContentStyle="mix-width: 400px">
|
||||
<DialogContent>
|
||||
<MudGrid>
|
||||
<MudItem xl="12">
|
||||
<TorrentActions Hashes="Hashes" ParentAction="ParentAction" RenderType="RenderType.Children" AfterAction="CloseDialog" />
|
||||
<MudItem xs="12">
|
||||
<CascadingValue Value="MainData">
|
||||
<CascadingValue Value="Preferences">
|
||||
<TorrentActions Hashes="Hashes" ParentAction="ParentAction" RenderType="RenderType.Children" AfterAction="CloseDialog" />
|
||||
</CascadingValue>
|
||||
</CascadingValue>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
@if (MultiAction)
|
||||
{
|
||||
<MudButton OnClick="Cancel">Close</MudButton>
|
||||
}
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Lantean.QBTMudBlade.Models;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MudBlazor;
|
||||
|
||||
namespace Lantean.QBTMudBlade.Components.Dialogs
|
||||
@@ -11,6 +12,14 @@ namespace Lantean.QBTMudBlade.Components.Dialogs
|
||||
[Parameter]
|
||||
public TorrentAction? ParentAction { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public MainData MainData { get; set; } = default!;
|
||||
|
||||
[Parameter]
|
||||
public QBitTorrentClient.Models.Preferences? Preferences { get; set; }
|
||||
|
||||
protected bool MultiAction => ParentAction?.MultiAction ?? false;
|
||||
|
||||
[Parameter]
|
||||
public IEnumerable<string> Hashes { get; set; } = [];
|
||||
|
||||
|
||||
@@ -37,14 +37,15 @@
|
||||
@foreach (var column in GetColumns())
|
||||
{
|
||||
var className = column.IconOnly ? null : "overflow-cell";
|
||||
var columnHeader = column.IconOnly ? "" : column.Header;
|
||||
<MudTh Class="@className" Style="@(GetColumnStyle(column))">
|
||||
@if (column.SortSelector is not null)
|
||||
{
|
||||
<SortLabel SortDirectionChanged="@(c => SetSort(column.Id, c))" SortDirection="@(column.Id == _sortColumn ? _sortDirection : SortDirection.None)">@column.Header</SortLabel>
|
||||
<SortLabel SortDirectionChanged="@(c => SetSort(column.Id, c))" SortDirection="@(column.Id == _sortColumn ? _sortDirection : SortDirection.None)">@columnHeader</SortLabel>
|
||||
}
|
||||
else
|
||||
{
|
||||
@column.Header
|
||||
@columnHeader
|
||||
}
|
||||
</MudTh>
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using Lantean.QBTMudBlade.Models;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MudBlazor;
|
||||
using System.Data.Common;
|
||||
using static MudBlazor.CategoryTypes;
|
||||
|
||||
namespace Lantean.QBTMudBlade.Components
|
||||
{
|
||||
@@ -55,8 +56,8 @@ namespace Lantean.QBTMudBlade.Components
|
||||
[Parameter]
|
||||
public EventCallback<T> SelectedItemChanged { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public T? SelectedItem { get; set; }
|
||||
//[Parameter]
|
||||
//public T? SelectedItem { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<ColumnDefinition<T>, bool> ColumnFilter { get; set; } = t => true;
|
||||
@@ -193,16 +194,51 @@ namespace Lantean.QBTMudBlade.Components
|
||||
|
||||
protected async Task OnRowClickInternal(TableRowClickEventArgs<T> eventArgs)
|
||||
{
|
||||
SelectedItem = eventArgs.Item;
|
||||
if (MultiSelection)
|
||||
{
|
||||
if (eventArgs.MouseEventArgs.CtrlKey)
|
||||
{
|
||||
if (SelectedItems.Contains(eventArgs.Item))
|
||||
{
|
||||
SelectedItems.Remove(eventArgs.Item);
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedItems.Add(eventArgs.Item);
|
||||
}
|
||||
}
|
||||
else if (eventArgs.MouseEventArgs.AltKey)
|
||||
{
|
||||
SelectedItems.Clear();
|
||||
SelectedItems.Add(eventArgs.Item);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!SelectedItems.Contains(eventArgs.Item))
|
||||
{
|
||||
SelectedItems.Clear();
|
||||
SelectedItems.Add(eventArgs.Item);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!SelectedItems.Contains(eventArgs.Item))
|
||||
{
|
||||
SelectedItems.Clear();
|
||||
SelectedItems.Add(eventArgs.Item);
|
||||
await SelectedItemChanged.InvokeAsync(eventArgs.Item);
|
||||
}
|
||||
}
|
||||
|
||||
await SelectedItemChanged.InvokeAsync(SelectedItem);
|
||||
await OnRowClick.InvokeAsync(eventArgs);
|
||||
}
|
||||
|
||||
protected string RowStyleFuncInternal(T item, int index)
|
||||
{
|
||||
var style = "user-select: none; cursor: pointer;";
|
||||
if (EqualityComparer<T>.Default.Equals(item, SelectedItem))
|
||||
//EqualityComparer<T>.Default.Equals(item, SelectedItem) ||
|
||||
if (SelectedItems.Contains(item))
|
||||
{
|
||||
style += " background-color: var(--mud-palette-grey-dark); color: var(--mud-palette-grey-light) !important;";
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
Dense="true"
|
||||
Breakpoint="Breakpoint.None"
|
||||
Bordered="true"
|
||||
Striped="true"
|
||||
Square="true"
|
||||
LoadingProgressColor="Color.Info"
|
||||
HorizontalScrollbar="true"
|
||||
|
||||
@@ -42,7 +42,14 @@ else if (RenderType == RenderType.Children)
|
||||
<MudList Clickable="true">
|
||||
@foreach (var action in parent.Children)
|
||||
{
|
||||
<MudListItem Icon="@action.Icon" Color="action.Color" OnClick="action.Callback" Disabled="Disabled" />
|
||||
@if (action is Divider)
|
||||
{
|
||||
<MudDivider />
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudListItem Icon="@action.Icon" IconColor="action.Color" OnClick="action.Callback" Disabled="Disabled">@action.Name</MudListItem>
|
||||
}
|
||||
}
|
||||
</MudList>
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Lantean.QBitTorrentClient;
|
||||
using Lantean.QBTMudBlade.Components.Dialogs;
|
||||
using Lantean.QBTMudBlade.Interop;
|
||||
using Lantean.QBTMudBlade.Models;
|
||||
using Lantean.QBTMudBlade.Services;
|
||||
@@ -107,39 +108,6 @@ namespace Lantean.QBTMudBlade.Components
|
||||
await ApiClient.SetTorrentCategory(category, null, Hashes.ToArray());
|
||||
}
|
||||
|
||||
protected async Task AddCategory()
|
||||
{
|
||||
await DialogService.InvokeAddCategoryDialog(ApiClient, Hashes);
|
||||
}
|
||||
|
||||
protected async Task ResetCategory()
|
||||
{
|
||||
await ApiClient.SetTorrentCategory("", null, Hashes.ToArray());
|
||||
}
|
||||
|
||||
protected async Task AddTag()
|
||||
{
|
||||
await DialogService.ShowSingleFieldDialog("Add Tags", "Comma-separated tags", "", v => ApiClient.AddTorrentTags(v.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries), null, Hashes.ToArray()));
|
||||
}
|
||||
|
||||
protected async Task RemoveTags()
|
||||
{
|
||||
var torrents = GetTorrents();
|
||||
|
||||
foreach (var torrent in torrents)
|
||||
{
|
||||
await ApiClient.RemoveTorrentTags(torrent.Tags, null, torrent.Hash);
|
||||
}
|
||||
}
|
||||
|
||||
protected async Task ToggleTag(string tag)
|
||||
{
|
||||
var torrents = GetTorrents();
|
||||
|
||||
await ApiClient.RemoveTorrentTag(tag, torrents.Where(t => t.Tags.Contains(tag)).Select(t => t.Hash));
|
||||
await ApiClient.AddTorrentTag(tag, torrents.Where(t => !t.Tags.Contains(tag)).Select(t => t.Hash));
|
||||
}
|
||||
|
||||
protected async Task ToggleAutoTMM()
|
||||
{
|
||||
var torrents = GetTorrents();
|
||||
@@ -231,9 +199,29 @@ namespace Lantean.QBTMudBlade.Components
|
||||
}
|
||||
}
|
||||
|
||||
protected async Task ShowTags()
|
||||
{
|
||||
var parameters = new DialogParameters
|
||||
{
|
||||
{ nameof(ManageTagsDialog.Hashes), Hashes }
|
||||
};
|
||||
|
||||
await DialogService.ShowAsync<ManageTagsDialog>("Manage Torrent Tags", parameters, DialogHelper.FormDialogOptions);
|
||||
}
|
||||
|
||||
protected async Task ShowCategories()
|
||||
{
|
||||
var parameters = new DialogParameters
|
||||
{
|
||||
{ nameof(ManageCategoriesDialog.Hashes), Hashes }
|
||||
};
|
||||
|
||||
await DialogService.ShowAsync<ManageCategoriesDialog>("Manage Torrent Categories", parameters, DialogHelper.FormDialogOptions);
|
||||
}
|
||||
|
||||
protected async Task SubMenuTouch(TorrentAction action)
|
||||
{
|
||||
await DialogService.ShowSubMenu(Hashes, action);
|
||||
await DialogService.ShowSubMenu(Hashes, action, MainData, Preferences);
|
||||
}
|
||||
|
||||
private IEnumerable<Torrent> GetTorrents()
|
||||
@@ -249,12 +237,16 @@ namespace Lantean.QBTMudBlade.Components
|
||||
|
||||
private List<TorrentAction>? _actions;
|
||||
|
||||
private IReadOnlyList<TorrentAction> Actions
|
||||
private IEnumerable<TorrentAction> Actions
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_actions is not null)
|
||||
{
|
||||
if (Preferences?.QueueingEnabled == false)
|
||||
{
|
||||
return _actions.Where(a => a.Name != "Queue");
|
||||
}
|
||||
return _actions;
|
||||
}
|
||||
|
||||
@@ -268,22 +260,6 @@ namespace Lantean.QBTMudBlade.Components
|
||||
}
|
||||
}
|
||||
|
||||
var categories = new List<TorrentAction>
|
||||
{
|
||||
new TorrentAction("New", Icons.Material.Filled.Add, Color.Info, CreateCallback(AddCategory)),
|
||||
new TorrentAction("Reset", Icons.Material.Filled.Remove, Color.Error, CreateCallback(ResetCategory)),
|
||||
new Divider()
|
||||
};
|
||||
categories.AddRange(MainData.Categories.Select(c => new TorrentAction(c.Value.Name, Icons.Material.Filled.List, Color.Info, CreateCallback(() => SetCategory(c.Key)))));
|
||||
|
||||
var tags = new List<TorrentAction>
|
||||
{
|
||||
new TorrentAction("Add", Icons.Material.Filled.Add, Color.Info, CreateCallback(AddTag)),
|
||||
new TorrentAction("Remove All", Icons.Material.Filled.Remove, Color.Error, CreateCallback(RemoveTags)),
|
||||
new Divider()
|
||||
};
|
||||
tags.AddRange(MainData.Tags.Select(t => new TorrentAction(t, (torrent?.Tags.Contains(t) == true) ? Icons.Material.Filled.CheckBox : Icons.Material.Filled.CheckBoxOutlineBlank, Color.Default, CreateCallback(() => ToggleTag(t)))));
|
||||
|
||||
_actions = new List<TorrentAction>
|
||||
{
|
||||
new TorrentAction("Pause", Icons.Material.Filled.Pause, Color.Warning, CreateCallback(Pause)),
|
||||
@@ -293,8 +269,8 @@ namespace Lantean.QBTMudBlade.Components
|
||||
new Divider(),
|
||||
new TorrentAction("Set location", Icons.Material.Filled.MyLocation, Color.Info, CreateCallback(SetLocation)),
|
||||
new TorrentAction("Rename", Icons.Material.Filled.DriveFileRenameOutline, Color.Info, CreateCallback(Rename)),
|
||||
new TorrentAction("Category", Icons.Material.Filled.List, Color.Info, categories, true),
|
||||
new TorrentAction("Tags", Icons.Material.Filled.Label, Color.Info, tags, true),
|
||||
new TorrentAction("Category", Icons.Material.Filled.List, Color.Info, CreateCallback(ShowCategories)),
|
||||
new TorrentAction("Tags", Icons.Material.Filled.Label, Color.Info, CreateCallback(ShowTags)),
|
||||
new TorrentAction("Automatic Torrent Management", Icons.Material.Filled.Check, (torrent?.AutomaticTorrentManagement == true) ? Color.Info : Color.Transparent, CreateCallback(ToggleAutoTMM)),
|
||||
new Divider(),
|
||||
new TorrentAction("Limit upload rate", Icons.Material.Filled.KeyboardDoubleArrowUp, Color.Info, CreateCallback(LimitUploadRate)),
|
||||
@@ -322,18 +298,13 @@ namespace Lantean.QBTMudBlade.Components
|
||||
new TorrentAction("Export", Icons.Material.Filled.SaveAlt, Color.Info, CreateCallback(Export)),
|
||||
};
|
||||
|
||||
if (Preferences?.QueueingEnabled == false)
|
||||
{
|
||||
_actions.RemoveAt(18);
|
||||
}
|
||||
|
||||
return _actions;
|
||||
}
|
||||
}
|
||||
|
||||
private EventCallback CreateCallback(Func<Task> action)
|
||||
private EventCallback CreateCallback(Func<Task> action, bool ignoreAfterAction = false)
|
||||
{
|
||||
if (AfterAction is not null)
|
||||
if (AfterAction is not null && !ignoreAfterAction)
|
||||
{
|
||||
return EventCallback.Factory.Create(this, async () =>
|
||||
{
|
||||
@@ -392,7 +363,7 @@ namespace Lantean.QBTMudBlade.Components
|
||||
Children = [];
|
||||
}
|
||||
|
||||
public TorrentAction(string name, string? icon, Color color, IEnumerable<TorrentAction> children, bool useTextButton = false)
|
||||
public TorrentAction(string name, string? icon, Color color, IEnumerable<TorrentAction> children, bool multiAction = false, bool useTextButton = false)
|
||||
{
|
||||
Name = name;
|
||||
Icon = icon;
|
||||
@@ -413,5 +384,7 @@ namespace Lantean.QBTMudBlade.Components
|
||||
public IEnumerable<TorrentAction> Children { get; }
|
||||
|
||||
public bool UseTextButton { get; }
|
||||
|
||||
public bool MultiAction { get; }
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@
|
||||
Dense="true"
|
||||
Breakpoint="Breakpoint.None"
|
||||
Bordered="true"
|
||||
Striped="true"
|
||||
Square="true"
|
||||
LoadingProgressColor="Color.Info"
|
||||
HorizontalScrollbar="true"
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
Dense="true"
|
||||
Breakpoint="Breakpoint.None"
|
||||
Bordered="true"
|
||||
Striped="true"
|
||||
Square="true"
|
||||
LoadingProgressColor="Color.Info"
|
||||
HorizontalScrollbar="true"
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
{
|
||||
public const string Magnet = @"<path fill=""currentColor"" d=""M3 7v6a9 9 0 0 0 9 9a9 9 0 0 0 9-9V7h-4v6a5 5 0 0 1-5 5a5 5 0 0 1-5-5V7m10-2h4V2h-4M3 5h4V2H3""/>";
|
||||
|
||||
public const string Random = @"<path d=""M21.67 3.955l-2.825-2.202.665-.753 4.478 3.497-4.474 3.503-.665-.753 2.942-2.292h-4.162c-3.547.043-5.202 3.405-6.913 7.023 1.711 3.617 3.366 6.979 6.913 7.022h4.099l-2.883-2.247.665-.753 4.478 3.497-4.474 3.503-.665-.753 2.884-2.247h-4.11c-3.896-.048-5.784-3.369-7.461-6.858-1.687 3.51-3.592 6.842-7.539 6.858h-2.623v-1h2.621c3.6-.014 5.268-3.387 6.988-7.022-1.72-3.636-3.388-7.009-6.988-7.023h-2.621v-1h2.623c3.947.016 5.852 3.348 7.539 6.858 1.677-3.489 3.565-6.81 7.461-6.858h4.047z""/>";
|
||||
public const string Random = @"<path fill=""currentColor"" d=""M21.67 3.955l-2.825-2.202.665-.753 4.478 3.497-4.474 3.503-.665-.753 2.942-2.292h-4.162c-3.547.043-5.202 3.405-6.913 7.023 1.711 3.617 3.366 6.979 6.913 7.022h4.099l-2.883-2.247.665-.753 4.478 3.497-4.474 3.503-.665-.753 2.884-2.247h-4.11c-3.896-.048-5.784-3.369-7.461-6.858-1.687 3.51-3.592 6.842-7.539 6.858h-2.623v-1h2.621c3.6-.014 5.268-3.387 6.988-7.022-1.72-3.636-3.388-7.009-6.988-7.023h-2.621v-1h2.623c3.947.016 5.852 3.348 7.539 6.858 1.677-3.489 3.565-6.81 7.461-6.858h4.047z""/>";
|
||||
|
||||
public const string RadioIndeterminate = @"<path fill=""currentColor"" d=""M0 0h24v24H0z""/><path d=""M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zm-5-9h10v2H7v-2z""/>";
|
||||
}
|
||||
}
|
||||
@@ -4,14 +4,19 @@ using Lantean.QBTMudBlade.Components.Dialogs;
|
||||
using Lantean.QBTMudBlade.Filter;
|
||||
using Lantean.QBTMudBlade.Models;
|
||||
using MudBlazor;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Lantean.QBTMudBlade
|
||||
{
|
||||
public static class DialogHelper
|
||||
{
|
||||
public static readonly DialogOptions FormDialogOptions = new() { CloseButton = true, MaxWidth = MaxWidth.Medium, ClassBackground = "background-blur" };
|
||||
public static readonly DialogOptions FormDialogOptions = new() { CloseButton = true, MaxWidth = MaxWidth.Medium, ClassBackground = "background-blur", FullWidth = true };
|
||||
|
||||
private static readonly DialogOptions _confirmDialogOptions = new() { ClassBackground = "background-blur" };
|
||||
public static readonly DialogOptions NonBlurFormDialogOptions = new() { CloseButton = true, MaxWidth = MaxWidth.Medium, FullWidth = true };
|
||||
|
||||
public static readonly DialogOptions ConfirmDialogOptions = new() { ClassBackground = "background-blur", MaxWidth = MaxWidth.Small, FullWidth = true };
|
||||
|
||||
public static readonly DialogOptions NonBlurConfirmDialogOptions = new() { MaxWidth = MaxWidth.Small, FullWidth = true };
|
||||
|
||||
public const long _maxFileSize = 4194304;
|
||||
|
||||
@@ -109,23 +114,35 @@ namespace Lantean.QBTMudBlade
|
||||
await Task.Delay(0);
|
||||
}
|
||||
|
||||
public static async Task InvokeAddCategoryDialog(this IDialogService dialogService, IApiClient apiClient, IEnumerable<string>? hashes = null)
|
||||
public static async Task<string?> ShowAddCategoryDialog(this IDialogService dialogService, IApiClient apiClient)
|
||||
{
|
||||
var reference = await dialogService.ShowAsync<DeleteDialog>("New Category");
|
||||
var reference = await dialogService.ShowAsync<AddCategoryDialog>("New Category", NonBlurFormDialogOptions);
|
||||
var result = await reference.Result;
|
||||
if (result.Canceled)
|
||||
{
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
var category = (Category)result.Data;
|
||||
|
||||
await apiClient.AddCategory(category.Name, category.SavePath);
|
||||
|
||||
if (hashes is not null)
|
||||
return category.Name;
|
||||
}
|
||||
|
||||
public static async Task<HashSet<string>?> ShowAddTagsDialog(this IDialogService dialogService, IApiClient apiClient)
|
||||
{
|
||||
var dialogReference = await dialogService.ShowAsync<AddTagDialog>("Add Tags", NonBlurFormDialogOptions);
|
||||
var result = await dialogReference.Result;
|
||||
|
||||
if (result.Canceled)
|
||||
{
|
||||
await apiClient.SetTorrentCategory(category.Name, null, hashes.ToArray());
|
||||
return null;
|
||||
}
|
||||
|
||||
var tags = (HashSet<string>)result.Data;
|
||||
|
||||
return tags;
|
||||
}
|
||||
|
||||
public static async Task ShowConfirmDialog(this IDialogService dialogService, string title, string content, Func<Task> onSuccess)
|
||||
@@ -134,7 +151,7 @@ namespace Lantean.QBTMudBlade
|
||||
{
|
||||
{ nameof(ConfirmDialog.Content), content }
|
||||
};
|
||||
var result = await dialogService.ShowAsync<ConfirmDialog>(title, parameters, _confirmDialogOptions);
|
||||
var result = await dialogService.ShowAsync<ConfirmDialog>(title, parameters, ConfirmDialogOptions);
|
||||
|
||||
var dialogResult = await result.Result;
|
||||
if (dialogResult.Canceled)
|
||||
@@ -162,7 +179,7 @@ namespace Lantean.QBTMudBlade
|
||||
{ nameof(SingleFieldDialog<T>.Label), label },
|
||||
{ nameof(SingleFieldDialog<T>.Value), value }
|
||||
};
|
||||
var result = await dialogService.ShowAsync<SingleFieldDialog<T>>(title, parameters, _confirmDialogOptions);
|
||||
var result = await dialogService.ShowAsync<SingleFieldDialog<T>>(title, parameters, FormDialogOptions);
|
||||
|
||||
var dialogResult = await result.Result;
|
||||
if (dialogResult.Canceled)
|
||||
@@ -251,15 +268,17 @@ namespace Lantean.QBTMudBlade
|
||||
await Task.Delay(0);
|
||||
}
|
||||
|
||||
public static async Task ShowSubMenu(this IDialogService dialogService, IEnumerable<string> hashes, TorrentAction parent)
|
||||
public static async Task ShowSubMenu(this IDialogService dialogService, IEnumerable<string> hashes, TorrentAction parent, MainData mainData, QBitTorrentClient.Models.Preferences? preferences)
|
||||
{
|
||||
var parameters = new DialogParameters
|
||||
{
|
||||
{ nameof(SubMenuDialog.ParentAction), parent },
|
||||
{ nameof(SubMenuDialog.Hashes), hashes }
|
||||
{ nameof(SubMenuDialog.Hashes), hashes },
|
||||
{ nameof(SubMenuDialog.MainData), mainData },
|
||||
{ nameof(SubMenuDialog.Preferences), preferences },
|
||||
};
|
||||
|
||||
await dialogService.ShowAsync<SubMenuDialog>("Actions", parameters, FormDialogOptions);
|
||||
await dialogService.ShowAsync<SubMenuDialog>(parent.Name, parameters, FormDialogOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,4 +22,10 @@
|
||||
<ProjectReference Include="..\Lantean.QBitTorrentClient\Lantean.QBitTorrentClient.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="Components\Dialogs\ManageCategoriesDialog.razor">
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -2,39 +2,40 @@
|
||||
|
||||
|
||||
<EnhancedErrorBoundary @ref="ErrorBoundary" OnClear="Cleared">
|
||||
<MudThemeProvider @ref="MudThemeProvider" @bind-IsDarkMode="IsDarkMode" Theme="Theme" />
|
||||
<MudDialogProvider />
|
||||
<MudSnackbarProvider />
|
||||
|
||||
<PageTitle>qBittorrent Web UI</PageTitle>
|
||||
|
||||
<MudLayout>
|
||||
<MudAppBar>
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Menu" Color="Color.Inherit" Edge="Edge.Start" OnClick="ToggleDrawer" />
|
||||
<MudText Typo="Typo.h5" Class="ml-3">qBittorrent Web UI</MudText>
|
||||
<MudSpacer />
|
||||
@if (ErrorBoundary?.Errors.Count > 0)
|
||||
{
|
||||
<MudBadge Content="@(ErrorBoundary?.Errors.Count ?? 0)" Color="Color.Error" Overlap="true">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Error" Color="Color.Default" OnClick="ToggleErrorDrawer" />
|
||||
</MudBadge>
|
||||
}
|
||||
<MudSwitch T="bool" Label="Dark Mode" LabelPosition="LabelPosition.End" @bind-Value="IsDarkMode" />
|
||||
@if (ShowMenu)
|
||||
{
|
||||
<Menu />
|
||||
}
|
||||
</MudAppBar>
|
||||
<MudDrawer Open="ErrorDrawerOpen" ClipMode="DrawerClipMode.Docked" Elevation="2" Anchor="Anchor.Right">
|
||||
<ErrorDisplay ErrorBoundary="ErrorBoundary" />
|
||||
</MudDrawer>
|
||||
<CascadingValue Value="Theme">
|
||||
<CascadingValue Value="DrawerOpen" Name="DrawerOpen">
|
||||
<CascadingValue Value="IsDarkMode" Name="IsDarkMode">
|
||||
@Body
|
||||
</CascadingValue>
|
||||
</CascadingValue>
|
||||
</CascadingValue>
|
||||
</MudLayout>
|
||||
|
||||
</EnhancedErrorBoundary>
|
||||
|
||||
<MudThemeProvider @ref="MudThemeProvider" @bind-IsDarkMode="IsDarkMode" Theme="Theme" />
|
||||
<MudDialogProvider />
|
||||
<MudSnackbarProvider />
|
||||
|
||||
<PageTitle>qBittorrent Web UI</PageTitle>
|
||||
|
||||
<MudLayout>
|
||||
<MudAppBar>
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Menu" Color="Color.Inherit" Edge="Edge.Start" OnClick="ToggleDrawer" />
|
||||
<MudText Typo="Typo.h5" Class="ml-3">qBittorrent Web UI</MudText>
|
||||
<MudSpacer />
|
||||
@if (ErrorBoundary?.Errors.Count > 0)
|
||||
{
|
||||
<MudBadge Content="@(ErrorBoundary?.Errors.Count ?? 0)" Color="Color.Error" Overlap="true">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Error" Color="Color.Default" OnClick="ToggleErrorDrawer" />
|
||||
</MudBadge>
|
||||
}
|
||||
<MudSwitch T="bool" Label="Dark Mode" LabelPosition="LabelPosition.End" @bind-Value="IsDarkMode" />
|
||||
@if (ShowMenu)
|
||||
{
|
||||
<Menu />
|
||||
}
|
||||
</MudAppBar>
|
||||
<MudDrawer Open="ErrorDrawerOpen" ClipMode="DrawerClipMode.Docked" Elevation="2" Anchor="Anchor.Right">
|
||||
<ErrorDisplay ErrorBoundary="ErrorBoundary" />
|
||||
</MudDrawer>
|
||||
<CascadingValue Value="Theme">
|
||||
<CascadingValue Value="DrawerOpen" Name="DrawerOpen">
|
||||
<CascadingValue Value="IsDarkMode" Name="IsDarkMode">
|
||||
@Body
|
||||
</CascadingValue>
|
||||
</CascadingValue>
|
||||
</CascadingValue>
|
||||
</MudLayout>
|
||||
@@ -49,7 +49,7 @@ namespace Lantean.QBTMudBlade.Pages
|
||||
#if DEBUG
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await DoLogin("admin", "u4FR4ZQCm");
|
||||
await DoLogin("admin", "U9zfhvndk");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
OnRowClick="RowClick"
|
||||
MultiSelection="true"
|
||||
SelectedItemsChanged="SelectedItemsChanged"
|
||||
SelectedItemChanged="SelectedItemChanged"
|
||||
SortColumnChanged="SortColumnChangedHandler"
|
||||
SortDirectionChanged="SortDirectionChangedHandler"
|
||||
/>
|
||||
|
||||
@@ -39,21 +39,15 @@ namespace Lantean.QBTMudBlade.Pages
|
||||
|
||||
protected string? SearchText { get; set; }
|
||||
|
||||
protected Torrent? SelectedTorrent { get; set; }
|
||||
|
||||
protected HashSet<Torrent> SelectedItems { get; set; } = [];
|
||||
|
||||
protected bool ToolbarButtonsEnabled => SelectedItems.Count > 0 || SelectedTorrent is not null;
|
||||
protected bool ToolbarButtonsEnabled => SelectedItems.Count > 0;
|
||||
|
||||
protected DynamicTable<Torrent>? Table { get; set; }
|
||||
|
||||
protected void SelectedItemsChanged(HashSet<Torrent> selectedItems)
|
||||
{
|
||||
SelectedItems = selectedItems;
|
||||
if (selectedItems.Count == 1)
|
||||
{
|
||||
SelectedTorrent = selectedItems.First();
|
||||
}
|
||||
}
|
||||
|
||||
protected async Task SortDirectionChangedHandler(SortDirection sortDirection)
|
||||
@@ -61,11 +55,6 @@ namespace Lantean.QBTMudBlade.Pages
|
||||
await SortDirectionChanged.InvokeAsync(sortDirection);
|
||||
}
|
||||
|
||||
protected void SelectedItemChanged(Torrent torrent)
|
||||
{
|
||||
SelectedTorrent = torrent;
|
||||
}
|
||||
|
||||
protected async Task SortColumnChangedHandler(string columnId)
|
||||
{
|
||||
await SortColumnChanged.InvokeAsync(columnId);
|
||||
@@ -122,7 +111,12 @@ namespace Lantean.QBTMudBlade.Pages
|
||||
{
|
||||
if (eventArgs.MouseEventArgs.Detail > 1)
|
||||
{
|
||||
NavigationManager.NavigateTo("/details/" + SelectedTorrent);
|
||||
var torrent = SelectedItems.FirstOrDefault();
|
||||
if (torrent is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
NavigationManager.NavigateTo($"/details/{torrent.Hash}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,11 +127,6 @@ namespace Lantean.QBTMudBlade.Pages
|
||||
return SelectedItems.Select(t => t.Hash);
|
||||
}
|
||||
|
||||
if (SelectedTorrent is not null)
|
||||
{
|
||||
return [SelectedTorrent.Hash];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -158,11 +147,12 @@ namespace Lantean.QBTMudBlade.Pages
|
||||
|
||||
protected void ShowTorrent()
|
||||
{
|
||||
if (SelectedTorrent is null)
|
||||
var torrent = SelectedItems.FirstOrDefault();
|
||||
if (torrent is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
NavigationManager.NavigateTo("/details/" + SelectedTorrent.Hash);
|
||||
NavigationManager.NavigateTo($"/details/{torrent.Hash}");
|
||||
}
|
||||
|
||||
protected IEnumerable<ColumnDefinition<Torrent>> Columns => ColumnsDefinitions;
|
||||
@@ -170,7 +160,7 @@ namespace Lantean.QBTMudBlade.Pages
|
||||
public static List<ColumnDefinition<Torrent>> ColumnsDefinitions { get; } =
|
||||
[
|
||||
CreateColumnDefinition("#", t => t.Priority),
|
||||
CreateColumnDefinition("", t => t.State, IconColumn, iconOnly: true),
|
||||
CreateColumnDefinition("Icon", t => t.State, IconColumn, iconOnly: true),
|
||||
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),
|
||||
|
||||
@@ -133,7 +133,7 @@ td.no-wrap {
|
||||
}
|
||||
|
||||
.sub-menu::after {
|
||||
content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z'/%3E%3C/svg%3E");
|
||||
content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z' fill='rgba(39,44,52,1)'/%3E%3C/svg%3E");
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
position: absolute;
|
||||
|
||||
@@ -798,7 +798,7 @@ namespace Lantean.QBitTorrentClient
|
||||
{
|
||||
var content = new FormUrlEncodedBuilder()
|
||||
.AddAllOrPipeSeparated("hashes", all, hashes)
|
||||
.AddPipeSeparated("tags", tags)
|
||||
.AddCommaSeparated("tags", tags)
|
||||
.ToFormUrlEncodedContent();
|
||||
|
||||
var response = await _httpClient.PostAsync("torrents/addTags", content);
|
||||
@@ -810,7 +810,7 @@ namespace Lantean.QBitTorrentClient
|
||||
{
|
||||
var content = new FormUrlEncodedBuilder()
|
||||
.AddAllOrPipeSeparated("hashes", all, hashes)
|
||||
.AddPipeSeparated("tags", tags)
|
||||
.AddCommaSeparated("tags", tags)
|
||||
.ToFormUrlEncodedContent();
|
||||
|
||||
var response = await _httpClient.PostAsync("torrents/removeTags", content);
|
||||
@@ -830,7 +830,7 @@ namespace Lantean.QBitTorrentClient
|
||||
public async Task CreateTags(IEnumerable<string> tags)
|
||||
{
|
||||
var content = new FormUrlEncodedBuilder()
|
||||
.AddPipeSeparated("tags", tags)
|
||||
.AddCommaSeparated("tags", tags)
|
||||
.ToFormUrlEncodedContent();
|
||||
|
||||
var response = await _httpClient.PostAsync("torrents/createTags", content);
|
||||
@@ -841,7 +841,7 @@ namespace Lantean.QBitTorrentClient
|
||||
public async Task DeleteTags(IEnumerable<string> tags)
|
||||
{
|
||||
var content = new FormUrlEncodedBuilder()
|
||||
.AddPipeSeparated("tags", tags)
|
||||
.AddCommaSeparated("tags", tags)
|
||||
.ToFormUrlEncodedContent();
|
||||
|
||||
var response = await _httpClient.PostAsync("torrents/deleteTags", content);
|
||||
|
||||
@@ -66,11 +66,31 @@ namespace Lantean.QBitTorrentClient
|
||||
return apiClient.SetTorrentCategory(category, null, hash);
|
||||
}
|
||||
|
||||
public static Task SetTorrentCategory(this IApiClient apiClient, string category, IEnumerable<string> hashes)
|
||||
{
|
||||
return apiClient.SetTorrentCategory(category, null, hashes.ToArray());
|
||||
}
|
||||
|
||||
public static Task RemoveTorrentCategory(this IApiClient apiClient, string hash)
|
||||
{
|
||||
return apiClient.SetTorrentCategory(string.Empty, null, hash);
|
||||
}
|
||||
|
||||
public static Task RemoveTorrentCategory(this IApiClient apiClient, IEnumerable<string> hashes)
|
||||
{
|
||||
return apiClient.SetTorrentCategory(string.Empty, null, hashes.ToArray());
|
||||
}
|
||||
|
||||
public static Task RemoveTorrentTags(this IApiClient apiClient, IEnumerable<string> tags, string hash)
|
||||
{
|
||||
return apiClient.RemoveTorrentTags(tags, null, hash);
|
||||
}
|
||||
|
||||
public static Task RemoveTorrentTags(this IApiClient apiClient, IEnumerable<string> tags, IEnumerable<string> hashes)
|
||||
{
|
||||
return apiClient.RemoveTorrentTags(tags, null, hashes.ToArray());
|
||||
}
|
||||
|
||||
public static Task RemoveTorrentTag(this IApiClient apiClient, string tag, string hash)
|
||||
{
|
||||
return apiClient.RemoveTorrentTags([tag], hash);
|
||||
@@ -86,6 +106,11 @@ namespace Lantean.QBitTorrentClient
|
||||
return apiClient.AddTorrentTags(tags, null, hash);
|
||||
}
|
||||
|
||||
public static Task AddTorrentTags(this IApiClient apiClient, IEnumerable<string> tags, IEnumerable<string> hashes)
|
||||
{
|
||||
return apiClient.AddTorrentTags(tags, null, hashes.ToArray());
|
||||
}
|
||||
|
||||
public static Task AddTorrentTag(this IApiClient apiClient, string tag, string hash)
|
||||
{
|
||||
return apiClient.AddTorrentTags([tag], hash);
|
||||
@@ -95,6 +120,7 @@ namespace Lantean.QBitTorrentClient
|
||||
{
|
||||
return apiClient.AddTorrentTags([tag], null, hashes.ToArray());
|
||||
}
|
||||
|
||||
|
||||
public static Task RecheckTorrent(this IApiClient apiClient, string hash)
|
||||
{
|
||||
|
||||
@@ -14,14 +14,13 @@ namespace Lantean.QBitTorrentClient.Converters
|
||||
|
||||
List<string> list;
|
||||
var value = reader.GetString();
|
||||
if (value is null)
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
list = [];
|
||||
}
|
||||
else
|
||||
{
|
||||
var values = value.Split(',');
|
||||
list = [.. values];
|
||||
list = [.. value.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)];
|
||||
}
|
||||
|
||||
return list.AsReadOnly();
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace Lantean.QBitTorrentClient.Models
|
||||
long? estimatedTimeOfArrival,
|
||||
bool? firstLastPiecePriority,
|
||||
bool? forceStart,
|
||||
string hash,
|
||||
string? infoHashV1,
|
||||
string? infoHashV2,
|
||||
long? lastActivity,
|
||||
@@ -70,6 +71,7 @@ namespace Lantean.QBitTorrentClient.Models
|
||||
EstimatedTimeOfArrival = estimatedTimeOfArrival;
|
||||
FirstLastPiecePriority = firstLastPiecePriority;
|
||||
ForceStart = forceStart;
|
||||
Hash = hash;
|
||||
InfoHashV1 = infoHashV1;
|
||||
InfoHashV2 = infoHashV2;
|
||||
LastActivity = lastActivity;
|
||||
@@ -149,6 +151,9 @@ namespace Lantean.QBitTorrentClient.Models
|
||||
[JsonPropertyName("force_start")]
|
||||
public bool? ForceStart { get; }
|
||||
|
||||
[JsonPropertyName("hash")]
|
||||
public string Hash { get; }
|
||||
|
||||
[JsonPropertyName("infohash_v1")]
|
||||
public string? InfoHashV1 { get; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user