diff --git a/Lantean.QBTMudBlade/Components/Dialogs/AddTagDialog.razor b/Lantean.QBTMudBlade/Components/Dialogs/AddTagDialog.razor
index bcf9eb0..e53744e 100644
--- a/Lantean.QBTMudBlade/Components/Dialogs/AddTagDialog.razor
+++ b/Lantean.QBTMudBlade/Components/Dialogs/AddTagDialog.razor
@@ -3,7 +3,7 @@
- |
+ |
|
@foreach (var tag in Tags)
@@ -11,7 +11,7 @@
var tagRef = tag;
@tag |
- |
+ |
}
diff --git a/Lantean.QBTMudBlade/Components/Dialogs/AddTagDialog.razor.cs b/Lantean.QBTMudBlade/Components/Dialogs/AddTagDialog.razor.cs
index cfb7bb6..305a82a 100644
--- a/Lantean.QBTMudBlade/Components/Dialogs/AddTagDialog.razor.cs
+++ b/Lantean.QBTMudBlade/Components/Dialogs/AddTagDialog.razor.cs
@@ -34,6 +34,11 @@ namespace Lantean.QBTMudBlade.Components.Dialogs
Tag = tag;
}
+ protected void DeleteTag(string tag)
+ {
+ Tags.Remove(tag);
+ }
+
protected void Cancel()
{
MudDialog.Cancel();
diff --git a/Lantean.QBTMudBlade/Components/Dialogs/AddTorrentLinkDialog.razor b/Lantean.QBTMudBlade/Components/Dialogs/AddTorrentLinkDialog.razor
index b2449fb..eab964b 100644
--- a/Lantean.QBTMudBlade/Components/Dialogs/AddTorrentLinkDialog.razor
+++ b/Lantean.QBTMudBlade/Components/Dialogs/AddTorrentLinkDialog.razor
@@ -2,7 +2,7 @@
-
+
diff --git a/Lantean.QBTMudBlade/Components/Dialogs/AddTrackerDialog.razor b/Lantean.QBTMudBlade/Components/Dialogs/AddTrackerDialog.razor
new file mode 100644
index 0000000..a5e6080
--- /dev/null
+++ b/Lantean.QBTMudBlade/Components/Dialogs/AddTrackerDialog.razor
@@ -0,0 +1,24 @@
+
+
+
+
+
+ |
+ |
+
+ @foreach (var tracker in Trackers)
+ {
+ var trackerRef = tracker;
+
+ @tracker |
+ |
+
+ }
+
+
+
+
+ Cancel
+ Save
+
+
\ No newline at end of file
diff --git a/Lantean.QBTMudBlade/Components/Dialogs/AddTrackerDialog.razor.cs b/Lantean.QBTMudBlade/Components/Dialogs/AddTrackerDialog.razor.cs
new file mode 100644
index 0000000..33db1e1
--- /dev/null
+++ b/Lantean.QBTMudBlade/Components/Dialogs/AddTrackerDialog.razor.cs
@@ -0,0 +1,45 @@
+using Microsoft.AspNetCore.Components;
+using MudBlazor;
+
+namespace Lantean.QBTMudBlade.Components.Dialogs
+{
+ public partial class AddTrackerDialog
+ {
+ [CascadingParameter]
+ public MudDialogInstance MudDialog { get; set; } = default!;
+
+ protected HashSet Trackers { get; } = [];
+
+ protected string? Tracker { get; set; }
+
+ protected void AddTracker()
+ {
+ if (string.IsNullOrEmpty(Tracker))
+ {
+ return;
+ }
+ Trackers.Add(Tracker);
+ Tracker = null;
+ }
+
+ protected void SetTracker(string tracker)
+ {
+ Tracker = tracker;
+ }
+
+ protected void DeleteTracker(string tracker)
+ {
+ Trackers.Remove(tracker);
+ }
+
+ protected void Cancel()
+ {
+ MudDialog.Cancel();
+ }
+
+ protected void Submit()
+ {
+ MudDialog.Close(Trackers);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Lantean.QBTMudBlade/Components/Dialogs/CategoryPropertiesDialog.razor b/Lantean.QBTMudBlade/Components/Dialogs/CategoryPropertiesDialog.razor
index f2982c2..dc3b318 100644
--- a/Lantean.QBTMudBlade/Components/Dialogs/CategoryPropertiesDialog.razor
+++ b/Lantean.QBTMudBlade/Components/Dialogs/CategoryPropertiesDialog.razor
@@ -2,10 +2,10 @@
-
+
-
+
diff --git a/Lantean.QBTMudBlade/Components/Dialogs/ColumnOptionsDialog.razor b/Lantean.QBTMudBlade/Components/Dialogs/ColumnOptionsDialog.razor
index 8e6dca6..5fabc71 100644
--- a/Lantean.QBTMudBlade/Components/Dialogs/ColumnOptionsDialog.razor
+++ b/Lantean.QBTMudBlade/Components/Dialogs/ColumnOptionsDialog.razor
@@ -12,7 +12,7 @@
-
+
diff --git a/Lantean.QBTMudBlade/Components/Dialogs/ColumnOptionsDialog.razor.cs b/Lantean.QBTMudBlade/Components/Dialogs/ColumnOptionsDialog.razor.cs
index 0c991ea..a91ed57 100644
--- a/Lantean.QBTMudBlade/Components/Dialogs/ColumnOptionsDialog.razor.cs
+++ b/Lantean.QBTMudBlade/Components/Dialogs/ColumnOptionsDialog.razor.cs
@@ -22,6 +22,8 @@ namespace Lantean.QBTMudBlade.Components.Dialogs
protected HashSet SelectedColumnsInternal { get; set; } = [];
+ protected Dictionary WidthsInternal { get; set; } = [];
+
protected override void OnParametersSet()
{
if (SelectedColumnsInternal.Count == 0)
@@ -41,6 +43,14 @@ namespace Lantean.QBTMudBlade.Components.Dialogs
}
}
}
+
+ if (WidthsInternal.Count == 0)
+ {
+ foreach (var width in Widths)
+ {
+ WidthsInternal[width.Key] = width.Value;
+ }
+ }
}
protected void SetSelected(bool selected, string id)
@@ -64,22 +74,22 @@ namespace Lantean.QBTMudBlade.Components.Dialogs
{
if (width == defaultWidth)
{
- Widths.Remove(id);
+ WidthsInternal.Remove(id);
}
else
{
- Widths[id] = width;
+ WidthsInternal[id] = width;
}
}
else
{
if (defaultWidth is null)
{
- Widths.Remove(id);
+ WidthsInternal.Remove(id);
}
else
{
- Widths[id] = null;
+ WidthsInternal[id] = null;
}
}
}
@@ -106,7 +116,7 @@ namespace Lantean.QBTMudBlade.Components.Dialogs
protected string GetValue(int? value, string columnId)
{
- if (Widths.TryGetValue(columnId, out var newWidth))
+ if (WidthsInternal.TryGetValue(columnId, out var newWidth))
{
value = newWidth;
}
@@ -131,7 +141,7 @@ namespace Lantean.QBTMudBlade.Components.Dialogs
protected void Submit(MouseEventArgs args)
{
- MudDialog.Close(DialogResult.Ok((SelectedColumnsInternal, Widths)));
+ MudDialog.Close(DialogResult.Ok((SelectedColumnsInternal, WidthsInternal)));
}
}
}
\ No newline at end of file
diff --git a/Lantean.QBTMudBlade/Components/Dialogs/ManageTagsDialog.razor.cs b/Lantean.QBTMudBlade/Components/Dialogs/ManageTagsDialog.razor.cs
index ac1c2d3..72d0bd8 100644
--- a/Lantean.QBTMudBlade/Components/Dialogs/ManageTagsDialog.razor.cs
+++ b/Lantean.QBTMudBlade/Components/Dialogs/ManageTagsDialog.razor.cs
@@ -101,7 +101,7 @@ namespace Lantean.QBTMudBlade.Components.Dialogs
protected async Task AddTag()
{
- var addedTags = await DialogService.ShowAddTagsDialog(ApiClient);
+ var addedTags = await DialogService.ShowAddTagsDialog();
if (addedTags is null || addedTags.Count == 0)
{
diff --git a/Lantean.QBTMudBlade/Components/Dialogs/SingleFieldDialog.razor b/Lantean.QBTMudBlade/Components/Dialogs/SingleFieldDialog.razor
index 8c5b9ea..26b79fd 100644
--- a/Lantean.QBTMudBlade/Components/Dialogs/SingleFieldDialog.razor
+++ b/Lantean.QBTMudBlade/Components/Dialogs/SingleFieldDialog.razor
@@ -4,7 +4,7 @@
-
+
diff --git a/Lantean.QBTMudBlade/Components/Dialogs/SliderFieldDialog.razor b/Lantean.QBTMudBlade/Components/Dialogs/SliderFieldDialog.razor
index 40798bf..519babf 100644
--- a/Lantean.QBTMudBlade/Components/Dialogs/SliderFieldDialog.razor
+++ b/Lantean.QBTMudBlade/Components/Dialogs/SliderFieldDialog.razor
@@ -4,7 +4,7 @@
-
+
diff --git a/Lantean.QBTMudBlade/Components/FiltersNav.razor.cs b/Lantean.QBTMudBlade/Components/FiltersNav.razor.cs
index 32030d7..ede3d4f 100644
--- a/Lantean.QBTMudBlade/Components/FiltersNav.razor.cs
+++ b/Lantean.QBTMudBlade/Components/FiltersNav.razor.cs
@@ -313,7 +313,13 @@ namespace Lantean.QBTMudBlade.Components
return;
}
- await DialogService.ShowAddTagsDialog(ApiClient);
+ var tags = await DialogService.ShowAddTagsDialog();
+ if (tags is null || tags.Count == 0)
+ {
+ return;
+ }
+
+ await ApiClient.CreateTags(tags);
}
protected async Task RemoveTag()
diff --git a/Lantean.QBTMudBlade/Components/Options/AdvancedOptions.razor b/Lantean.QBTMudBlade/Components/Options/AdvancedOptions.razor
index 0bd25e3..c9c4cf1 100644
--- a/Lantean.QBTMudBlade/Components/Options/AdvancedOptions.razor
+++ b/Lantean.QBTMudBlade/Components/Options/AdvancedOptions.razor
@@ -9,16 +9,16 @@
-
+
Fastresume files
SQLite database (experimental)
-
+
-
+
Any interface
@foreach (var networkInterface in NetworkInterfaces)
{
@@ -27,7 +27,7 @@
-
+
All addresses
All IPv4 addresses
All IPv6 addresses
@@ -38,16 +38,16 @@
-
+
-
+
-
+
@@ -59,7 +59,7 @@
-
+
@@ -77,47 +77,47 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
Default
Memory mapped files
POSIX-compliant
-
+
Disable OS cache
Enable OS cache
-
+
Disable OS cache
Enable OS cache
Write-through (requires libtorrent >= 2.0.6)
@@ -133,40 +133,40 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
Prefer TCP
Peer proportional (throttles TCP)
@@ -187,13 +187,13 @@
-
+
Fixed slots
Upload rate based
-
+
Round-robin
Fastest upload
Anti-leech
@@ -206,37 +206,37 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
diff --git a/Lantean.QBTMudBlade/Components/Options/BehaviourOptions.razor b/Lantean.QBTMudBlade/Components/Options/BehaviourOptions.razor
index 2af13f4..111d6c3 100644
--- a/Lantean.QBTMudBlade/Components/Options/BehaviourOptions.razor
+++ b/Lantean.QBTMudBlade/Components/Options/BehaviourOptions.razor
@@ -9,7 +9,7 @@
-
+
English
@@ -29,13 +29,13 @@
-
+
-
+
@@ -43,10 +43,10 @@
-
+
-
+
days
months
years
diff --git a/Lantean.QBTMudBlade/Components/Options/BitTorrentOptions.razor b/Lantean.QBTMudBlade/Components/Options/BitTorrentOptions.razor
index ccdd7c3..d341d28 100644
--- a/Lantean.QBTMudBlade/Components/Options/BitTorrentOptions.razor
+++ b/Lantean.QBTMudBlade/Components/Options/BitTorrentOptions.razor
@@ -18,7 +18,7 @@
-
+
Allow encryption
Require encryption
Disable encryption
@@ -38,7 +38,7 @@
-
+
@@ -56,25 +56,25 @@
-
+
-
+
-
+
-
+
-
+
-
+
@@ -92,22 +92,22 @@
-
+
-
+
-
+
-
+
Stop torrent
Remove torrent
Remove torrent and its files
@@ -130,7 +130,7 @@
-
+
diff --git a/Lantean.QBTMudBlade/Components/Options/ConnectionOptions.razor b/Lantean.QBTMudBlade/Components/Options/ConnectionOptions.razor
index ad37881..1d6209f 100644
--- a/Lantean.QBTMudBlade/Components/Options/ConnectionOptions.razor
+++ b/Lantean.QBTMudBlade/Components/Options/ConnectionOptions.razor
@@ -4,7 +4,7 @@
-
+
TCP and μTP
TCP
μTP
@@ -23,7 +23,7 @@
-
+
@@ -44,25 +44,25 @@
-
+
-
+
-
+
-
+
@@ -75,10 +75,10 @@
-
+
-
+
@@ -96,7 +96,7 @@
-
+
None
SOCKS4
SOCKS5
@@ -104,10 +104,10 @@
-
+
-
+
@@ -116,10 +116,10 @@
-
+
-
+
@@ -150,13 +150,13 @@
-
+
-
+
diff --git a/Lantean.QBTMudBlade/Components/Options/DownloadsOptions.razor b/Lantean.QBTMudBlade/Components/Options/DownloadsOptions.razor
index a840762..0b0d714 100644
--- a/Lantean.QBTMudBlade/Components/Options/DownloadsOptions.razor
+++ b/Lantean.QBTMudBlade/Components/Options/DownloadsOptions.razor
@@ -9,7 +9,7 @@
-
+
Original
Create subfolder
Don't create subfolder
@@ -22,7 +22,7 @@
-
+
None
Metadata received
Files Checked
@@ -62,25 +62,25 @@
-
+
Manual
Automatic
-
+
Relocate torrent
Switch torrent to Manual Mode
-
+
Relocate affected torrents
Switch affected torrents to Manual Mode
-
+
Relocate affected torrents
Switch affected torrents to Manual Mode
@@ -89,7 +89,7 @@
-
+
@@ -97,7 +97,7 @@
-
+
@@ -107,7 +107,7 @@
-
+
@@ -117,7 +117,7 @@
-
+
@@ -225,7 +225,7 @@
-
+
@@ -243,13 +243,13 @@
-
+
-
+
-
+
@@ -258,10 +258,10 @@
-
+
-
+
@@ -279,13 +279,13 @@
-
+
-
+
Supported parameters (case sensitive):
diff --git a/Lantean.QBTMudBlade/Components/Options/RSSOptions.razor b/Lantean.QBTMudBlade/Components/Options/RSSOptions.razor
index beb143f..629f325 100644
--- a/Lantean.QBTMudBlade/Components/Options/RSSOptions.razor
+++ b/Lantean.QBTMudBlade/Components/Options/RSSOptions.razor
@@ -12,10 +12,10 @@
-
+
-
+
@@ -51,7 +51,7 @@
-
+
diff --git a/Lantean.QBTMudBlade/Components/Options/SpeedOptions.razor b/Lantean.QBTMudBlade/Components/Options/SpeedOptions.razor
index a84f112..d289f07 100644
--- a/Lantean.QBTMudBlade/Components/Options/SpeedOptions.razor
+++ b/Lantean.QBTMudBlade/Components/Options/SpeedOptions.razor
@@ -9,10 +9,10 @@
-
+
-
+
@@ -27,10 +27,10 @@
-
+
-
+
@@ -42,7 +42,7 @@
-
+
Every day
Weekdays
Weekends
diff --git a/Lantean.QBTMudBlade/Components/Options/WebUIOptions.razor b/Lantean.QBTMudBlade/Components/Options/WebUIOptions.razor
index 8ae01d1..091d101 100644
--- a/Lantean.QBTMudBlade/Components/Options/WebUIOptions.razor
+++ b/Lantean.QBTMudBlade/Components/Options/WebUIOptions.razor
@@ -9,10 +9,10 @@
-
+
-
+
@@ -33,10 +33,10 @@
-
+
-
+
@@ -51,10 +51,10 @@
-
+
-
+
@@ -63,16 +63,16 @@
-
+
-
+
-
+
-
+
@@ -90,7 +90,7 @@
-
+
@@ -117,7 +117,7 @@
-
+
@@ -144,7 +144,7 @@
-
+
@@ -162,7 +162,7 @@
-
+
@@ -180,7 +180,7 @@
-
+
@@ -198,7 +198,7 @@
-
+
DynDNS
NO-IP
@@ -207,13 +207,13 @@
Register
-
+
-
+
-
+
diff --git a/Lantean.QBTMudBlade/Components/PeersTab.razor b/Lantean.QBTMudBlade/Components/PeersTab.razor
index e1b3519..ec5beac 100644
--- a/Lantean.QBTMudBlade/Components/PeersTab.razor
+++ b/Lantean.QBTMudBlade/Components/PeersTab.razor
@@ -1,53 +1,5 @@
-
-
- @if (ShowFlags)
- {
- Country/Region
- }
- IP
- Port
- Connection
- Flags
- Client
- Progress
- Download Speed
- Upload Speed
- Downloaded
- Uploaded
- Relevance
- Files
-
-
- @if (ShowFlags)
- {
-
- }
- @context.IPAddress
- @context.Port
- @context.Connection
- @context.Flags
- @context.Client
- @DisplayHelpers.Percentage(context.Progress)
- @DisplayHelpers.Speed(context.DownloadSpeed)
- @DisplayHelpers.Speed(context.UploadSpeed)
- @DisplayHelpers.Size(context.Downloaded)
- @DisplayHelpers.Size(context.Uploaded)
- @DisplayHelpers.Percentage(context.Relevance)
- @context.Files
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/Lantean.QBTMudBlade/Components/PeersTab.razor.cs b/Lantean.QBTMudBlade/Components/PeersTab.razor.cs
index 1f68e3c..aa7c8b6 100644
--- a/Lantean.QBTMudBlade/Components/PeersTab.razor.cs
+++ b/Lantean.QBTMudBlade/Components/PeersTab.razor.cs
@@ -2,6 +2,8 @@
using Lantean.QBTMudBlade.Models;
using Lantean.QBTMudBlade.Services;
using Microsoft.AspNetCore.Components;
+using MudBlazor;
+using System.Data;
using System.Net;
namespace Lantean.QBTMudBlade.Components
@@ -123,6 +125,26 @@ namespace Lantean.QBTMudBlade.Components
}
}
+ protected IEnumerable> Columns => ColumnsDefinitions.Where(c => c.Id != "country/region" || _showFlags == true);
+
+ public static List> ColumnsDefinitions { get; } =
+ [
+ new ColumnDefinition("Country/Region", p => p.Country),
+ new ColumnDefinition("IP", p => p.IPAddress),
+ new ColumnDefinition("Port", p => p.Port),
+ new ColumnDefinition("Connection", p => p.Connection),
+ new ColumnDefinition("Flags", p => p.Flags),
+ new ColumnDefinition("Client", p => p.Client),
+ new ColumnDefinition("Progress", p => p.Progress, p => DisplayHelpers.Percentage(p.Progress)),
+ new ColumnDefinition("Download Speed", p => p.DownloadSpeed, p => DisplayHelpers.Speed(p.DownloadSpeed)),
+ new ColumnDefinition("Upload Speed", p => p.UploadSpeed, p => DisplayHelpers.Speed(p.UploadSpeed)),
+ new ColumnDefinition("Downloaded", p => p.Downloaded, p => @DisplayHelpers.Size(p.Downloaded)),
+ new ColumnDefinition("Uploaded", p => p.Uploaded, p => @DisplayHelpers.Size(p.Uploaded)),
+ new ColumnDefinition("Relevance", p => p.Relevance, p => @DisplayHelpers.Percentage(p.Relevance)),
+ new ColumnDefinition("Files", p => p.Files, p => p.Files),
+ ];
+
+
protected virtual async Task DisposeAsync(bool disposing)
{
if (!_disposedValue)
diff --git a/Lantean.QBTMudBlade/Components/TrackersTab.razor b/Lantean.QBTMudBlade/Components/TrackersTab.razor
index 1a107ff..28dd371 100644
--- a/Lantean.QBTMudBlade/Components/TrackersTab.razor
+++ b/Lantean.QBTMudBlade/Components/TrackersTab.razor
@@ -1,42 +1,20 @@
-
-
- Tier
- URL
- Status
- Peers
- Seeds
- Leeches
- Times Downloaded
- Message
-
-
-
- @if (context.Tier >= 0)
- {
- @context.Tier
- }
-
- @context.Url
- @context.Status
- @context.Peers
- @context.Seeds
- @context.Leeches
- @context.Downloads
- @context.Message
-
-
\ No newline at end of file
+
+ Add trackers
+ @if (ContextMenuItem is not null)
+ {
+ Edit tracker URL
+ Remove tracker
+ Copy tracker url
+ }
+
+
+
\ No newline at end of file
diff --git a/Lantean.QBTMudBlade/Components/TrackersTab.razor.cs b/Lantean.QBTMudBlade/Components/TrackersTab.razor.cs
index b6853d0..adb9696 100644
--- a/Lantean.QBTMudBlade/Components/TrackersTab.razor.cs
+++ b/Lantean.QBTMudBlade/Components/TrackersTab.razor.cs
@@ -1,7 +1,10 @@
using Lantean.QBitTorrentClient;
using Lantean.QBitTorrentClient.Models;
+using Lantean.QBTMudBlade.Interop;
using Lantean.QBTMudBlade.Services;
using Microsoft.AspNetCore.Components;
+using Microsoft.JSInterop;
+using MudBlazor;
using System.Net;
namespace Lantean.QBTMudBlade.Components
@@ -11,6 +14,9 @@ namespace Lantean.QBTMudBlade.Components
private readonly CancellationTokenSource _timerCancellationToken = new();
private bool _disposedValue;
+ private string? _sortColumn;
+ private SortDirection _sortDirection;
+
[Parameter, EditorRequired]
public string? Hash { get; set; }
@@ -20,13 +26,25 @@ namespace Lantean.QBTMudBlade.Components
[CascadingParameter(Name = "RefreshInterval")]
public int RefreshInterval { get; set; }
+ [Inject]
+ protected IDialogService DialogService { get; set; } = default!;
+
+ [Inject]
+ protected IJSRuntime JSRuntime { get; set; } = default!;
+
[Inject]
protected IApiClient ApiClient { get; set; } = default!;
[Inject]
protected IDataManager DataManager { get; set; } = default!;
- protected IReadOnlyList? Trackers { get; set; }
+ protected IReadOnlyList? TrackerList { get; set; }
+
+ protected IEnumerable? Trackers => GetTrackers();
+
+ protected TorrentTracker? ContextMenuItem { get; set; }
+
+ protected ContextMenu? ContextMenu { get; set; }
protected override async Task OnParametersSetAsync()
{
@@ -40,11 +58,125 @@ namespace Lantean.QBTMudBlade.Components
return;
}
- Trackers = await ApiClient.GetTorrentTrackers(Hash);
+ TrackerList = await ApiClient.GetTorrentTrackers(Hash);
await InvokeAsync(StateHasChanged);
}
+ protected IEnumerable? GetTrackers()
+ {
+ if (TrackerList is null)
+ {
+ return null;
+ }
+
+ var trackers = TrackerList.Where(t => !IsRealTracker(t)).ToList();
+ trackers.AddRange(TrackerList.Where(IsRealTracker).OrderByDirection(_sortDirection, GetSortSelector()));
+
+ return trackers.AsReadOnly();
+ }
+
+ private static bool IsRealTracker(TorrentTracker torrentTracker)
+ {
+ return !torrentTracker.Url.StartsWith("**");
+ }
+
+ private Func GetSortSelector()
+ {
+ var sortSelector = ColumnsDefinitions.Find(c => c.Id == _sortColumn)?.SortSelector;
+
+ return sortSelector ?? (i => i.Url);
+ }
+
+ protected void SortDirectionChanged(SortDirection sortDirection)
+ {
+ _sortDirection = sortDirection;
+
+ StateHasChanged();
+ }
+
+ protected void SortColumnChanged(string column)
+ {
+ _sortColumn = column;
+
+ StateHasChanged();
+ }
+
+ protected Task TableDataContextMenu(TableDataContextMenuEventArgs eventArgs)
+ {
+ return ShowContextMenu(eventArgs.Item, eventArgs.MouseEventArgs);
+ }
+
+ protected Task TableDataLongPress(TableDataLongPressEventArgs eventArgs)
+ {
+ return ShowContextMenu(eventArgs.Item, eventArgs.LongPressEventArgs);
+ }
+
+ protected async Task ShowContextMenu(TorrentTracker? tracker, EventArgs eventArgs)
+ {
+ if (tracker is not null && IsRealTracker(tracker))
+ {
+ ContextMenuItem = tracker;
+ }
+ else
+ {
+ ContextMenuItem = null;
+ }
+
+ if (ContextMenu is null)
+ {
+ return;
+ }
+
+ await ContextMenu.ToggleMenuAsync(eventArgs);
+ }
+
+ protected async Task AddTracker()
+ {
+ if (Hash is null)
+ {
+ return;
+ }
+
+ var trackers = await DialogService.ShowAddTrackersDialog();
+ if (trackers is null || trackers.Count == 0)
+ {
+ return;
+ }
+
+ await ApiClient.AddTrackersToTorrent(Hash, trackers);
+ }
+
+ protected async Task EditTracker()
+ {
+ 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));
+ }
+
+ protected async Task RemoveTracker()
+ {
+ if (Hash is null || ContextMenuItem is null)
+ {
+ return;
+ }
+
+ await ApiClient.RemoveTrackers(Hash, [ContextMenuItem.Url]);
+ }
+
+ protected async Task CopyTrackerUrl()
+ {
+ if (Hash is null || ContextMenuItem is null)
+ {
+ return;
+ }
+
+ await JSRuntime.WriteToClipboard(ContextMenuItem.Url);
+ }
+
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
@@ -57,7 +189,7 @@ namespace Lantean.QBTMudBlade.Components
{
try
{
- Trackers = await ApiClient.GetTorrentTrackers(Hash);
+ TrackerList = await ApiClient.GetTorrentTrackers(Hash);
}
catch (HttpRequestException exception) when (exception.StatusCode == HttpStatusCode.Forbidden || exception.StatusCode == HttpStatusCode.NotFound)
{
@@ -72,6 +204,20 @@ namespace Lantean.QBTMudBlade.Components
}
}
+ protected IEnumerable> Columns => ColumnsDefinitions;
+
+ public static List> ColumnsDefinitions { get; } =
+ [
+ new ColumnDefinition("Tier", w => w.Tier, w => w.Tier > 0 ? w.Tier.ToString() : ""),
+ new ColumnDefinition("URL", w => w.Url),
+ new ColumnDefinition("Status", w => w.Status),
+ new ColumnDefinition("Peers", w => w.Peers),
+ new ColumnDefinition("Seeds", w => w.Seeds),
+ new ColumnDefinition("Leeches", w => w.Leeches),
+ new ColumnDefinition("Times Downloaded", w => w.Downloads),
+ new ColumnDefinition("Message", w => w.Message),
+ ];
+
protected virtual async Task DisposeAsync(bool disposing)
{
if (!_disposedValue)
diff --git a/Lantean.QBTMudBlade/Components/WebSeedsTab.razor b/Lantean.QBTMudBlade/Components/WebSeedsTab.razor
index b4b0a50..7af257a 100644
--- a/Lantean.QBTMudBlade/Components/WebSeedsTab.razor
+++ b/Lantean.QBTMudBlade/Components/WebSeedsTab.razor
@@ -1,23 +1,5 @@
-
-
- URL
-
-
- @context.Url
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/Lantean.QBTMudBlade/Components/WebSeedsTab.razor.cs b/Lantean.QBTMudBlade/Components/WebSeedsTab.razor.cs
index 7bfe56e..c704519 100644
--- a/Lantean.QBTMudBlade/Components/WebSeedsTab.razor.cs
+++ b/Lantean.QBTMudBlade/Components/WebSeedsTab.razor.cs
@@ -89,5 +89,12 @@ namespace Lantean.QBTMudBlade.Components
await InvokeAsync(StateHasChanged);
}
+
+ protected IEnumerable> Columns => ColumnsDefinitions;
+
+ public static List> ColumnsDefinitions { get; } =
+ [
+ new ColumnDefinition("URL", w => w.Url, w => w.Url),
+ ];
}
}
\ No newline at end of file
diff --git a/Lantean.QBTMudBlade/DialogHelper.cs b/Lantean.QBTMudBlade/DialogHelper.cs
index 7b88200..d11082c 100644
--- a/Lantean.QBTMudBlade/DialogHelper.cs
+++ b/Lantean.QBTMudBlade/DialogHelper.cs
@@ -157,7 +157,7 @@ namespace Lantean.QBTMudBlade
return updatedCategory.Name;
}
- public static async Task?> ShowAddTagsDialog(this IDialogService dialogService, IApiClient apiClient)
+ public static async Task?> ShowAddTagsDialog(this IDialogService dialogService)
{
var reference = await dialogService.ShowAsync("Add Tags", NonBlurFormDialogOptions);
var dialogResult = await reference.Result;
@@ -172,6 +172,21 @@ namespace Lantean.QBTMudBlade
return tags;
}
+ public static async Task?> ShowAddTrackersDialog(this IDialogService dialogService)
+ {
+ var reference = await dialogService.ShowAsync("Add Tags", NonBlurFormDialogOptions);
+ var dialogResult = await reference.Result;
+
+ if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null)
+ {
+ return null;
+ }
+
+ var tags = (HashSet)dialogResult.Data;
+
+ return tags;
+ }
+
public static async Task ShowConfirmDialog(this IDialogService dialogService, string title, string content)
{
var parameters = new DialogParameters
diff --git a/Lantean.QBTMudBlade/Pages/Blocks.razor b/Lantean.QBTMudBlade/Pages/Blocks.razor
index 1526a42..e798d1d 100644
--- a/Lantean.QBTMudBlade/Pages/Blocks.razor
+++ b/Lantean.QBTMudBlade/Pages/Blocks.razor
@@ -16,7 +16,7 @@
-
+
Filter
diff --git a/Lantean.QBTMudBlade/Pages/Log.razor b/Lantean.QBTMudBlade/Pages/Log.razor
index 8c3cd69..679a857 100644
--- a/Lantean.QBTMudBlade/Pages/Log.razor
+++ b/Lantean.QBTMudBlade/Pages/Log.razor
@@ -16,10 +16,10 @@
-
+
-
+
Normal
Info
Warning
diff --git a/Lantean.QBTMudBlade/Pages/Search.razor b/Lantean.QBTMudBlade/Pages/Search.razor
index caa3369..b692771 100644
--- a/Lantean.QBTMudBlade/Pages/Search.razor
+++ b/Lantean.QBTMudBlade/Pages/Search.razor
@@ -16,10 +16,10 @@
-
+
-
+
@foreach (var (value, name) in Categories)
{
@name
@@ -31,7 +31,7 @@
-
+
All
@foreach (var (value, name) in Plugins)
diff --git a/Lantean.QBTMudBlade/Pages/TorrentList.razor b/Lantean.QBTMudBlade/Pages/TorrentList.razor
index 947871d..9c56783 100644
--- a/Lantean.QBTMudBlade/Pages/TorrentList.razor
+++ b/Lantean.QBTMudBlade/Pages/TorrentList.razor
@@ -13,8 +13,6 @@
-@* *@
-
diff --git a/Lantean.QBTMudBlade/Pages/TorrentList.razor.cs b/Lantean.QBTMudBlade/Pages/TorrentList.razor.cs
index dac7fe9..26707a9 100644
--- a/Lantean.QBTMudBlade/Pages/TorrentList.razor.cs
+++ b/Lantean.QBTMudBlade/Pages/TorrentList.razor.cs
@@ -49,8 +49,6 @@ namespace Lantean.QBTMudBlade.Pages
protected DynamicTable? Table { get; set; }
- protected TorrentActions? ContextMenuActions { get; set; }
-
protected Torrent? ContextMenuItem { get; set; }
protected ContextMenu? ContextMenu { get; set; }
@@ -137,62 +135,11 @@ namespace Lantean.QBTMudBlade.Pages
protected Task TableDataContextMenu(TableDataContextMenuEventArgs eventArgs)
{
return ShowContextMenu(eventArgs.Item, eventArgs.MouseEventArgs);
- //return ShowContextMenu(eventArgs.Item, eventArgs.MouseEventArgs.ClientX, eventArgs.MouseEventArgs.ClientY);
}
protected Task TableDataLongPress(TableDataLongPressEventArgs eventArgs)
{
return ShowContextMenu(eventArgs.Item, eventArgs.LongPressEventArgs);
- //return ShowContextMenu(eventArgs.Item, eventArgs.LongPressEventArgs.ClientX, eventArgs.LongPressEventArgs.ClientY);
- }
-
- protected async Task ShowContextMenu(Torrent? torrent, double x, double y)
- {
- if (torrent is not null)
- {
- ContextMenuItem = torrent;
- }
-
- await JSRuntime.ClearSelection();
- if (ContextMenuActions is null || ContextMenuActions.ActionsMenu is null)
- {
- return;
- }
-
- int? maxHeight = null;
-
- var mainContentSize = await JSRuntime.GetInnerDimensions(".mud-main-content");
- var contextMenuHeight = ContextMenuActions.CalculateMenuHeight();
-
- // the bottom position of the window will be rendered off screen
- if ((y - 64 + contextMenuHeight) >= (mainContentSize.Height))
- {
- // adjust the top of the context menu
- var overshoot = Math.Abs(mainContentSize.Height - (y + contextMenuHeight));
- y -= overshoot;
- if (y < 70)
- {
- y = 70;
- }
-
- if ((y - 64 + contextMenuHeight) >= mainContentSize.Height)
- {
- maxHeight = (int)mainContentSize.Height - (int)y + 64;
- }
- }
-
-#pragma warning disable BL0005 // Component parameter should not be set outside of its component.
- ContextMenuActions.ActionsMenu.MaxHeight = maxHeight;
-#pragma warning restore BL0005 // Component parameter should not be set outside of its component.
-
- // emulate mouseeventargs for MudBlazor
- var mouseEventArgs = new MouseEventArgs
- {
- OffsetX = x,
- OffsetY = y,
- };
-
- await ContextMenuActions.ActionsMenu.OpenMenuAsync(mouseEventArgs);
}
protected async Task ShowContextMenu(Torrent? torrent, EventArgs eventArgs)
diff --git a/Lantean.QBTMudBlade/Program.cs b/Lantean.QBTMudBlade/Program.cs
index 430d7b7..5711545 100644
--- a/Lantean.QBTMudBlade/Program.cs
+++ b/Lantean.QBTMudBlade/Program.cs
@@ -3,6 +3,7 @@ using Lantean.QBitTorrentClient;
using Lantean.QBTMudBlade.Services;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
+using MudBlazor;
using MudBlazor.Services;
namespace Lantean.QBTMudBlade
@@ -48,6 +49,7 @@ namespace Lantean.QBTMudBlade
builder.Logging.SetMinimumLevel(LogLevel.Error);
#endif
+ MudGlobal.InputDefaults.ShrinkLabel = true;
await builder.Build().RunAsync();
}
}
diff --git a/Lantean.QBTMudBlade/Services/DataManager.cs b/Lantean.QBTMudBlade/Services/DataManager.cs
index a1e107d..95f21bc 100644
--- a/Lantean.QBTMudBlade/Services/DataManager.cs
+++ b/Lantean.QBTMudBlade/Services/DataManager.cs
@@ -110,31 +110,31 @@ namespace Lantean.QBTMudBlade.Services
return new ServerState();
}
return new ServerState(
- serverState.AllTimeDownloaded!.Value,
- serverState.AllTimeUploaded!.Value,
- serverState.AverageTimeQueue!.Value,
+ serverState.AllTimeDownloaded.GetValueOrDefault(),
+ serverState.AllTimeUploaded.GetValueOrDefault(),
+ serverState.AverageTimeQueue.GetValueOrDefault(),
serverState.ConnectionStatus!,
- serverState.DHTNodes!.Value,
- serverState.DownloadInfoData!.Value,
- serverState.DownloadInfoSpeed!.Value,
- serverState.DownloadRateLimit!.Value,
- serverState.FreeSpaceOnDisk!.Value,
- serverState.GlobalRatio!.Value,
- serverState.QueuedIOJobs!.Value,
- serverState.Queuing!.Value,
- serverState.ReadCacheHits!.Value,
- serverState.ReadCacheOverload!.Value,
- serverState.RefreshInterval!.Value,
- serverState.TotalBuffersSize!.Value,
- serverState.TotalPeerConnections!.Value,
- serverState.TotalQueuedSize!.Value,
- serverState.TotalWastedSession!.Value,
- serverState.UploadInfoData!.Value,
- serverState.UploadInfoSpeed!.Value,
- serverState.UploadRateLimit!.Value,
- serverState.UseAltSpeedLimits!.Value,
- serverState.UseSubcategories!.Value,
- serverState.WriteCacheOverload!.Value);
+ serverState.DHTNodes.GetValueOrDefault(),
+ serverState.DownloadInfoData.GetValueOrDefault(),
+ serverState.DownloadInfoSpeed.GetValueOrDefault(),
+ serverState.DownloadRateLimit.GetValueOrDefault(),
+ serverState.FreeSpaceOnDisk.GetValueOrDefault(),
+ serverState.GlobalRatio.GetValueOrDefault(),
+ serverState.QueuedIOJobs.GetValueOrDefault(),
+ serverState.Queuing.GetValueOrDefault(),
+ serverState.ReadCacheHits.GetValueOrDefault(),
+ serverState.ReadCacheOverload.GetValueOrDefault(),
+ serverState.RefreshInterval.GetValueOrDefault(),
+ serverState.TotalBuffersSize.GetValueOrDefault(),
+ serverState.TotalPeerConnections.GetValueOrDefault(),
+ serverState.TotalQueuedSize.GetValueOrDefault(),
+ serverState.TotalWastedSession.GetValueOrDefault(),
+ serverState.UploadInfoData.GetValueOrDefault(),
+ serverState.UploadInfoSpeed.GetValueOrDefault(),
+ serverState.UploadRateLimit.GetValueOrDefault(),
+ serverState.UseAltSpeedLimits.GetValueOrDefault(),
+ serverState.UseSubcategories.GetValueOrDefault(),
+ serverState.WriteCacheOverload.GetValueOrDefault());
}
public void MergeMainData(QBitTorrentClient.Models.MainData mainData, MainData torrentList)
@@ -469,69 +469,69 @@ namespace Lantean.QBTMudBlade.Services
peer.Connection!,
peer.Country,
peer.CountryCode,
- peer.Downloaded!.Value,
- peer.DownloadSpeed!.Value,
+ peer.Downloaded.GetValueOrDefault(),
+ peer.DownloadSpeed.GetValueOrDefault(),
peer.Files!,
peer.Flags!,
peer.FlagsDescription!,
peer.IPAddress!,
- peer.Port!.Value,
- peer.Progress!.Value,
- peer.Relevance!.Value,
- peer.Uploaded!.Value,
- peer.UploadSpeed!.Value);
+ peer.Port.GetValueOrDefault(),
+ peer.Progress.GetValueOrDefault(),
+ peer.Relevance.GetValueOrDefault(),
+ peer.Uploaded.GetValueOrDefault(),
+ peer.UploadSpeed.GetValueOrDefault());
}
public Torrent CreateTorrent(string hash, QBitTorrentClient.Models.Torrent torrent)
{
return new Torrent(
hash,
- torrent.AddedOn!.Value,
- torrent.AmountLeft!.Value,
- torrent.AutomaticTorrentManagement!.Value,
- torrent.Availability!.Value,
+ torrent.AddedOn.GetValueOrDefault(),
+ torrent.AmountLeft.GetValueOrDefault(),
+ torrent.AutomaticTorrentManagement.GetValueOrDefault(),
+ torrent.Availability.GetValueOrDefault(),
torrent.Category!,
- torrent.Completed!.Value,
- torrent.CompletionOn!.Value,
+ torrent.Completed.GetValueOrDefault(),
+ torrent.CompletionOn.GetValueOrDefault(),
torrent.ContentPath!,
- torrent.DownloadLimit!.Value,
- torrent.DownloadSpeed!.Value,
- torrent.Downloaded!.Value,
- torrent.DownloadedSession!.Value,
- torrent.EstimatedTimeOfArrival!.Value,
- torrent.FirstLastPiecePriority!.Value,
- torrent.ForceStart!.Value,
+ torrent.DownloadLimit.GetValueOrDefault(),
+ torrent.DownloadSpeed.GetValueOrDefault(),
+ torrent.Downloaded.GetValueOrDefault(),
+ torrent.DownloadedSession.GetValueOrDefault(),
+ torrent.EstimatedTimeOfArrival.GetValueOrDefault(),
+ torrent.FirstLastPiecePriority.GetValueOrDefault(),
+ torrent.ForceStart.GetValueOrDefault(),
torrent.InfoHashV1!,
torrent.InfoHashV2!,
- torrent.LastActivity!.Value,
+ torrent.LastActivity.GetValueOrDefault(),
torrent.MagnetUri!,
- torrent.MaxRatio!.Value,
- torrent.MaxSeedingTime!.Value,
+ torrent.MaxRatio.GetValueOrDefault(),
+ torrent.MaxSeedingTime.GetValueOrDefault(),
torrent.Name!,
- torrent.NumberComplete!.Value,
- torrent.NumberIncomplete!.Value,
- torrent.NumberLeeches!.Value,
- torrent.NumberSeeds!.Value,
- torrent.Priority!.Value,
- torrent.Progress!.Value,
- torrent.Ratio!.Value,
- torrent.RatioLimit!.Value,
+ torrent.NumberComplete.GetValueOrDefault(),
+ torrent.NumberIncomplete.GetValueOrDefault(),
+ torrent.NumberLeeches.GetValueOrDefault(),
+ torrent.NumberSeeds.GetValueOrDefault(),
+ torrent.Priority.GetValueOrDefault(),
+ torrent.Progress.GetValueOrDefault(),
+ torrent.Ratio.GetValueOrDefault(),
+ torrent.RatioLimit.GetValueOrDefault(),
torrent.SavePath!,
- torrent.SeedingTime!.Value,
- torrent.SeedingTimeLimit!.Value,
- torrent.SeenComplete!.Value,
- torrent.SequentialDownload!.Value,
- torrent.Size!.Value,
+ torrent.SeedingTime.GetValueOrDefault(),
+ torrent.SeedingTimeLimit.GetValueOrDefault(),
+ torrent.SeenComplete.GetValueOrDefault(),
+ torrent.SequentialDownload.GetValueOrDefault(),
+ torrent.Size.GetValueOrDefault(),
torrent.State!,
- torrent.SuperSeeding!.Value,
+ torrent.SuperSeeding.GetValueOrDefault(),
torrent.Tags!,
- torrent.TimeActive!.Value,
- torrent.TotalSize!.Value,
+ torrent.TimeActive.GetValueOrDefault(),
+ torrent.TotalSize.GetValueOrDefault(),
torrent.Tracker!,
- torrent.UploadLimit!.Value,
- torrent.Uploaded!.Value,
- torrent.UploadedSession!.Value,
- torrent.UploadSpeed!.Value,
+ torrent.UploadLimit.GetValueOrDefault(),
+ torrent.Uploaded.GetValueOrDefault(),
+ torrent.UploadedSession.GetValueOrDefault(),
+ torrent.UploadSpeed.GetValueOrDefault(),
torrent.Reannounce ?? 0);
}
diff --git a/Lantean.QBTMudBlade/readme.md b/Lantean.QBTMudBlade/readme.md
index aa36f62..82908d1 100644
--- a/Lantean.QBTMudBlade/readme.md
+++ b/Lantean.QBTMudBlade/readme.md
@@ -5,14 +5,10 @@
- Rename multiple files dialog
- RSS feeds and dialogs
- About
-- Context Menu component
-- Context menu for files list, filter (categories, tags & trackers)
+- Context menu for files list/trackers list/peers list
- Tag management page
- Category management page
- Update all tables to use DynamicTable
- - WebSeeds
- - Trackers
- - Peers
- Log
- Blocks
- Search
\ No newline at end of file
diff --git a/Lantean.QBitTorrentClient/ApiClient.cs b/Lantean.QBitTorrentClient/ApiClient.cs
index 26c421f..abaf799 100644
--- a/Lantean.QBitTorrentClient/ApiClient.cs
+++ b/Lantean.QBitTorrentClient/ApiClient.cs
@@ -357,13 +357,13 @@ namespace Lantean.QBitTorrentClient
return await GetJson(response.Content);
}
- public async Task> GetTorrentTrackers(string hash)
+ public async Task> GetTorrentTrackers(string hash)
{
var response = await _httpClient.GetAsync($"torrents/trackers?hash={hash}");
response.EnsureSuccessStatusCode();
- return await GetJsonList(response.Content);
+ return await GetJsonList(response.Content);
}
public async Task> GetTorrentWebSeeds(string hash)
diff --git a/Lantean.QBitTorrentClient/IApiClient.cs b/Lantean.QBitTorrentClient/IApiClient.cs
index 20aed84..e8c0bdd 100644
--- a/Lantean.QBitTorrentClient/IApiClient.cs
+++ b/Lantean.QBitTorrentClient/IApiClient.cs
@@ -78,7 +78,7 @@ namespace Lantean.QBitTorrentClient
Task GetTorrentProperties(string hash);
- Task> GetTorrentTrackers(string hash);
+ Task> GetTorrentTrackers(string hash);
Task> GetTorrentWebSeeds(string hash);
diff --git a/Lantean.QBitTorrentClient/MockApiClient.cs b/Lantean.QBitTorrentClient/MockApiClient.cs
index 7a30615..a587fef 100644
--- a/Lantean.QBitTorrentClient/MockApiClient.cs
+++ b/Lantean.QBitTorrentClient/MockApiClient.cs
@@ -209,7 +209,7 @@ namespace Lantean.QBitTorrentClient
return _apiClient.GetTorrentProperties(hash);
}
- public Task> GetTorrentTrackers(string hash)
+ public Task> GetTorrentTrackers(string hash)
{
return _apiClient.GetTorrentTrackers(hash);
}
diff --git a/Lantean.QBitTorrentClient/Models/TorrentTrackers.cs b/Lantean.QBitTorrentClient/Models/TorrentTracker.cs
similarity index 94%
rename from Lantean.QBitTorrentClient/Models/TorrentTrackers.cs
rename to Lantean.QBitTorrentClient/Models/TorrentTracker.cs
index e065a9a..bc6824a 100644
--- a/Lantean.QBitTorrentClient/Models/TorrentTrackers.cs
+++ b/Lantean.QBitTorrentClient/Models/TorrentTracker.cs
@@ -2,10 +2,10 @@
namespace Lantean.QBitTorrentClient.Models
{
- public record TorrentTrackers
+ public record TorrentTracker
{
[JsonConstructor]
- public TorrentTrackers(
+ public TorrentTracker(
string url,
TrackerStatus status,
int tier,