mirror of
				https://github.com/lantean-code/qbtmud.git
				synced 2025-11-04 05:53:22 +00:00 
			
		
		
		
	Compare commits
	
		
			11 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					a29e64fc1b | ||
| 
						 | 
					e55955c75e | ||
| 
						 | 
					aa80396862 | ||
| 
						 | 
					30ced3293c | ||
| 
						 | 
					c54f73a517 | ||
| 
						 | 
					bad509e40f | ||
| 
						 | 
					6a0796ef20 | ||
| 
						 | 
					dc4b515763 | ||
| 
						 | 
					938702a7b3 | ||
| 
						 | 
					6ca1c6edd4 | ||
| 
						 | 
					24eb5cf5e9 | 
							
								
								
									
										4
									
								
								.github/workflows/dotnet.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/dotnet.yml
									
									
									
									
										vendored
									
									
								
							@@ -21,12 +21,12 @@ jobs:
 | 
				
			|||||||
      - name: Setup .NET
 | 
					      - name: Setup .NET
 | 
				
			||||||
        uses: actions/setup-dotnet@v4
 | 
					        uses: actions/setup-dotnet@v4
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          dotnet-version: '8.0.x'
 | 
					          dotnet-version: '9.0.x'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Install GitVersion
 | 
					      - name: Install GitVersion
 | 
				
			||||||
        uses: gittools/actions/gitversion/setup@v3.0.0
 | 
					        uses: gittools/actions/gitversion/setup@v3.0.0
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          versionSpec: '6.x'
 | 
					          versionSpec: '6.0.0'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Determine Version
 | 
					      - name: Determine Version
 | 
				
			||||||
        id: gitversion
 | 
					        id: gitversion
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
					<Project Sdk="Microsoft.NET.Sdk">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <PropertyGroup>
 | 
					  <PropertyGroup>
 | 
				
			||||||
    <TargetFramework>net8.0</TargetFramework>
 | 
					    <TargetFramework>net9.0</TargetFramework>
 | 
				
			||||||
    <ImplicitUsings>enable</ImplicitUsings>
 | 
					    <ImplicitUsings>enable</ImplicitUsings>
 | 
				
			||||||
    <Nullable>enable</Nullable>
 | 
					    <Nullable>enable</Nullable>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -10,11 +10,14 @@
 | 
				
			|||||||
  </PropertyGroup>
 | 
					  </PropertyGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <ItemGroup>
 | 
					  <ItemGroup>
 | 
				
			||||||
    <PackageReference Include="FluentAssertions" Version="6.12.1" />
 | 
						<PackageReference Include="FluentAssertions" Version="7.1.0" AllowedVersions="[5.0.0,7.*.*)" />
 | 
				
			||||||
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
 | 
					    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.1" />
 | 
				
			||||||
 | 
					    <PackageReference Include="Microsoft.Extensions.Http" Version="9.0.1" />
 | 
				
			||||||
 | 
					    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
 | 
				
			||||||
 | 
					    <PackageReference Include="MudBlazor" Version="8.2.0" />
 | 
				
			||||||
    <PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
 | 
					    <PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
 | 
				
			||||||
    <PackageReference Include="xunit" Version="2.9.2" />
 | 
					    <PackageReference Include="xunit" Version="2.9.3" />
 | 
				
			||||||
    <PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
 | 
					    <PackageReference Include="xunit.runner.visualstudio" Version="3.0.1">
 | 
				
			||||||
      <PrivateAssets>all</PrivateAssets>
 | 
					      <PrivateAssets>all</PrivateAssets>
 | 
				
			||||||
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
 | 
					      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
 | 
				
			||||||
    </PackageReference>
 | 
					    </PackageReference>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,7 @@ namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			|||||||
    public partial class AddPeerDialog
 | 
					    public partial class AddPeerDialog
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [CascadingParameter]
 | 
					        [CascadingParameter]
 | 
				
			||||||
        public MudDialogInstance MudDialog { get; set; } = default!;
 | 
					        public IMudDialogInstance MudDialog { get; set; } = default!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected HashSet<PeerId> Peers { get; } = [];
 | 
					        protected HashSet<PeerId> Peers { get; } = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@ namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			|||||||
        protected IDialogService DialogService { get; set; } = default!;
 | 
					        protected IDialogService DialogService { get; set; } = default!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [CascadingParameter]
 | 
					        [CascadingParameter]
 | 
				
			||||||
        public MudDialogInstance MudDialog { get; set; } = default!;
 | 
					        IMudDialogInstance MudDialog { get; set; } = default!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected HashSet<string> Tags { get; } = [];
 | 
					        protected HashSet<string> Tags { get; } = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,7 @@ namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			|||||||
    public partial class AddTorrentFileDialog
 | 
					    public partial class AddTorrentFileDialog
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [CascadingParameter]
 | 
					        [CascadingParameter]
 | 
				
			||||||
        public MudDialogInstance MudDialog { get; set; } = default!;
 | 
					        IMudDialogInstance MudDialog { get; set; } = default!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected IReadOnlyList<IBrowserFile> Files { get; set; } = [];
 | 
					        protected IReadOnlyList<IBrowserFile> Files { get; set; } = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,7 +18,7 @@ namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			|||||||
        protected IKeyboardService KeyboardService { get; set; } = default!;
 | 
					        protected IKeyboardService KeyboardService { get; set; } = default!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [CascadingParameter]
 | 
					        [CascadingParameter]
 | 
				
			||||||
        public MudDialogInstance MudDialog { get; set; } = default!;
 | 
					        IMudDialogInstance MudDialog { get; set; } = default!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Parameter]
 | 
					        [Parameter]
 | 
				
			||||||
        public string? Url { get; set; }
 | 
					        public string? Url { get; set; }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
<MudGrid>
 | 
					<MudGrid>
 | 
				
			||||||
    <MudItem xs="12">
 | 
					    <MudItem xs="12">
 | 
				
			||||||
        <MudSwitch Label="Additional Options" @bind-Value="Expanded" LabelPosition="LabelPosition.End" />
 | 
					        <MudSwitch Label="Additional Options" @bind-Value="Expanded" LabelPlacement="Placement.End" />
 | 
				
			||||||
    </MudItem>
 | 
					    </MudItem>
 | 
				
			||||||
</MudGrid>
 | 
					</MudGrid>
 | 
				
			||||||
<MudCollapse Expanded="Expanded">
 | 
					<MudCollapse Expanded="Expanded">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
using Lantean.QBitTorrentClient;
 | 
					using Lantean.QBitTorrentClient;
 | 
				
			||||||
using Lantean.QBTMud.Models;
 | 
					using Lantean.QBTMud.Models;
 | 
				
			||||||
using Microsoft.AspNetCore.Components;
 | 
					using Microsoft.AspNetCore.Components;
 | 
				
			||||||
 | 
					using MudBlazor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Lantean.QBTMud.Components.Dialogs
 | 
					namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@ namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			|||||||
    public partial class AddTrackerDialog
 | 
					    public partial class AddTrackerDialog
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [CascadingParameter]
 | 
					        [CascadingParameter]
 | 
				
			||||||
        public MudDialogInstance MudDialog { get; set; } = default!;
 | 
					        IMudDialogInstance MudDialog { get; set; } = default!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected HashSet<string> Trackers { get; } = [];
 | 
					        protected HashSet<string> Trackers { get; } = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,7 +10,7 @@ namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			|||||||
        private string _savePath = string.Empty;
 | 
					        private string _savePath = string.Empty;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [CascadingParameter]
 | 
					        [CascadingParameter]
 | 
				
			||||||
        public MudDialogInstance MudDialog { get; set; } = default!;
 | 
					        IMudDialogInstance MudDialog { get; set; } = default!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Inject]
 | 
					        [Inject]
 | 
				
			||||||
        protected IApiClient ApiClient { get; set; } = default!;
 | 
					        protected IApiClient ApiClient { get; set; } = default!;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,12 +5,13 @@
 | 
				
			|||||||
    <DialogContent>
 | 
					    <DialogContent>
 | 
				
			||||||
        <MudCard Class="w-100" Elevation="0">
 | 
					        <MudCard Class="w-100" Elevation="0">
 | 
				
			||||||
            <MudGrid>
 | 
					            <MudGrid>
 | 
				
			||||||
                @for (var i = 0; i < Columns.Count; i++)
 | 
					                @for (var i = 0; i < OrderedColumns.Length; i++)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var column = Columns[i];
 | 
					                    var item = OrderedColumns[i];
 | 
				
			||||||
 | 
					                    var column = Columns.First(c => c.Id == item);
 | 
				
			||||||
                    var index = i;
 | 
					                    var index = i;
 | 
				
			||||||
                    <MudItem xs="7">
 | 
					                    <MudItem xs="7">
 | 
				
			||||||
                            <MudCheckBox T="bool" ValueChanged="@(c => SetSelected(c, column.Id))" Label="@column.Header" LabelPosition="LabelPosition.End" Value="@(SelectedColumnsInternal.Contains(column.Id))" />
 | 
					                        <MudCheckBox T="bool" ValueChanged="@(c => SetSelected(c, column.Id))" Label="@column.Header" LabelPlacement="Placement.End" Value="@(SelectedColumnsInternal.Contains(column.Id))" />
 | 
				
			||||||
                    </MudItem>
 | 
					                    </MudItem>
 | 
				
			||||||
                    <MudItem xs="3">
 | 
					                    <MudItem xs="3">
 | 
				
			||||||
                        <MudTextField T="string" Value="@(GetValue(column.Width, column.Id))" ValueChanged="@(c => SetWidth(c, column.Id))" Label="Width" Variant="Variant.Text" HelperText="px" Adornment="Adornment.End" AdornmentIcon="@Icons.Material.Outlined.WidthNormal" OnAdornmentClick="@(c => SetWidth("auto", column.Id))" />
 | 
					                        <MudTextField T="string" Value="@(GetValue(column.Width, column.Id))" ValueChanged="@(c => SetWidth(c, column.Id))" Label="Width" Variant="Variant.Text" HelperText="px" Adornment="Adornment.End" AdornmentIcon="@Icons.Material.Outlined.WidthNormal" OnAdornmentClick="@(c => SetWidth("auto", column.Id))" />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@ namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			|||||||
    public partial class ColumnOptionsDialog<T>
 | 
					    public partial class ColumnOptionsDialog<T>
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [CascadingParameter]
 | 
					        [CascadingParameter]
 | 
				
			||||||
        public MudDialogInstance MudDialog { get; set; } = default!;
 | 
					        private IMudDialogInstance MudDialog { get; set; } = default!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Parameter]
 | 
					        [Parameter]
 | 
				
			||||||
        [EditorRequired]
 | 
					        [EditorRequired]
 | 
				
			||||||
