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; - + } 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 @@ + + +
@tag
+ + + + + + @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,