@@ -20,10 +20,15 @@ namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			|||||||
        [Parameter]
 | 
					        [Parameter]
 | 
				
			||||||
        public Dictionary<string, int?> Widths { get; set; } = [];
 | 
					        public Dictionary<string, int?> Widths { get; set; } = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Parameter]
 | 
				
			||||||
 | 
					        public Dictionary<string, int> Order { get; set; } = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected HashSet<string> SelectedColumnsInternal { get; set; } = [];
 | 
					        protected HashSet<string> SelectedColumnsInternal { get; set; } = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected Dictionary<string, int?> WidthsInternal { get; set; } = [];
 | 
					        protected Dictionary<string, int?> WidthsInternal { get; set; } = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        protected Dictionary<string, int> OrderInternal { get; set; } = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected override void OnParametersSet()
 | 
					        protected override void OnParametersSet()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (SelectedColumnsInternal.Count == 0)
 | 
					            if (SelectedColumnsInternal.Count == 0)
 | 
				
			||||||
@@ -51,6 +56,25 @@ namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			|||||||
                    WidthsInternal[width.Key] = width.Value;
 | 
					                    WidthsInternal[width.Key] = width.Value;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (OrderInternal.Count == 0)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if (Order.Count == 0)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    for (int i = 0; i < Columns.Count; i++)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        var column = Columns[i];
 | 
				
			||||||
 | 
					                        OrderInternal.Add(column.Id, i);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    foreach (var order in Order)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        OrderInternal[order.Key] = order.Value;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected void SetSelected(bool selected, string id)
 | 
					        protected void SetSelected(bool selected, string id)
 | 
				
			||||||
@@ -101,7 +125,15 @@ namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			|||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            (Columns[index], Columns[index - 1]) = (Columns[index - 1], Columns[index]);
 | 
					            var currentId = OrderInternal.FirstOrDefault(o => o.Value == index).Key;
 | 
				
			||||||
 | 
					            var otherId = OrderInternal.FirstOrDefault(o => o.Value == index - 1).Key;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            OrderInternal[otherId] = index;
 | 
				
			||||||
 | 
					            OrderInternal[currentId] = index - 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            //(Columns[index], Columns[index - 1]) = (Columns[index - 1], Columns[index]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            StateHasChanged();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected void MoveDown(int index)
 | 
					        protected void MoveDown(int index)
 | 
				
			||||||
@@ -111,7 +143,15 @@ namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			|||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            (Columns[index], Columns[index + 1]) = (Columns[index + 1], Columns[index]);
 | 
					            var currentId = OrderInternal.FirstOrDefault(o => o.Value == index).Key;
 | 
				
			||||||
 | 
					            var otherId = OrderInternal.FirstOrDefault(o => o.Value == index + 1).Key;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            OrderInternal[otherId] = index;
 | 
				
			||||||
 | 
					            OrderInternal[currentId] = index + 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            //(Columns[index], Columns[index + 1]) = (Columns[index + 1], Columns[index]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            StateHasChanged();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected string GetValue(int? value, string columnId)
 | 
					        protected string GetValue(int? value, string columnId)
 | 
				
			||||||
@@ -134,6 +174,13 @@ namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			|||||||
            return value.Value.ToString();
 | 
					            return value.Value.ToString();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private string[] OrderedColumns => GetOrderedColumns();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private string[] GetOrderedColumns()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return OrderInternal.OrderBy(x => x.Value).Select(x => x.Key).ToArray();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected void Cancel()
 | 
					        protected void Cancel()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            MudDialog.Cancel();
 | 
					            MudDialog.Cancel();
 | 
				
			||||||
@@ -141,7 +188,7 @@ namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        protected void Submit()
 | 
					        protected void Submit()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            MudDialog.Close(DialogResult.Ok((SelectedColumnsInternal, WidthsInternal)));
 | 
					            MudDialog.Close(DialogResult.Ok((SelectedColumnsInternal, WidthsInternal, OrderInternal)));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected override Task Submit(KeyboardEvent keyboardEvent)
 | 
					        protected override Task Submit(KeyboardEvent keyboardEvent)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@ namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			|||||||
    public partial class ConfirmDialog
 | 
					    public partial class ConfirmDialog
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [CascadingParameter]
 | 
					        [CascadingParameter]
 | 
				
			||||||
        public MudDialogInstance MudDialog { get; set; } = default!;
 | 
					        IMudDialogInstance MudDialog { get; set; } = default!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Parameter]
 | 
					        [Parameter]
 | 
				
			||||||
        public string Content { get; set; } = default!;
 | 
					        public string Content { get; set; } = default!;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        <MudGrid>
 | 
					        <MudGrid>
 | 
				
			||||||
            <MudItem xs="12">
 | 
					            <MudItem xs="12">
 | 
				
			||||||
                <MudCheckBox Label="Also permanently delete the files" @bind-Value="DeleteFiles" LabelPosition="LabelPosition.End" />
 | 
					                <MudCheckBox Label="Also permanently delete the files" @bind-Value="DeleteFiles" LabelPlacement="Placement.End" />
 | 
				
			||||||
            </MudItem>
 | 
					            </MudItem>
 | 
				
			||||||
        </MudGrid>
 | 
					        </MudGrid>
 | 
				
			||||||
    </DialogContent>
 | 
					    </DialogContent>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@ namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			|||||||
    public partial class DeleteDialog
 | 
					    public partial class DeleteDialog
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [CascadingParameter]
 | 
					        [CascadingParameter]
 | 
				
			||||||
        public MudDialogInstance MudDialog { get; set; } = default!;
 | 
					        IMudDialogInstance MudDialog { get; set; } = default!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Parameter]
 | 
					        [Parameter]
 | 
				
			||||||
        public int Count { get; set; }
 | 
					        public int Count { get; set; }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@ namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			|||||||
    public partial class ExceptionDialog
 | 
					    public partial class ExceptionDialog
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [CascadingParameter]
 | 
					        [CascadingParameter]
 | 
				
			||||||
        public MudDialogInstance MudDialog { get; set; } = default!;
 | 
					        IMudDialogInstance MudDialog { get; set; } = default!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Parameter]
 | 
					        [Parameter]
 | 
				
			||||||
        public Exception? Exception { get; set; }
 | 
					        public Exception? Exception { get; set; }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,7 +11,7 @@ namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			|||||||
        private static readonly IReadOnlyList<PropertyInfo> _properties = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public);
 | 
					        private static readonly IReadOnlyList<PropertyInfo> _properties = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [CascadingParameter]
 | 
					        [CascadingParameter]
 | 
				
			||||||
        public MudDialogInstance MudDialog { get; set; } = default!;
 | 
					        IMudDialogInstance MudDialog { get; set; } = default!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected IReadOnlyList<PropertyInfo> Columns => _properties;
 | 
					        protected IReadOnlyList<PropertyInfo> Columns => _properties;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@ namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			|||||||
        protected IDialogService DialogService { get; set; } = default!;
 | 
					        protected IDialogService DialogService { get; set; } = default!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [CascadingParameter]
 | 
					        [CascadingParameter]
 | 
				
			||||||
        public MudDialogInstance MudDialog { get; set; } = default!;
 | 
					        IMudDialogInstance MudDialog { get; set; } = default!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Parameter]
 | 
					        [Parameter]
 | 
				
			||||||
        public IEnumerable<string> Hashes { get; set; } = [];
 | 
					        public IEnumerable<string> Hashes { get; set; } = [];
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@ namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			|||||||
        protected IDialogService DialogService { get; set; } = default!;
 | 
					        protected IDialogService DialogService { get; set; } = default!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [CascadingParameter]
 | 
					        [CascadingParameter]
 | 
				
			||||||
        public MudDialogInstance MudDialog { get; set; } = default!;
 | 
					        IMudDialogInstance MudDialog { get; set; } = default!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Parameter]
 | 
					        [Parameter]
 | 
				
			||||||
        public IEnumerable<string> Hashes { get; set; } = [];
 | 
					        public IEnumerable<string> Hashes { get; set; } = [];
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@ namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			|||||||
    public partial class MultipleFieldDialog
 | 
					    public partial class MultipleFieldDialog
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [CascadingParameter]
 | 
					        [CascadingParameter]
 | 
				
			||||||
        public MudDialogInstance MudDialog { get; set; } = default!;
 | 
					        IMudDialogInstance MudDialog { get; set; } = default!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Parameter]
 | 
					        [Parameter]
 | 
				
			||||||
        public string Label { get; set; } = default!;
 | 
					        public string Label { get; set; } = default!;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,7 @@ namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			|||||||
    public partial class NumericFieldDialog<T> where T : struct, INumber<T>
 | 
					    public partial class NumericFieldDialog<T> where T : struct, INumber<T>
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [CascadingParameter]
 | 
					        [CascadingParameter]
 | 
				
			||||||
        public MudDialogInstance MudDialog { get; set; } = default!;
 | 
					        IMudDialogInstance MudDialog { get; set; } = default!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Parameter]
 | 
					        [Parameter]
 | 
				
			||||||
        public string? Label { get; set; }
 | 
					        public string? Label { get; set; }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,6 @@ using Lantean.QBTMud.Services;
 | 
				
			|||||||
using Microsoft.AspNetCore.Components;
 | 
					using Microsoft.AspNetCore.Components;
 | 
				
			||||||
using MudBlazor;
 | 
					using MudBlazor;
 | 
				
			||||||
using System.Collections.ObjectModel;
 | 
					using System.Collections.ObjectModel;
 | 
				
			||||||
using static MudBlazor.Colors;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Lantean.QBTMud.Components.Dialogs
 | 
					namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -31,7 +30,7 @@ namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			|||||||
        protected ILocalStorageService LocalStorage { get; set; } = default!;
 | 
					        protected ILocalStorageService LocalStorage { get; set; } = default!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [CascadingParameter]
 | 
					        [CascadingParameter]
 | 
				
			||||||
        public MudDialogInstance MudDialog { get; set; } = default!;
 | 
					        IMudDialogInstance MudDialog { get; set; } = default!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Parameter]
 | 
					        [Parameter]
 | 
				
			||||||
        public string? Hash { get; set; }
 | 
					        public string? Hash { get; set; }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,7 +10,7 @@ namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			|||||||
        private readonly List<string> _unsavedRuleNames = [];
 | 
					        private readonly List<string> _unsavedRuleNames = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [CascadingParameter]
 | 
					        [CascadingParameter]
 | 
				
			||||||
        public MudDialogInstance MudDialog { get; set; } = default!;
 | 
					        IMudDialogInstance MudDialog { get; set; } = default!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Inject]
 | 
					        [Inject]
 | 
				
			||||||
        protected IDialogService DialogService { get; set; } = default!;
 | 
					        protected IDialogService DialogService { get; set; } = default!;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,7 @@ namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			|||||||
    public partial class ShareRatioDialog
 | 
					    public partial class ShareRatioDialog
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [CascadingParameter]
 | 
					        [CascadingParameter]
 | 
				
			||||||
        public MudDialogInstance MudDialog { get; set; } = default!;
 | 
					        IMudDialogInstance MudDialog { get; set; } = default!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Parameter]
 | 
					        [Parameter]
 | 
				
			||||||
        public string? Label { get; set; }
 | 
					        public string? Label { get; set; }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,7 @@ namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			|||||||
    public partial class SliderFieldDialog<T> where T : struct, INumber<T>
 | 
					    public partial class SliderFieldDialog<T> where T : struct, INumber<T>
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [CascadingParameter]
 | 
					        [CascadingParameter]
 | 
				
			||||||
        public MudDialogInstance MudDialog { get; set; } = default!;
 | 
					        IMudDialogInstance MudDialog { get; set; } = default!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Parameter]
 | 
					        [Parameter]
 | 
				
			||||||
        public string? Label { get; set; }
 | 
					        public string? Label { get; set; }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@ namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			|||||||
    public partial class StringFieldDialog
 | 
					    public partial class StringFieldDialog
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [CascadingParameter]
 | 
					        [CascadingParameter]
 | 
				
			||||||
        public MudDialogInstance MudDialog { get; set; } = default!;
 | 
					        IMudDialogInstance MudDialog { get; set; } = default!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Parameter]
 | 
					        [Parameter]
 | 
				
			||||||
        public string? Label { get; set; }
 | 
					        public string? Label { get; set; }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@ namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			|||||||
    public partial class SubMenuDialog
 | 
					    public partial class SubMenuDialog
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [CascadingParameter]
 | 
					        [CascadingParameter]
 | 
				
			||||||
        public MudDialogInstance MudDialog { get; set; } = default!;
 | 
					        IMudDialogInstance MudDialog { get; set; } = default!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Parameter]
 | 
					        [Parameter]
 | 
				
			||||||
        public UIAction? ParentAction { get; set; }
 | 
					        public UIAction? ParentAction { get; set; }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@ namespace Lantean.QBTMud.Components.Dialogs
 | 
				
			|||||||
    public partial class TorrentOptionsDialog
 | 
					    public partial class TorrentOptionsDialog
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        [CascadingParameter]
 | 
					        [CascadingParameter]
 | 
				
			||||||
        public MudDialogInstance MudDialog { get; set; } = default!;
 | 
					        IMudDialogInstance MudDialog { get; set; } = default!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Parameter]
 | 
					        [Parameter]
 | 
				
			||||||
        [EditorRequired]
 | 
					        [EditorRequired]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@
 | 
				
			|||||||
    <MudMenuItem Icon="@Icons.Material.Filled.DriveFileRenameOutline" OnClick="RenameFileContextMenu">Rename</MudMenuItem>
 | 
					    <MudMenuItem Icon="@Icons.Material.Filled.DriveFileRenameOutline" OnClick="RenameFileContextMenu">Rename</MudMenuItem>
 | 
				
			||||||
</ContextMenu>
 | 
					</ContextMenu>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div style="overflow-x: auto; white-space: nowrap; width: 100%;">
 | 
				
			||||||
<MudToolBar Gutters="false" Dense="true">
 | 
					<MudToolBar Gutters="false" Dense="true">
 | 
				
			||||||
    <MudIconButton Icon="@Icons.Material.Filled.DriveFileRenameOutline" OnClick="RenameFileToolbar" title="Rename" />
 | 
					    <MudIconButton Icon="@Icons.Material.Filled.DriveFileRenameOutline" OnClick="RenameFileToolbar" title="Rename" />
 | 
				
			||||||
    <MudDivider Vertical="true" />
 | 
					    <MudDivider Vertical="true" />
 | 
				
			||||||
@@ -22,6 +23,7 @@
 | 
				
			|||||||
    <MudSpacer />
 | 
					    <MudSpacer />
 | 
				
			||||||
    <MudTextField T="string" Value="SearchText" ValueChanged="SearchTextChanged" Immediate="true" DebounceInterval="500" Placeholder="Filter file list" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0"></MudTextField>
 | 
					    <MudTextField T="string" Value="SearchText" ValueChanged="SearchTextChanged" Immediate="true" DebounceInterval="500" Placeholder="Filter file list" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0"></MudTextField>
 | 
				
			||||||
</MudToolBar>
 | 
					</MudToolBar>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<DynamicTable
 | 
					<DynamicTable
 | 
				
			||||||
    @ref="Table"
 | 
					    @ref="Table"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -191,13 +191,13 @@ else if (RenderType == RenderType.MenuItems)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                if (!action.Children.Any())
 | 
					                if (!action.Children.Any())
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    <MudMenuItem Icon="@action.Icon" IconColor="action.Color" OnClick="action.Callback" Disabled="Disabled">
 | 
					                    <MudMenuItem Icon="@action.Icon" IconColor="action.Color" OnClick="action.Callback" Disabled="Disabled" Class="icon-menu-dense">
 | 
				
			||||||
                        @action.Text
 | 
					                        @action.Text
 | 
				
			||||||
                    </MudMenuItem>
 | 
					                    </MudMenuItem>
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                else
 | 
					                else
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    <MudMenuItem Icon="@action.Icon" IconColor="action.Color" OnClick="@(t => SubMenuTouch(action))">
 | 
					                    <MudMenuItem Icon="@action.Icon" IconColor="action.Color" OnClick="@(t => SubMenuTouch(action))" Class="icon-menu-dense">
 | 
				
			||||||
                        <MudMenu ListClass="unselectable" Dense="true" AnchorOrigin="Origin.TopRight" TransformOrigin="Origin.TopLeft" ActivationEvent="MouseEvent.MouseOver" Icon="@Icons.Material.Filled.ArrowDropDown" Ripple="false" Class="sub-menu">
 | 
					                        <MudMenu ListClass="unselectable" Dense="true" AnchorOrigin="Origin.TopRight" TransformOrigin="Origin.TopLeft" ActivationEvent="MouseEvent.MouseOver" Icon="@Icons.Material.Filled.ArrowDropDown" Ripple="false" Class="sub-menu">
 | 
				
			||||||
                            <ActivatorContent>
 | 
					                            <ActivatorContent>
 | 
				
			||||||
                                @action.Text
 | 
					                                @action.Text
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -63,7 +63,7 @@ namespace Lantean.QBTMud.Components
 | 
				
			|||||||
        public QBitTorrentClient.Models.Preferences? Preferences { get; set; }
 | 
					        public QBitTorrentClient.Models.Preferences? Preferences { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Parameter]
 | 
					        [Parameter]
 | 
				
			||||||
        public MudDialogInstance? MudDialog { get; set; }
 | 
					        public IMudDialogInstance? MudDialog { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Parameter]
 | 
					        [Parameter]
 | 
				
			||||||
        public UIAction? ParentAction { get; set; }
 | 
					        public UIAction? ParentAction { get; set; }
 | 
				
			||||||
@@ -104,7 +104,7 @@ namespace Lantean.QBTMud.Components
 | 
				
			|||||||
            _actions =
 | 
					            _actions =
 | 
				
			||||||
            [
 | 
					            [
 | 
				
			||||||
                new("start", "Start", Icons.Material.Filled.PlayArrow, Color.Success, CreateCallback(Resume)),
 | 
					                new("start", "Start", Icons.Material.Filled.PlayArrow, Color.Success, CreateCallback(Resume)),
 | 
				
			||||||
                new("pause", "Pause", Icons.Material.Filled.Pause, Color.Warning, CreateCallback(Pause)),
 | 
					                new("pause", "Pause", MajorVersion < 5 ? Icons.Material.Filled.Pause : Icons.Material.Filled.Stop, Color.Warning, CreateCallback(Pause)),
 | 
				
			||||||
                new("forceStart", "Force start", Icons.Material.Filled.Forward, Color.Warning, CreateCallback(ForceStart)),
 | 
					                new("forceStart", "Force start", Icons.Material.Filled.Forward, Color.Warning, CreateCallback(ForceStart)),
 | 
				
			||||||
                new("delete", "Remove", Icons.Material.Filled.Delete, Color.Error, CreateCallback(Remove), separatorBefore: true),
 | 
					                new("delete", "Remove", Icons.Material.Filled.Delete, Color.Error, CreateCallback(Remove), separatorBefore: true),
 | 
				
			||||||
                new("setLocation", "Set location", Icons.Material.Filled.MyLocation, Color.Info, CreateCallback(SetLocation), separatorBefore: true),
 | 
					                new("setLocation", "Set location", Icons.Material.Filled.MyLocation, Color.Info, CreateCallback(SetLocation), separatorBefore: true),
 | 
				
			||||||
@@ -441,7 +441,7 @@ namespace Lantean.QBTMud.Components
 | 
				
			|||||||
                    thereAreFirstLastPiecePrio = true;
 | 
					                    thereAreFirstLastPiecePrio = true;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (torrent.Progress != 1.0) // not downloaded
 | 
					                if (torrent.Progress > 0.999999) // not downloaded
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    allAreDownloaded = false;
 | 
					                    allAreDownloaded = false;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,17 +8,15 @@
 | 
				
			|||||||
            Class="unselectable"
 | 
					            Class="unselectable"
 | 
				
			||||||
            MaxHeight="@MaxHeight"
 | 
					            MaxHeight="@MaxHeight"
 | 
				
			||||||
            AnchorOrigin="@AnchorOrigin"
 | 
					            AnchorOrigin="@AnchorOrigin"
 | 
				
			||||||
            TransformOrigin="TransformOrigin"
 | 
					            TransformOrigin="@TransformOrigin"
 | 
				
			||||||
            RelativeWidth="@FullWidth"
 | 
					            RelativeWidth="@RelativeWidth"
 | 
				
			||||||
            OverflowBehavior="OverflowBehavior.FlipAlways"
 | 
					            OverflowBehavior="OverflowBehavior.FlipAlways"
 | 
				
			||||||
            Style="@_popoverStyle"
 | 
					            Style="@_popoverStyle"
 | 
				
			||||||
            @ontouchend:preventDefault>
 | 
					            @ontouchend:preventDefault>
 | 
				
			||||||
    <CascadingValue Value="@(FakeMenu)">
 | 
					    <CascadingValue Value="@(FakeMenu)">
 | 
				
			||||||
        @if (_showChildren)
 | 
					        @if (_showChildren)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            <MudList T="object"
 | 
					            <MudList T="object" Class="unselectable"  Dense="@Dense">
 | 
				
			||||||
            Class="unselectable"
 | 
					 | 
				
			||||||
                 Dense="@Dense">
 | 
					 | 
				
			||||||
                @ChildContent
 | 
					                @ChildContent
 | 
				
			||||||
            </MudList>
 | 
					            </MudList>
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,12 +7,6 @@ using MudBlazor.Utilities;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace Lantean.QBTMud.Components.UI
 | 
					namespace Lantean.QBTMud.Components.UI
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    // This is a very hacky approach but works for now.
 | 
					 | 
				
			||||||
    // This needs to inherit from MudMenu because MudMenuItem needs a MudMenu passed to it to control the close of the menu when an item is clicked.
 | 
					 | 
				
			||||||
    // MudPopover isn't ideal for this because that is designed to be used relative to an activator which in these cases it isn't.
 | 
					 | 
				
			||||||
    // Ideally this should be changed to use something like the way the DialogService works.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Or - rework this to have a hidden MudMenu and hook into the OpenChanged event to monitor when the MudMenuItem closes it.
 | 
					 | 
				
			||||||
    public partial class ContextMenu : MudComponentBase
 | 
					    public partial class ContextMenu : MudComponentBase
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private bool _open;
 | 
					        private bool _open;
 | 
				
			||||||
@@ -61,7 +55,7 @@ namespace Lantean.QBTMud.Components.UI
 | 
				
			|||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
        [Parameter]
 | 
					        [Parameter]
 | 
				
			||||||
        [Category(CategoryTypes.Menu.PopupAppearance)]
 | 
					        [Category(CategoryTypes.Menu.PopupAppearance)]
 | 
				
			||||||
        public bool FullWidth { get; set; }
 | 
					        public DropdownWidth RelativeWidth { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// Sets the max height the menu can have when open.
 | 
					        /// Sets the max height the menu can have when open.
 | 
				
			||||||
@@ -219,56 +213,58 @@ namespace Lantean.QBTMud.Components.UI
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected override async Task OnAfterRenderAsync(bool firstRender)
 | 
					        protected override Task OnAfterRenderAsync(bool firstRender)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (!_isResized)
 | 
					            if (!_isResized)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await DeterminePosition();
 | 
					                //await DeterminePosition();
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private async Task DeterminePosition()
 | 
					            return Task.CompletedTask;
 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var mainContentSize = await JSRuntime.GetInnerDimensions(".mud-main-content");
 | 
					 | 
				
			||||||
            double? contextMenuHeight = null;
 | 
					 | 
				
			||||||
            double? contextMenuWidth = null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var popoverHolder = PopoverService.ActivePopovers.FirstOrDefault(p => p.UserAttributes.ContainsKey("tracker") && (string?)p.UserAttributes["tracker"] == Id);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var popoverSize = await JSRuntime.GetBoundingClientRect($"#popovercontent-{popoverHolder?.Id}");
 | 
					 | 
				
			||||||
            if (popoverSize.Height > 0)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                contextMenuHeight = popoverSize.Height;
 | 
					 | 
				
			||||||
                contextMenuWidth = popoverSize.Width;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // the bottom position of the popover will be rendered off screen
 | 
					        //private async Task DeterminePosition()
 | 
				
			||||||
            if (_y - _diff + contextMenuHeight.Value >= mainContentSize.Height)
 | 
					        //{
 | 
				
			||||||
            {
 | 
					        //    var mainContentSize = await JSRuntime.GetInnerDimensions(".mud-main-content");
 | 
				
			||||||
                // adjust the top of the context menu
 | 
					        //    double? contextMenuHeight = null;
 | 
				
			||||||
                var overshoot = Math.Abs(mainContentSize.Height - (_y - _diff + contextMenuHeight.Value));
 | 
					        //    double? contextMenuWidth = null;
 | 
				
			||||||
                _y -= overshoot;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (_y - _diff + contextMenuHeight >= mainContentSize.Height)
 | 
					        //    var popoverHolder = PopoverService.ActivePopovers.FirstOrDefault(p => p.UserAttributes.ContainsKey("tracker") && (string?)p.UserAttributes["tracker"] == Id);
 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    MaxHeight = (int)(mainContentSize.Height - _y + _diff);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (_x + contextMenuWidth.Value > mainContentSize.Width)
 | 
					        //    var popoverSize = await JSRuntime.GetBoundingClientRect($"#popovercontent-{popoverHolder?.Id}");
 | 
				
			||||||
            {
 | 
					        //    if (popoverSize.Height > 0)
 | 
				
			||||||
                var overshoot = Math.Abs(mainContentSize.Width - (_x + contextMenuWidth.Value));
 | 
					        //    {
 | 
				
			||||||
                _x -= overshoot;
 | 
					        //        contextMenuHeight = popoverSize.Height;
 | 
				
			||||||
            }
 | 
					        //        contextMenuWidth = popoverSize.Width;
 | 
				
			||||||
 | 
					        //    }
 | 
				
			||||||
 | 
					        //    else
 | 
				
			||||||
 | 
					        //    {
 | 
				
			||||||
 | 
					        //        return;
 | 
				
			||||||
 | 
					        //    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            SetPopoverStyle(_x, _y);
 | 
					        //    // the bottom position of the popover will be rendered off screen
 | 
				
			||||||
            _isResized = true;
 | 
					        //    if (_y - _diff + contextMenuHeight.Value >= mainContentSize.Height)
 | 
				
			||||||
            await InvokeAsync(StateHasChanged);
 | 
					        //    {
 | 
				
			||||||
        }
 | 
					        //        // adjust the top of the context menu
 | 
				
			||||||
 | 
					        //        var overshoot = Math.Abs(mainContentSize.Height - (_y - _diff + contextMenuHeight.Value));
 | 
				
			||||||
 | 
					        //        _y -= overshoot;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //        if (_y - _diff + contextMenuHeight >= mainContentSize.Height)
 | 
				
			||||||
 | 
					        //        {
 | 
				
			||||||
 | 
					        //            MaxHeight = (int)(mainContentSize.Height - _y + _diff);
 | 
				
			||||||
 | 
					        //        }
 | 
				
			||||||
 | 
					        //    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //    if (_x + contextMenuWidth.Value > mainContentSize.Width)
 | 
				
			||||||
 | 
					        //    {
 | 
				
			||||||
 | 
					        //        var overshoot = Math.Abs(mainContentSize.Width - (_x + contextMenuWidth.Value));
 | 
				
			||||||
 | 
					        //        _x -= overshoot;
 | 
				
			||||||
 | 
					        //    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //    SetPopoverStyle(_x, _y);
 | 
				
			||||||
 | 
					        //    _isResized = true;
 | 
				
			||||||
 | 
					        //    await InvokeAsync(StateHasChanged);
 | 
				
			||||||
 | 
					        //}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private (double x, double y) GetPositionFromArgs(EventArgs eventArgs)
 | 
					        private (double x, double y) GetPositionFromArgs(EventArgs eventArgs)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
<div class="@Classname">
 | 
					<div class="@Classname">
 | 
				
			||||||
    <div @onclick="EventUtil.AsNonRenderingEventHandler<MouseEventArgs>(OnClickHandler)" class="@LinkClassname" @onlongpress="OnLongPressInternal" @oncontextmenu="OnContextMenuInternal" @oncontextmenu:preventDefault>
 | 
					    <div @onclick="this.AsNonRenderingEventHandler<MouseEventArgs>(OnClickHandler)" class="@LinkClassname" @onlongpress="OnLongPressInternal" @oncontextmenu="OnContextMenuInternal" @oncontextmenu:preventDefault>
 | 
				
			||||||
        @if (!string.IsNullOrEmpty(Icon))
 | 
					        @if (!string.IsNullOrEmpty(Icon))
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            <MudIcon Icon="@Icon" Color="@IconColor" Class="@IconClassname" />
 | 
					            <MudIcon Icon="@Icon" Color="@IconColor" Class="@IconClassname" />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,7 @@ namespace Lantean.QBTMud.Components.UI
 | 
				
			|||||||
        private readonly string _columnSelectionStorageKey = $"DynamicTable{_typeName}.ColumnSelection";
 | 
					        private readonly string _columnSelectionStorageKey = $"DynamicTable{_typeName}.ColumnSelection";
 | 
				
			||||||
        private readonly string _columnSortStorageKey = $"DynamicTable{_typeName}.ColumnSort";
 | 
					        private readonly string _columnSortStorageKey = $"DynamicTable{_typeName}.ColumnSort";
 | 
				
			||||||
        private readonly string _columnWidthsStorageKey = $"DynamicTable{_typeName}.ColumnWidths";
 | 
					        private readonly string _columnWidthsStorageKey = $"DynamicTable{_typeName}.ColumnWidths";
 | 
				
			||||||
 | 
					        private readonly string _columnOrderStorageKey = $"DynamicTable{_typeName}.ColumnOrder";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Inject]
 | 
					        [Inject]
 | 
				
			||||||
        public ILocalStorageService LocalStorage { get; set; } = default!;
 | 
					        public ILocalStorageService LocalStorage { get; set; } = default!;
 | 
				
			||||||
@@ -82,6 +83,8 @@ namespace Lantean.QBTMud.Components.UI
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private Dictionary<string, int?> _columnWidths = [];
 | 
					        private Dictionary<string, int?> _columnWidths = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private Dictionary<string, int> _columnOrder = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private string? _sortColumn;
 | 
					        private string? _sortColumn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private SortDirection _sortDirection;
 | 
					        private SortDirection _sortDirection;
 | 
				
			||||||
@@ -165,6 +168,8 @@ namespace Lantean.QBTMud.Components.UI
 | 
				
			|||||||
        protected IEnumerable<ColumnDefinition<T>> GetColumns()
 | 
					        protected IEnumerable<ColumnDefinition<T>> GetColumns()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var filteredColumns = ColumnDefinitions.Where(c => SelectedColumns.Contains(c.Id)).Where(ColumnFilter);
 | 
					            var filteredColumns = ColumnDefinitions.Where(c => SelectedColumns.Contains(c.Id)).Where(ColumnFilter);
 | 
				
			||||||
 | 
					            if (_columnOrder.Count == 0)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
                foreach (var column in filteredColumns)
 | 
					                foreach (var column in filteredColumns)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    if (_columnWidths.TryGetValue(column.Id, out var value))
 | 
					                    if (_columnWidths.TryGetValue(column.Id, out var value))
 | 
				
			||||||
@@ -174,6 +179,25 @@ namespace Lantean.QBTMud.Components.UI
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    yield return column;
 | 
					                    yield return column;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                yield break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var columnDictionary = filteredColumns.ToDictionary(c => c.Id);
 | 
				
			||||||
 | 
					            foreach (var columnId in _columnOrder.OrderBy(c => c.Value).Select(c => c.Key))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if (!columnDictionary.TryGetValue(columnId, out var column))
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (_columnWidths.TryGetValue(column.Id, out var value))
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    column.Width = value;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                yield return column;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private async Task SetSort(string columnId, SortDirection sortDirection)
 | 
					        private async Task SetSort(string columnId, SortDirection sortDirection)
 | 
				
			||||||
@@ -280,7 +304,7 @@ namespace Lantean.QBTMud.Components.UI
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public async Task ShowColumnOptionsDialog()
 | 
					        public async Task ShowColumnOptionsDialog()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var result = await DialogService.ShowColumnsOptionsDialog(ColumnDefinitions.Where(ColumnFilter).ToList(), SelectedColumns, _columnWidths);
 | 
					            var result = await DialogService.ShowColumnsOptionsDialog(ColumnDefinitions.Where(ColumnFilter).ToList(), SelectedColumns, _columnWidths, _columnOrder);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (result == default)
 | 
					            if (result == default)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@@ -299,11 +323,17 @@ namespace Lantean.QBTMud.Components.UI
 | 
				
			|||||||
                _columnWidths = result.ColumnWidths;
 | 
					                _columnWidths = result.ColumnWidths;
 | 
				
			||||||
                await LocalStorage.SetItemAsync(_columnWidthsStorageKey, _columnWidths);
 | 
					                await LocalStorage.SetItemAsync(_columnWidthsStorageKey, _columnWidths);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!DictionaryEqual(_columnOrder, result.ColumnOrder))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                _columnOrder = result.ColumnOrder;
 | 
				
			||||||
 | 
					                await LocalStorage.SetItemAsync(_columnOrderStorageKey, _columnOrder);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static bool DictionaryEqual(Dictionary<string, int?> left, Dictionary<string, int?> right)
 | 
					        private static bool DictionaryEqual<TKey, TValue>(Dictionary<TKey, TValue> left, Dictionary<TKey, TValue> right) where TKey : notnull
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return left.Keys.Count == right.Keys.Count && left.Keys.All(k => right.ContainsKey(k) && left[k] == right[k]);
 | 
					            return left.Keys.Count == right.Keys.Count && left.Keys.All(k => right.ContainsKey(k) && Equals(left[k], right[k]));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static string? GetColumnStyle(ColumnDefinition<T> column)
 | 
					        private static string? GetColumnStyle(ColumnDefinition<T> column)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -328,13 +328,14 @@ namespace Lantean.QBTMud.Helpers
 | 
				
			|||||||
            return tags;
 | 
					            return tags;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static async Task<(HashSet<string> SelectedColumns, Dictionary<string, int?> ColumnWidths)> ShowColumnsOptionsDialog<T>(this IDialogService dialogService, List<ColumnDefinition<T>> columnDefinitions, HashSet<string> selectedColumns, Dictionary<string, int?> widths)
 | 
					        public static async Task<(HashSet<string> SelectedColumns, Dictionary<string, int?> ColumnWidths, Dictionary<string, int> ColumnOrder)> ShowColumnsOptionsDialog<T>(this IDialogService dialogService, List<ColumnDefinition<T>> columnDefinitions, HashSet<string> selectedColumns, Dictionary<string, int?> widths, Dictionary<string, int> order)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var parameters = new DialogParameters
 | 
					            var parameters = new DialogParameters
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                { nameof(ColumnOptionsDialog<T>.Columns), columnDefinitions },
 | 
					                { nameof(ColumnOptionsDialog<T>.Columns), columnDefinitions },
 | 
				
			||||||
                { nameof(ColumnOptionsDialog<T>.SelectedColumns), selectedColumns },
 | 
					                { nameof(ColumnOptionsDialog<T>.SelectedColumns), selectedColumns },
 | 
				
			||||||
                { nameof(ColumnOptionsDialog<T>.Widths), widths },
 | 
					                { nameof(ColumnOptionsDialog<T>.Widths), widths },
 | 
				
			||||||
 | 
					                { nameof(ColumnOptionsDialog<T>.Order), order },
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var reference = await dialogService.ShowAsync<ColumnOptionsDialog<T>>("Column Options", parameters, FormDialogOptions);
 | 
					            var reference = await dialogService.ShowAsync<ColumnOptionsDialog<T>>("Column Options", parameters, FormDialogOptions);
 | 
				
			||||||
@@ -344,7 +345,7 @@ namespace Lantean.QBTMud.Helpers
 | 
				
			|||||||
                return default;
 | 
					                return default;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return ((HashSet<string>, Dictionary<string, int?>))dialogResult.Data;
 | 
					            return ((HashSet<string>, Dictionary<string, int?>, Dictionary<string, int>))dialogResult.Data;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static async Task<bool> ShowConfirmDialog(this IDialogService dialogService, string title, string content)
 | 
					        public static async Task<bool> ShowConfirmDialog(this IDialogService dialogService, string title, string content)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
 | 
					<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <PropertyGroup>
 | 
					  <PropertyGroup>
 | 
				
			||||||
    <TargetFramework>net8.0</TargetFramework>
 | 
					    <TargetFramework>net9.0</TargetFramework>
 | 
				
			||||||
    <Nullable>enable</Nullable>
 | 
					    <Nullable>enable</Nullable>
 | 
				
			||||||
    <ImplicitUsings>enable</ImplicitUsings>
 | 
					    <ImplicitUsings>enable</ImplicitUsings>
 | 
				
			||||||
	<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
 | 
						<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
 | 
				
			||||||
@@ -12,13 +12,13 @@
 | 
				
			|||||||
  <ItemGroup>
 | 
					  <ItemGroup>
 | 
				
			||||||
	<PackageReference Include="Blazored.LocalStorage" Version="4.5.0" />
 | 
						<PackageReference Include="Blazored.LocalStorage" Version="4.5.0" />
 | 
				
			||||||
	<PackageReference Include="ByteSize" Version="2.1.2" />
 | 
						<PackageReference Include="ByteSize" Version="2.1.2" />
 | 
				
			||||||
	<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.10" />
 | 
						<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.1" />
 | 
				
			||||||
	<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.10" PrivateAssets="all" />
 | 
						<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.1" PrivateAssets="all" />
 | 
				
			||||||
	<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.1" />
 | 
						<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.1" />
 | 
				
			||||||
	<PackageReference Include="MudBlazor" Version="7.15.0" />
 | 
						<PackageReference Include="MudBlazor" Version="8.2.0" />
 | 
				
			||||||
	<PackageReference Include="MudBlazor.ThemeManager" Version="2.1.0" />
 | 
						<PackageReference Include="MudBlazor.ThemeManager" Version="3.0.0" />
 | 
				
			||||||
    <!-- added to fix vuln in dependency -->
 | 
					    <!-- added to fix vuln in dependency -->
 | 
				
			||||||
	<PackageReference Include="System.Text.Json" Version="8.0.5" />
 | 
						<PackageReference Include="System.Text.Json" Version="9.0.1" />
 | 
				
			||||||
  </ItemGroup>
 | 
					  </ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <ItemGroup>
 | 
					  <ItemGroup>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,23 +36,23 @@
 | 
				
			|||||||
                </CascadingValue>
 | 
					                </CascadingValue>
 | 
				
			||||||
            </CascadingValue>
 | 
					            </CascadingValue>
 | 
				
			||||||
        </CascadingValue>
 | 
					        </CascadingValue>
 | 
				
			||||||
        <MudAppBar Bottom="true" Fixed="true" Elevation="0" Dense="true" Style="background-color: var(--mud-palette-dark-lighten);">
 | 
					        <MudAppBar Bottom="true" Fixed="true" Elevation="0" Dense="true" Style="background-color: var(--mud-palette-dark-lighten); z-index: 900">
 | 
				
			||||||
            @if (MainData?.LostConnection == true)
 | 
					            @if (MainData?.LostConnection == true)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                <MudText Class="mx-2 mb-1" Color="Color.Error">qBittorrent client is not reachable</MudText>
 | 
					                <MudText Class="mx-2 mb-1 d-none d-sm-flex" Color="Color.Error">qBittorrent client is not reachable</MudText>
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            <MudSpacer />
 | 
					            <MudSpacer />
 | 
				
			||||||
            <MudText Class="mx-2 mb-1">@DisplayHelpers.Size(MainData?.ServerState.FreeSpaceOnDisk, "Free space: ")</MudText>
 | 
					            <MudText Class="mx-2 mb-1 d-none d-sm-flex">@DisplayHelpers.Size(MainData?.ServerState.FreeSpaceOnDisk, "Free space: ")</MudText>
 | 
				
			||||||
            <MudDivider Vertical="true" />
 | 
					            <MudDivider Vertical="true" Class="d-none d-sm-flex" />
 | 
				
			||||||
            <MudText Class="mx-2 mb-1">DHT @(MainData?.ServerState.DHTNodes ?? 0) nodes</MudText>
 | 
					            <MudText Class="mx-2 mb-1 d-none d-sm-flex">DHT @(MainData?.ServerState.DHTNodes ?? 0) nodes</MudText>
 | 
				
			||||||
            <MudDivider Vertical="true" />
 | 
					            <MudDivider Vertical="true" Class="d-none d-sm-flex" />
 | 
				
			||||||
            @{
 | 
					            @{
 | 
				
			||||||
                var (icon, colour) = GetConnectionIcon(MainData?.ServerState.ConnectionStatus);
 | 
					                var (icon, colour) = GetConnectionIcon(MainData?.ServerState.ConnectionStatus);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            <MudIcon Class="mx-1 mb-1" Icon="@icon" Color="@colour" Title="MainData?.ServerState.ConnectionStatus" />
 | 
					            <MudIcon Class="mx-1 mb-1" Icon="@icon" Color="@colour" Title="MainData?.ServerState.ConnectionStatus" />
 | 
				
			||||||
            <MudDivider Vertical="true" />
 | 
					            <MudDivider Vertical="true" Class="" />
 | 
				
			||||||
            <MudIcon Class="mx-1 mb-1" Icon="@Icons.Material.Outlined.Speed" Color="@((MainData?.ServerState.UseAltSpeedLimits ?? false) ? Color.Error : Color.Success)" />
 | 
					            <MudIcon Class="mx-1 mb-1" Icon="@Icons.Material.Outlined.Speed" Color="@((MainData?.ServerState.UseAltSpeedLimits ?? false) ? Color.Error : Color.Success)" />
 | 
				
			||||||
            <MudDivider Vertical="true" />
 | 
					            <MudDivider Vertical="true" Class="" />
 | 
				
			||||||
            <MudIcon Class="ml-1 mb-1" Icon="@Icons.Material.Filled.KeyboardDoubleArrowDown" Color="Color.Success" />
 | 
					            <MudIcon Class="ml-1 mb-1" Icon="@Icons.Material.Filled.KeyboardDoubleArrowDown" Color="Color.Success" />
 | 
				
			||||||
            <MudText Class="mr-1 mb-1">
 | 
					            <MudText Class="mr-1 mb-1">
 | 
				
			||||||
                @DisplayHelpers.Size(MainData?.ServerState.DownloadInfoSpeed, null, "/s")
 | 
					                @DisplayHelpers.Size(MainData?.ServerState.DownloadInfoSpeed, null, "/s")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,19 +20,21 @@
 | 
				
			|||||||
                        <MudIconButton Icon="@Icons.Material.Filled.Error" Color="Color.Default" OnClick="ToggleErrorDrawer" />
 | 
					                        <MudIconButton Icon="@Icons.Material.Filled.Error" Color="Color.Default" OnClick="ToggleErrorDrawer" />
 | 
				
			||||||
                    </MudBadge>
 | 
					                    </MudBadge>
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                <MudSwitch T="bool" Label="Dark Mode" LabelPosition="LabelPosition.End" Value="IsDarkMode" ValueChanged="DarkModeChanged" Class="pl-3" />
 | 
					                <MudSwitch T="bool" Label="Dark Mode" LabelPlacement="Placement.End" Value="IsDarkMode" ValueChanged="DarkModeChanged" Class="pl-3" />
 | 
				
			||||||
                <Menu @ref="Menu" />
 | 
					                <Menu @ref="Menu" />
 | 
				
			||||||
            </MudAppBar>
 | 
					            </MudAppBar>
 | 
				
			||||||
            <MudDrawer Open="ErrorDrawerOpen" ClipMode="DrawerClipMode.Docked" Elevation="2" Anchor="Anchor.Right">
 | 
					            <MudDrawer @bind-Open="ErrorDrawerOpen" ClipMode="DrawerClipMode.Docked" Elevation="2" Anchor="Anchor.Right">
 | 
				
			||||||
                <ErrorDisplay ErrorBoundary="ErrorBoundary" />
 | 
					                <ErrorDisplay ErrorBoundary="ErrorBoundary" />
 | 
				
			||||||
            </MudDrawer>
 | 
					            </MudDrawer>
 | 
				
			||||||
            <CascadingValue Value="Theme">
 | 
					            <CascadingValue Value="Theme">
 | 
				
			||||||
                <CascadingValue Value="IsDarkMode" Name="IsDarkMode">
 | 
					                <CascadingValue Value="IsDarkMode" Name="IsDarkMode">
 | 
				
			||||||
                    <CascadingValue Value="Menu">
 | 
					                    <CascadingValue Value="Menu">
 | 
				
			||||||
 | 
					                        <CascadingValue Value="DrawerOpen" Name="DrawerOpen">
 | 
				
			||||||
                            @Body
 | 
					                            @Body
 | 
				
			||||||
                        </CascadingValue>
 | 
					                        </CascadingValue>
 | 
				
			||||||
                    </CascadingValue>
 | 
					                    </CascadingValue>
 | 
				
			||||||
                </CascadingValue>
 | 
					                </CascadingValue>
 | 
				
			||||||
 | 
					            </CascadingValue>
 | 
				
			||||||
        </MudLayout>
 | 
					        </MudLayout>
 | 
				
			||||||
    </EnhancedErrorBoundary>
 | 
					    </EnhancedErrorBoundary>
 | 
				
			||||||
</CascadingValue>
 | 
					</CascadingValue>
 | 
				
			||||||
@@ -90,9 +90,9 @@ namespace Lantean.QBTMud.Layout
 | 
				
			|||||||
            return Task.CompletedTask;
 | 
					            return Task.CompletedTask;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public Task NotifyBrowserViewportChangeAsync(BrowserViewportEventArgs browserViewportEventArgs)
 | 
					        public async Task NotifyBrowserViewportChangeAsync(BrowserViewportEventArgs browserViewportEventArgs)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (browserViewportEventArgs.Breakpoint == Breakpoint.Sm && DrawerOpen)
 | 
					            if (browserViewportEventArgs.Breakpoint <= Breakpoint.Sm)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                DrawerOpen = false;
 | 
					                DrawerOpen = false;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -101,7 +101,17 @@ namespace Lantean.QBTMud.Layout
 | 
				
			|||||||
                DrawerOpen = true;
 | 
					                DrawerOpen = true;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return Task.CompletedTask;
 | 
					            if (ErrorBoundary?.Errors.Count > 0)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ErrorDrawerOpen = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                await Task.Delay(250);
 | 
				
			||||||
 | 
					                ErrorDrawerOpen = false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await InvokeAsync(StateHasChanged);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected void ToggleErrorDrawer()
 | 
					        protected void ToggleErrorDrawer()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,89 +12,96 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<MudTabs Elevation="2" ApplyEffectsToContainer="true">
 | 
					<MudTabs Elevation="2" ApplyEffectsToContainer="true">
 | 
				
			||||||
    <MudTabPanel Text="About">
 | 
					    <MudTabPanel Text="About">
 | 
				
			||||||
        <div class="d-flex gap-4">
 | 
					        <MudContainer MaxWidth="MaxWidth.ExtraExtraLarge" Class="mt-3">
 | 
				
			||||||
            <MudImage Src="images/mascot.png" Alt="Mascot" Class="ma-6" Fluid ObjectFit="ObjectFit.None" ObjectPosition="ObjectPosition.LeftTop" Height="162" Width="94" />
 | 
					            <MudGrid Class="mt-0 mb-4">
 | 
				
			||||||
            <MudGrid Class="mx-0 mt-0 mb-3">
 | 
					                <MudItem xs="12" sm="3" md="2" lg="2" xl="1" Class="d-flex justify-center">
 | 
				
			||||||
                <MudItem xs="12">
 | 
					                    <MudImage Src="images/mascot.png" Alt="Mascot" Class="ma-6"
 | 
				
			||||||
                    <div class="d-flex gap-3">
 | 
					                              Fluid ObjectFit="ObjectFit.None" ObjectPosition="ObjectPosition.LeftTop"
 | 
				
			||||||
                        <MudImage Src="images/qbittorrent32.png" Fluid ObjectFit="ObjectFit.None" Alt="QBT" Height="32" Width="32"  /><MudText Typo="Typo.h6">qBittorrent @QBittorrentVersion</MudText>
 | 
					                              Height="162" Width="94" />
 | 
				
			||||||
 | 
					                </MudItem>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <MudItem xs="12" sm="9" md="10" lg="10" xl="11">
 | 
				
			||||||
 | 
					                    <div class="d-flex flex-column gap-2">
 | 
				
			||||||
 | 
					                        <div class="d-flex gap-3 align-items-center">
 | 
				
			||||||
 | 
					                            <MudImage Src="images/qbittorrent32.png" Fluid ObjectFit="ObjectFit.None"
 | 
				
			||||||
 | 
					                                      Alt="QBT" Height="32" Width="32" />
 | 
				
			||||||
 | 
					                            <MudText Typo="Typo.h6">qBittorrent @QBittorrentVersion</MudText>
 | 
				
			||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                </MudItem>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <MudItem xs="12">
 | 
					                        <MudText Typo="Typo.body1">
 | 
				
			||||||
                    <MudText Typo="Typo.body1">An advanced BitTorrent client programmed in C++, based on Qt toolkit and libtorrent-rasterbar.</MudText>
 | 
					                            An advanced BitTorrent client programmed in C++, based on Qt toolkit and libtorrent-rasterbar.
 | 
				
			||||||
                </MudItem>
 | 
					                        </MudText>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <MudItem xs="12">
 | 
					 | 
				
			||||||
                        <MudText Typo="Typo.body1">Copyright © 2006-2024 The qBittorrent project</MudText>
 | 
					                        <MudText Typo="Typo.body1">Copyright © 2006-2024 The qBittorrent project</MudText>
 | 
				
			||||||
                </MudItem>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <MudItem xs="2">
 | 
					                        <div class="d-flex flex-wrap">
 | 
				
			||||||
                    <MudText Typo="Typo.body1">Home Page</MudText>
 | 
					                            <MudText Typo="Typo.body1" Class="fw-bold">Home Page: </MudText>
 | 
				
			||||||
                </MudItem>
 | 
					                            <MudLink Href="https://www.qbittorrent.org" Target="_blank" Class="ms-2">
 | 
				
			||||||
                <MudItem xs="10">
 | 
					                                qbittorrent.org
 | 
				
			||||||
                    <MudLink Href="https://www.qbittorrent.org" Target="https://www.qbittorrent.org">https://www.qbittorrent.org</MudLink>
 | 
					                            </MudLink>
 | 
				
			||||||
                </MudItem>
 | 
					                        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <MudItem xs="2">
 | 
					                        <div class="d-flex flex-wrap">
 | 
				
			||||||
                    <MudText Typo="Typo.body1">Bug Tracker</MudText>
 | 
					                            <MudText Typo="Typo.body1" Class="fw-bold">Bug Tracker: </MudText>
 | 
				
			||||||
                </MudItem>
 | 
					                            <MudLink Href="https://bugs.qbittorrent.org" Target="_blank" Class="ms-2">
 | 
				
			||||||
                <MudItem xs="10">
 | 
					                                bugs.qbittorrent.org
 | 
				
			||||||
                    <MudLink Href="https://bugs.qbittorrent.org" Target="https://bugs.qbittorrent.org">https://bugs.qbittorrent.org</MudLink>
 | 
					                            </MudLink>
 | 
				
			||||||
                </MudItem>
 | 
					                        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <MudItem xs="2">
 | 
					                        <div class="d-flex flex-wrap">
 | 
				
			||||||
                    <MudText Typo="Typo.body1">Forum</MudText>
 | 
					                            <MudText Typo="Typo.body1" Class="fw-bold">Forum: </MudText>
 | 
				
			||||||
                </MudItem>
 | 
					                            <MudLink Href="https://forum.qbittorrent.org" Target="_blank" Class="ms-2">
 | 
				
			||||||
                <MudItem xs="10">
 | 
					                                forum.qbittorrent.org
 | 
				
			||||||
                    <MudLink Href="https://forum.qbittorrent.org" Target="https://forum.qbittorrent.org">https://forum.qbittorrent.org</MudLink>
 | 
					                            </MudLink>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
                </MudItem>
 | 
					                </MudItem>
 | 
				
			||||||
            </MudGrid>
 | 
					            </MudGrid>
 | 
				
			||||||
        </div>
 | 
					        </MudContainer>
 | 
				
			||||||
    </MudTabPanel>
 | 
					    </MudTabPanel>
 | 
				
			||||||
    <MudTabPanel Text="Authors">
 | 
					    <MudTabPanel Text="Authors">
 | 
				
			||||||
        <MudContainer MaxWidth="MaxWidth.ExtraExtraLarge" Class="mt-3">
 | 
					        <MudContainer MaxWidth="MaxWidth.ExtraExtraLarge" Class="mt-3">
 | 
				
			||||||
            <MudText Typo="Typo.body1" Class="py-1">Current maintainer</MudText>
 | 
					            <MudText Typo="Typo.h5" Class="py-1">Current maintainer</MudText>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <MudGrid Class="mt-0 mb-4">
 | 
					            <MudGrid Class="mt-0 mb-4">
 | 
				
			||||||
                <MudItem xs="2">
 | 
					                <MudItem xs="12" md="2">
 | 
				
			||||||
                    <MudText Typo="Typo.body1">Name</MudText>
 | 
					                    <MudText Typo="Typo.h6">Name</MudText>
 | 
				
			||||||
                </MudItem>
 | 
					                </MudItem>
 | 
				
			||||||
                <MudItem xs="10">
 | 
					                <MudItem xs="12" md="10">
 | 
				
			||||||
                    <MudText Typo="Typo.body1">Sledgehammer999</MudText>
 | 
					                    <MudText Typo="Typo.body1">Sledgehammer999</MudText>
 | 
				
			||||||
                </MudItem>
 | 
					                </MudItem>
 | 
				
			||||||
                <MudItem xs="2">
 | 
					                <MudItem xs="12" md="2">
 | 
				
			||||||
                    <MudText Typo="Typo.body1">Nationality</MudText>
 | 
					                    <MudText Typo="Typo.h6">Nationality</MudText>
 | 
				
			||||||
                </MudItem>
 | 
					                </MudItem>
 | 
				
			||||||
                <MudItem xs="10">
 | 
					                <MudItem xs="12" md="10">
 | 
				
			||||||
                    <MudText Typo="Typo.body1">Greece</MudText>
 | 
					                    <MudText Typo="Typo.body1">Greece</MudText>
 | 
				
			||||||
                </MudItem>
 | 
					                </MudItem>
 | 
				
			||||||
                <MudItem xs="2">
 | 
					                <MudItem xs="12" md="2">
 | 
				
			||||||
                    <MudText Typo="Typo.body1">E-mail</MudText>
 | 
					                    <MudText Typo="Typo.h6">E-mail</MudText>
 | 
				
			||||||
                </MudItem>
 | 
					                </MudItem>
 | 
				
			||||||
                <MudItem xs="10">
 | 
					                <MudItem xs="12" md="10">
 | 
				
			||||||
                    <MudLink Href="mailto:sledgehammer999@qbittorrent.org">sledgehammer999@qbittorrent.org</MudLink>
 | 
					                    <MudLink Href="mailto:sledgehammer999@qbittorrent.org">sledgehammer999@qbittorrent.org</MudLink>
 | 
				
			||||||
                </MudItem>
 | 
					                </MudItem>
 | 
				
			||||||
            </MudGrid>
 | 
					            </MudGrid>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <MudText Typo="Typo.body1" Class="py-1">Original author</MudText>
 | 
					            <MudText Typo="Typo.h5" Class="py-1">Original author</MudText>
 | 
				
			||||||
            <MudGrid Class="mt-0 mb-4">
 | 
					            <MudGrid Class="mt-0 mb-4">
 | 
				
			||||||
                <MudItem xs="2">
 | 
					                <MudItem xs="12" md="2">
 | 
				
			||||||
                    <MudText Typo="Typo.body1">Name</MudText>
 | 
					                    <MudText Typo="Typo.h6">Name</MudText>
 | 
				
			||||||
                </MudItem>
 | 
					                </MudItem>
 | 
				
			||||||
                <MudItem xs="10">
 | 
					                <MudItem xs="12" md="10">
 | 
				
			||||||
                    <MudText Typo="Typo.body1">Christophe Dumez</MudText>
 | 
					                    <MudText Typo="Typo.body1">Christophe Dumez</MudText>
 | 
				
			||||||
                </MudItem>
 | 
					                </MudItem>
 | 
				
			||||||
                <MudItem xs="2">
 | 
					                <MudItem xs="12" md="2">
 | 
				
			||||||
                    <MudText Typo="Typo.body1">Nationality</MudText>
 | 
					                    <MudText Typo="Typo.h6">Nationality</MudText>
 | 
				
			||||||
                </MudItem>
 | 
					                </MudItem>
 | 
				
			||||||
                <MudItem xs="10">
 | 
					                <MudItem xs="12" md="10">
 | 
				
			||||||
                    <MudText Typo="Typo.body1">France</MudText>
 | 
					                    <MudText Typo="Typo.body1">France</MudText>
 | 
				
			||||||
                </MudItem>
 | 
					                </MudItem>
 | 
				
			||||||
                <MudItem xs="2">
 | 
					                <MudItem xs="12" md="2">
 | 
				
			||||||
                    <MudText Typo="Typo.body1">E-mail</MudText>
 | 
					                    <MudText Typo="Typo.h6">E-mail</MudText>
 | 
				
			||||||
                </MudItem>
 | 
					                </MudItem>
 | 
				
			||||||
                <MudItem xs="10">
 | 
					                <MudItem xs="12" md="10">
 | 
				
			||||||
                    <MudLink Href="mailto:chris@qbittorrent.org">chris@qbittorrent.org</MudLink>
 | 
					                    <MudLink Href="mailto:chris@qbittorrent.org">chris@qbittorrent.org</MudLink>
 | 
				
			||||||
                </MudItem>
 | 
					                </MudItem>
 | 
				
			||||||
            </MudGrid>
 | 
					            </MudGrid>
 | 
				
			||||||
@@ -118,7 +125,7 @@
 | 
				
			|||||||
                (the list might not be up to date)
 | 
					                (the list might not be up to date)
 | 
				
			||||||
            </MudText>
 | 
					            </MudText>
 | 
				
			||||||
            <MudList T="string" ReadOnly>
 | 
					            <MudList T="string" ReadOnly>
 | 
				
			||||||
                <MudListItem Icon="@Icons.Material.Filled.Circle" IconColor="Color.Info"><u>Arabic:</u> SDERAWI (abz8868@msn.com), sn51234 (nesseyan@gmail.com) and Ibrahim Saed ibraheem_alex(Transifex)</MudListItem>
 | 
					                <MudListItem Icon="@Icons.Material.Filled.Circle"><u>Arabic:</u> SDERAWI (abz8868@msn.com), sn51234 (nesseyan@gmail.com) and Ibrahim Saed ibraheem_alex(Transifex)</MudListItem>
 | 
				
			||||||
                <MudListItem Icon="@Icons.Material.Filled.Circle"><u>Armenian:</u> Hrant Ohanyan (hrantohanyan@mail.am)</MudListItem>
 | 
					                <MudListItem Icon="@Icons.Material.Filled.Circle"><u>Armenian:</u> Hrant Ohanyan (hrantohanyan@mail.am)</MudListItem>
 | 
				
			||||||
                <MudListItem Icon="@Icons.Material.Filled.Circle"><u>Basque:</u> Xabier Aramendi (azpidatziak@gmail.com)</MudListItem>
 | 
					                <MudListItem Icon="@Icons.Material.Filled.Circle"><u>Basque:</u> Xabier Aramendi (azpidatziak@gmail.com)</MudListItem>
 | 
				
			||||||
                <MudListItem Icon="@Icons.Material.Filled.Circle"><u>Belarusian:</u> Mihas Varantsou (meequz@gmail.com)</MudListItem>
 | 
					                <MudListItem Icon="@Icons.Material.Filled.Circle"><u>Belarusian:</u> Mihas Varantsou (meequz@gmail.com)</MudListItem>
 | 
				
			||||||
@@ -1058,38 +1065,38 @@
 | 
				
			|||||||
            <MudText Typo="Typo.body1" Class="py-1">qBittorrent was built with the following libraries:</MudText>
 | 
					            <MudText Typo="Typo.body1" Class="py-1">qBittorrent was built with the following libraries:</MudText>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <MudGrid Class="mt-1 mb-4">
 | 
					            <MudGrid Class="mt-1 mb-4">
 | 
				
			||||||
                <MudItem xs="2">
 | 
					                <MudItem xs="3">
 | 
				
			||||||
                    <MudText Typo="Typo.body1">Qt</MudText>
 | 
					                    <MudText Typo="Typo.body1">Qt</MudText>
 | 
				
			||||||
                </MudItem>
 | 
					                </MudItem>
 | 
				
			||||||
                <MudItem xs="10">
 | 
					                <MudItem xs="9">
 | 
				
			||||||
                    <MudText Typo="Typo.body1">@QtVersion</MudText>
 | 
					                    <MudText Typo="Typo.body1">@QtVersion</MudText>
 | 
				
			||||||
                </MudItem>
 | 
					                </MudItem>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <MudItem xs="2">
 | 
					                <MudItem xs="3">
 | 
				
			||||||
                    <MudText Typo="Typo.body1">Libtorrent</MudText>
 | 
					                    <MudText Typo="Typo.body1">Libtorrent</MudText>
 | 
				
			||||||
                </MudItem>
 | 
					                </MudItem>
 | 
				
			||||||
                <MudItem xs="10">
 | 
					                <MudItem xs="9">
 | 
				
			||||||
                    <MudText Typo="Typo.body1">@LibtorrentVersion</MudText>
 | 
					                    <MudText Typo="Typo.body1">@LibtorrentVersion</MudText>
 | 
				
			||||||
                </MudItem>
 | 
					                </MudItem>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <MudItem xs="2">
 | 
					                <MudItem xs="3">
 | 
				
			||||||
                    <MudText Typo="Typo.body1">Boost</MudText>
 | 
					                    <MudText Typo="Typo.body1">Boost</MudText>
 | 
				
			||||||
                </MudItem>
 | 
					                </MudItem>
 | 
				
			||||||
                <MudItem xs="10">
 | 
					                <MudItem xs="9">
 | 
				
			||||||
                    <MudText Typo="Typo.body1">@BoostVersion</MudText>
 | 
					                    <MudText Typo="Typo.body1">@BoostVersion</MudText>
 | 
				
			||||||
                </MudItem>
 | 
					                </MudItem>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <MudItem xs="2">
 | 
					                <MudItem xs="3">
 | 
				
			||||||
                    <MudText Typo="Typo.body1">OpenSSL</MudText>
 | 
					                    <MudText Typo="Typo.body1">OpenSSL</MudText>
 | 
				
			||||||
                </MudItem>
 | 
					                </MudItem>
 | 
				
			||||||
                <MudItem xs="10">
 | 
					                <MudItem xs="9">
 | 
				
			||||||
                    <MudText Typo="Typo.body1">@OpensslVersion</MudText>
 | 
					                    <MudText Typo="Typo.body1">@OpensslVersion</MudText>
 | 
				
			||||||
                </MudItem>
 | 
					                </MudItem>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <MudItem xs="2">
 | 
					                <MudItem xs="3">
 | 
				
			||||||
                    <MudText Typo="Typo.body1">zlib</MudText>
 | 
					                    <MudText Typo="Typo.body1">zlib</MudText>
 | 
				
			||||||
                </MudItem>
 | 
					                </MudItem>
 | 
				
			||||||
                <MudItem xs="10">
 | 
					                <MudItem xs="9">
 | 
				
			||||||
                    <MudText Typo="Typo.body1">@ZlibVersion</MudText>
 | 
					                    <MudText Typo="Typo.body1">@ZlibVersion</MudText>
 | 
				
			||||||
                </MudItem>
 | 
					                </MudItem>
 | 
				
			||||||
            </MudGrid>
 | 
					            </MudGrid>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
@page "/details/{hash}"
 | 
					@page "/details/{hash}"
 | 
				
			||||||
@layout DetailsLayout
 | 
					@layout DetailsLayout
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div style="overflow-x: auto; white-space: nowrap; width: 100%;">
 | 
				
			||||||
<MudToolBar Gutters="false" Dense="true">
 | 
					<MudToolBar Gutters="false" Dense="true">
 | 
				
			||||||
    @if (!DrawerOpen)
 | 
					    @if (!DrawerOpen)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -14,6 +15,7 @@
 | 
				
			|||||||
    <MudDivider Vertical="true" />
 | 
					    <MudDivider Vertical="true" />
 | 
				
			||||||
    <MudText Class="pl-5 no-wrap">@Name</MudText>
 | 
					    <MudText Class="pl-5 no-wrap">@Name</MudText>
 | 
				
			||||||
</MudToolBar>
 | 
					</MudToolBar>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@if (ShowTabs)
 | 
					@if (ShowTabs)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -49,7 +49,7 @@ namespace Lantean.QBTMud.Pages
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        protected override Task OnInitializedAsync()
 | 
					        protected override Task OnInitializedAsync()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return DoLogin("admin", "eBGJzbjkJ");
 | 
					            return DoLogin("admin", "5FUM5pATq");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,7 +18,7 @@
 | 
				
			|||||||
                <MudItem xs="12" md="4">
 | 
					                <MudItem xs="12" md="4">
 | 
				
			||||||
                    <MudTextField T="string" Label="Criteria" @bind-Value="Model.SearchText" Variant="Variant.Outlined" />
 | 
					                    <MudTextField T="string" Label="Criteria" @bind-Value="Model.SearchText" Variant="Variant.Outlined" />
 | 
				
			||||||
                </MudItem>
 | 
					                </MudItem>
 | 
				
			||||||
                <MudItem xs="2" md="3">
 | 
					                <MudItem xs="12" md="3">
 | 
				
			||||||
                    <MudSelect T="string" Label="Categories" @bind-Value="Model.SelectedCategory" Variant="Variant.Outlined">
 | 
					                    <MudSelect T="string" Label="Categories" @bind-Value="Model.SelectedCategory" Variant="Variant.Outlined">
 | 
				
			||||||
                        @foreach (var (value, name) in Categories)
 | 
					                        @foreach (var (value, name) in Categories)
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
@@ -30,17 +30,21 @@
 | 
				
			|||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    </MudSelect>
 | 
					                    </MudSelect>
 | 
				
			||||||
                </MudItem>
 | 
					                </MudItem>
 | 
				
			||||||
                <MudItem xs="2" md="3">
 | 
					                <MudItem xs="12" md="3">
 | 
				
			||||||
                    <MudSelect T="string" Label="Plugins" @bind-Value="Model.SelectedPlugin" Variant="Variant.Outlined">
 | 
					                    <MudSelect T="string" Label="Plugins" @bind-Value="Model.SelectedPlugin" Variant="Variant.Outlined">
 | 
				
			||||||
                        <MudSelectItem Value="@("all")">All</MudSelectItem>
 | 
					                        <MudSelectItem Value="@("all")">All</MudSelectItem>
 | 
				
			||||||
 | 
					                        @if (Plugins.Count > 0)
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
                            <MudDivider />
 | 
					                            <MudDivider />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
                        @foreach (var (value, name) in Plugins)
 | 
					                        @foreach (var (value, name) in Plugins)
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            <MudSelectItem Value="value">@name</MudSelectItem>
 | 
					                            <MudSelectItem Value="value">@name</MudSelectItem>
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    </MudSelect>
 | 
					                    </MudSelect>
 | 
				
			||||||
                </MudItem>
 | 
					                </MudItem>
 | 
				
			||||||
                <MudItem xs="2" md="2">
 | 
					                <MudItem xs="12" md="2">
 | 
				
			||||||
                    <MudButton ButtonType="ButtonType.Submit" FullWidth="true" Color="Color.Primary" EndIcon="@Icons.Material.Filled.Search" Variant="Variant.Filled" Class="mt-6">@(_searchId is null ? "Search" : "Stop")</MudButton>
 | 
					                    <MudButton ButtonType="ButtonType.Submit" FullWidth="true" Color="Color.Primary" EndIcon="@Icons.Material.Filled.Search" Variant="Variant.Filled" Class="mt-6">@(_searchId is null ? "Search" : "Stop")</MudButton>
 | 
				
			||||||
                </MudItem>
 | 
					                </MudItem>
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,12 +1,13 @@
 | 
				
			|||||||
@page "/"
 | 
					@page "/"
 | 
				
			||||||
@layout ListLayout
 | 
					@layout ListLayout
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<ContextMenu @ref="ContextMenu" Dense="true" AdjustmentX="@(DrawerOpen ? -235 : 0)">
 | 
					<ContextMenu @ref="ContextMenu" Dense="true" RelativeWidth="DropdownWidth.Ignore" AdjustmentX="-242" AdjustmentY="0">
 | 
				
			||||||
    <MudMenuItem Icon="@Icons.Material.Outlined.Info" IconColor="Color.Inherit" OnClick="ShowTorrentContextMenu">View torrent details</MudMenuItem>
 | 
					    <MudMenuItem Icon="@Icons.Material.Outlined.Info" IconColor="Color.Inherit" OnClick="ShowTorrentContextMenu">View torrent details</MudMenuItem>
 | 
				
			||||||
    <MudDivider />
 | 
					    <MudDivider />
 | 
				
			||||||
    <TorrentActions RenderType="RenderType.MenuItems" Hashes="GetContextMenuTargetHashes()" PrimaryHash="@(ContextMenuItem?.Hash)" Torrents="MainData.Torrents" Preferences="Preferences" />
 | 
					    <TorrentActions RenderType="RenderType.MenuItems" Hashes="GetContextMenuTargetHashes()" PrimaryHash="@(ContextMenuItem?.Hash)" Torrents="MainData.Torrents" Preferences="Preferences" />
 | 
				
			||||||
</ContextMenu>
 | 
					</ContextMenu>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div style="overflow-x: auto; white-space: nowrap; width: 100%;">
 | 
				
			||||||
<MudToolBar Gutters="false" Dense="true">
 | 
					<MudToolBar Gutters="false" Dense="true">
 | 
				
			||||||
    <MudIconButton Icon="@Icons.Material.Outlined.AddLink" OnClick="AddTorrentLink" title="Add torrent link" />
 | 
					    <MudIconButton Icon="@Icons.Material.Outlined.AddLink" OnClick="AddTorrentLink" title="Add torrent link" />
 | 
				
			||||||
    <MudIconButton Icon="@Icons.Material.Outlined.AddCircle" OnClick="AddTorrentFile" title="Add torrent file" />
 | 
					    <MudIconButton Icon="@Icons.Material.Outlined.AddCircle" OnClick="AddTorrentFile" title="Add torrent file" />
 | 
				
			||||||
@@ -18,6 +19,7 @@
 | 
				
			|||||||
    <MudSpacer />
 | 
					    <MudSpacer />
 | 
				
			||||||
    <MudTextField Value="SearchText" TextChanged="SearchTextChanged" Immediate="true" DebounceInterval="1000" Placeholder="Filter torrent list" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0"></MudTextField>
 | 
					    <MudTextField Value="SearchText" TextChanged="SearchTextChanged" Immediate="true" DebounceInterval="1000" Placeholder="Filter torrent list" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0"></MudTextField>
 | 
				
			||||||
</MudToolBar>
 | 
					</MudToolBar>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<MudContainer MaxWidth="MaxWidth.ExtraExtraLarge" Class="ma-0 pa-0">
 | 
					<MudContainer MaxWidth="MaxWidth.ExtraExtraLarge" Class="ma-0 pa-0">
 | 
				
			||||||
    <DynamicTable
 | 
					    <DynamicTable
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -193,7 +193,7 @@ namespace Lantean.QBTMud.Pages
 | 
				
			|||||||
        public static List<ColumnDefinition<Torrent>> ColumnsDefinitions { get; } =
 | 
					        public static List<ColumnDefinition<Torrent>> ColumnsDefinitions { get; } =
 | 
				
			||||||
        [
 | 
					        [
 | 
				
			||||||
            ColumnDefinitionHelper.CreateColumnDefinition<Torrent>("#", t => t.Priority),
 | 
					            ColumnDefinitionHelper.CreateColumnDefinition<Torrent>("#", t => t.Priority),
 | 
				
			||||||
            ColumnDefinitionHelper.CreateColumnDefinition("Icon", t => t.State, IconColumn, iconOnly: true, width: 25),
 | 
					            ColumnDefinitionHelper.CreateColumnDefinition("Icon", t => t.State, IconColumn, iconOnly: true, width: 25, tdClass: "table-icon"),
 | 
				
			||||||
            ColumnDefinitionHelper.CreateColumnDefinition<Torrent>("Name", t => t.Name, width: 400),
 | 
					            ColumnDefinitionHelper.CreateColumnDefinition<Torrent>("Name", t => t.Name, width: 400),
 | 
				
			||||||
            ColumnDefinitionHelper.CreateColumnDefinition<Torrent>("Size", t => t.Size, t => DisplayHelpers.Size(t.Size)),
 | 
					            ColumnDefinitionHelper.CreateColumnDefinition<Torrent>("Size", t => t.Size, t => DisplayHelpers.Size(t.Size)),
 | 
				
			||||||
            ColumnDefinitionHelper.CreateColumnDefinition<Torrent>("Total Size", t => t.TotalSize, t => DisplayHelpers.Size(t.TotalSize), enabled: false),
 | 
					            ColumnDefinitionHelper.CreateColumnDefinition<Torrent>("Total Size", t => t.TotalSize, t => DisplayHelpers.Size(t.TotalSize), enabled: false),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -155,7 +155,7 @@ code {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.torrent-list .mud-table-container {
 | 
					.torrent-list .mud-table-container {
 | 
				
			||||||
    height: calc(100vh - 149px);
 | 
					    height: calc(100vh - 160px);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.file-list .mud-table-container {
 | 
					.file-list .mud-table-container {
 | 
				
			||||||
@@ -241,3 +241,18 @@ td .folder-button {
 | 
				
			|||||||
.mud-dialog .mud-dialog-content {
 | 
					.mud-dialog .mud-dialog-content {
 | 
				
			||||||
    padding-top: 4px !important;
 | 
					    padding-top: 4px !important;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.icon-menu-dense {
 | 
				
			||||||
 | 
					    padding-top: 2px;
 | 
				
			||||||
 | 
					    padding-bottom: 2px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.table-icon {
 | 
				
			||||||
 | 
					    width: 25px;
 | 
				
			||||||
 | 
					    max-width: 25px;
 | 
				
			||||||
 | 
					    padding: 0 8px !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.mud-popover .mud-divider:last-child {
 | 
				
			||||||
 | 
					    display: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
					<Project Sdk="Microsoft.NET.Sdk">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <PropertyGroup>
 | 
					  <PropertyGroup>
 | 
				
			||||||
    <TargetFramework>net8.0</TargetFramework>
 | 
					    <TargetFramework>net9.0</TargetFramework>
 | 
				
			||||||
    <ImplicitUsings>enable</ImplicitUsings>
 | 
					    <ImplicitUsings>enable</ImplicitUsings>
 | 
				
			||||||
    <Nullable>enable</Nullable>
 | 
					    <Nullable>enable</Nullable>
 | 
				
			||||||
	<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
 | 
						<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										11
									
								
								nuget.config
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								nuget.config
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					<configuration>
 | 
				
			||||||
 | 
					  <packageSources>
 | 
				
			||||||
 | 
					    <!-- Define package sources here -->
 | 
				
			||||||
 | 
					  </packageSources>
 | 
				
			||||||
 | 
					  <packageSourceMapping>
 | 
				
			||||||
 | 
					    <!-- Optional source mapping -->
 | 
				
			||||||
 | 
					  </packageSourceMapping>
 | 
				
			||||||
 | 
					  <packageVersionOverride>
 | 
				
			||||||
 | 
					    <package id="FluentAssertions" allowedVersions="[7.0.0,8.0.0)" />
 | 
				
			||||||
 | 
					  </packageVersionOverride>
 | 
				
			||||||
 | 
					</configuration>
 | 
				
			||||||
		Reference in New Issue
	
	Block a user