9 Commits

Author SHA1 Message Date
ahjephson
bad509e40f Merge branch 'release/0.2.0' 2025-02-07 09:48:56 +00:00
ahjephson
6a0796ef20 Update actions to build .net 9 2025-02-07 09:46:14 +00:00
ahjephson
dc4b515763 Fix styling issues with torrent list
Only display errors in debug mode
Add column sorting
2025-02-07 09:23:54 +00:00
ahjephson
938702a7b3 Partial .net9 upgrade 2025-02-04 13:58:24 +00:00
ahjephson
6ca1c6edd4 Update to net9.0 2025-01-07 09:18:45 +00:00
ahjephson
24eb5cf5e9 Merge tag '0.1.0' into develop
0.1.0
2024-11-02 13:46:03 +00:00
ahjephson
7d62c9aecf Merge branch 'release/0.1.0' 2024-11-02 13:45:36 +00:00
ahjephson
b1e5424f55 Update rename files UI 2024-11-02 13:44:00 +00:00
ahjephson
66a6c2ca78 Move readme file 2024-11-02 10:47:46 +00:00
49 changed files with 319 additions and 137 deletions

View File

@@ -2,3 +2,78 @@
# IDE0290: Use primary constructor # IDE0290: Use primary constructor
csharp_style_prefer_primary_constructors = false csharp_style_prefer_primary_constructors = false
[*.cs]
#### Naming styles ####
# Naming rules
dotnet_naming_rule.private_or_internal_field_should_be_begins_with_underscore.severity = suggestion
dotnet_naming_rule.private_or_internal_field_should_be_begins_with_underscore.symbols = private_or_internal_field
dotnet_naming_rule.private_or_internal_field_should_be_begins_with_underscore.style = begins_with_underscore
# Symbol specifications
dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field
dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private, private_protected
dotnet_naming_symbols.private_or_internal_field.required_modifiers =
# Naming styles
dotnet_naming_style.begins_with_underscore.required_prefix = _
dotnet_naming_style.begins_with_underscore.required_suffix =
dotnet_naming_style.begins_with_underscore.word_separator =
dotnet_naming_style.begins_with_underscore.capitalization = camel_case
csharp_indent_labels = one_less_than_current
[*.{cs,vb}]
#### Naming styles ####
# Naming rules
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
# Symbol specifications
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =
# Naming styles
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_style_operator_placement_when_wrapping = beginning_of_line
tab_width = 4
indent_size = 4
end_of_line = crlf

View File

@@ -21,7 +21,7 @@ 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

View File

@@ -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>

View File

@@ -12,6 +12,7 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{1BF1A631-87D7-4039-A701-88C5E0234B63}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{1BF1A631-87D7-4039-A701-88C5E0234B63}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig .editorconfig = .editorconfig
readme.md = readme.md
EndProjectSection EndProjectSection
EndProject EndProject
Global Global

View File

@@ -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; } = [];

View File

@@ -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; } = [];

View File

@@ -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; } = [];

View File

@@ -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; }

View File

@@ -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">

View File

@@ -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
{ {

View File

@@ -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; } = [];

View File

@@ -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!;

View File

@@ -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))" />

View File

@@ -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)

View File

@@ -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!;

View File

@@ -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>

View File

@@ -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; }

View File

@@ -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; }

View File

@@ -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;

View File

@@ -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; } = [];

View File

@@ -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; } = [];

View File

@@ -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!;

View File

@@ -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; }

View File

@@ -9,16 +9,15 @@
<MudItem xs="12"> <MudItem xs="12">
<MudTextField T="string" Label="Search files" Value="Search" ValueChanged="SearchChanged" Variant="Variant.Outlined" /> <MudTextField T="string" Label="Search files" Value="Search" ValueChanged="SearchChanged" Variant="Variant.Outlined" />
</MudItem> </MudItem>
<MudItem xs="12"> <MudItem xs="12" lg="4">
<FieldSwitch Label="Use regular expressions" Value="UseRegex" ValueChanged="UseRegexChanged" /> <FieldSwitch Label="Use regular expressions" Value="UseRegex" ValueChanged="UseRegexChanged" />
</MudItem> </MudItem>
<MudItem xs="12"> <MudItem xs="12" lg="4">
<FieldSwitch Label="Match all occurrences" Value="MatchAllOccurrences" ValueChanged="MatchAllOccurrencesChanged" /> <FieldSwitch Label="Match all occurrences" Value="MatchAllOccurrences" ValueChanged="MatchAllOccurrencesChanged" />
</MudItem> </MudItem>
<MudItem xs="12"> <MudItem xs="12" lg="4">
<FieldSwitch Label="Case sensitive" Value="CaseSensitive" ValueChanged="CaseSensitiveChanged" /> <FieldSwitch Label="Case sensitive" Value="CaseSensitive" ValueChanged="CaseSensitiveChanged" />
</MudItem> </MudItem>
<MudDivider />
<MudItem xs="12"> <MudItem xs="12">
<MudTextField T="string" Label="Replacement" Value="Replacement" ValueChanged="ReplacementChanged" Variant="Variant.Outlined" /> <MudTextField T="string" Label="Replacement" Value="Replacement" ValueChanged="ReplacementChanged" Variant="Variant.Outlined" />
</MudItem> </MudItem>
@@ -29,20 +28,19 @@
<MudSelectItem T="AppliesTo" Value="AppliesTo.Extension">Extension</MudSelectItem> <MudSelectItem T="AppliesTo" Value="AppliesTo.Extension">Extension</MudSelectItem>
</MudSelect> </MudSelect>
</MudItem> </MudItem>
<MudItem xs="12"> <MudItem xs="12" lg="4">
<FieldSwitch Label="Include files" Value="IncludeFiles" ValueChanged="IncludeFilesChanged" /> <FieldSwitch Label="Include files" Value="IncludeFiles" ValueChanged="IncludeFilesChanged" />
</MudItem> </MudItem>
<MudItem xs="12"> <MudItem xs="12" lg="4">
<FieldSwitch Label="Include folders" Value="IncludeFolders" ValueChanged="IncludeFoldersChanged" /> <FieldSwitch Label="Include folders" Value="IncludeFolders" ValueChanged="IncludeFoldersChanged" />
</MudItem> </MudItem>
<MudItem xs="12" md="6"> <MudItem xs="12" lg="4">
<MudNumericField T="int" Label="Enumerate files" Value="FileEnumerationStart" ValueChanged="FileEnumerationStartChanged" Min="0" Variant="Variant.Outlined" /> <MudNumericField T="int" Label="Enumerate files" Value="FileEnumerationStart" ValueChanged="FileEnumerationStartChanged" Min="0" Variant="Variant.Outlined" />
</MudItem> </MudItem>
<MudDivider />
<MudItem xs="12"> <MudItem xs="12">
<MudSelect T="bool" Label="Replace type" Value="ReplaceAll" ValueChanged="ReplaceAllChanged" Variant="Variant.Outlined"> <MudSelect T="bool" Label="Replace type" Value="ReplaceAll" ValueChanged="ReplaceAllChanged" Variant="Variant.Outlined">
<MudSelectItem T="bool" Value="true">Replace</MudSelectItem> <MudSelectItem T="bool" Value="false">Replace</MudSelectItem>
<MudSelectItem T="bool" Value="false">Replace all</MudSelectItem> <MudSelectItem T="bool" Value="true">Replace all</MudSelectItem>
</MudSelect> </MudSelect>
</MudItem> </MudItem>
</MudGrid> </MudGrid>

View File

@@ -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; }
@@ -484,19 +483,20 @@ namespace Lantean.QBTMud.Components.Dialogs
ReplaceAll, ReplaceAll,
FileEnumerationStart); FileEnumerationStart);
foreach (var (_, renamedFile) in renamedFiles) foreach (var (_, renamedFile) in renamedFiles.Where(f => !f.Value.IsFolder))
{ {
var oldPath = renamedFile.Path + renamedFile.OriginalName; var oldPath = renamedFile.Path + renamedFile.OriginalName;
var newPath = renamedFile.Path + renamedFile.NewName; var newPath = renamedFile.Path + renamedFile.NewName;
if (renamedFile.IsFolder)
{ await ApiClient.RenameFile(Hash, oldPath, newPath);
}
await ApiClient.RenameFolder(Hash, oldPath, newPath);
} foreach (var (_, renamedFile) in renamedFiles.Where(f => f.Value.IsFolder).OrderBy(f => f.Value.Path.Split(Extensions.DirectorySeparator)))
else {
{ var oldPath = renamedFile.Path + renamedFile.OriginalName;
await ApiClient.RenameFile(Hash, oldPath, newPath); var newPath = renamedFile.Path + renamedFile.NewName;
}
await ApiClient.RenameFolder(Hash, oldPath, newPath);
} }
MudDialog.Close(); MudDialog.Close();

View File

@@ -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!;

View File

@@ -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; }

View File

@@ -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; }

View File

@@ -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; }

View File

@@ -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; }

View File

@@ -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]

View File

@@ -1,4 +1,4 @@
<ContextMenu @ref="StatusContextMenu" Dense="true" AdjustmentY="-60"> <ContextMenu @ref="StatusContextMenu" Dense="true" AdjustmentY="-60">
@TorrentControls(_statusType) @TorrentControls(_statusType)
</ContextMenu> </ContextMenu>

View File

@@ -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

View File

@@ -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;
} }

View File

@@ -8,19 +8,17 @@
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>
} }
</CascadingValue> </CascadingValue>
</MudPopover> </MudPopover>

View File

@@ -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();
} }
return Task.CompletedTask;
} }
private async Task DeterminePosition() //private async Task DeterminePosition()
{ //{
var mainContentSize = await JSRuntime.GetInnerDimensions(".mud-main-content"); // var mainContentSize = await JSRuntime.GetInnerDimensions(".mud-main-content");
double? contextMenuHeight = null; // double? contextMenuHeight = null;
double? contextMenuWidth = null; // double? contextMenuWidth = null;
var popoverHolder = PopoverService.ActivePopovers.FirstOrDefault(p => p.UserAttributes.ContainsKey("tracker") && (string?)p.UserAttributes["tracker"] == Id); // var popoverHolder = PopoverService.ActivePopovers.FirstOrDefault(p => p.UserAttributes.ContainsKey("tracker") && (string?)p.UserAttributes["tracker"] == Id);
var popoverSize = await JSRuntime.GetBoundingClientRect($"#popovercontent-{popoverHolder?.Id}"); // var popoverSize = await JSRuntime.GetBoundingClientRect($"#popovercontent-{popoverHolder?.Id}");
if (popoverSize.Height > 0) // if (popoverSize.Height > 0)
{ // {
contextMenuHeight = popoverSize.Height; // contextMenuHeight = popoverSize.Height;
contextMenuWidth = popoverSize.Width; // contextMenuWidth = popoverSize.Width;
} // }
else // else
{ // {
return; // return;
} // }
// the bottom position of the popover will be rendered off screen // // the bottom position of the popover will be rendered off screen
if (_y - _diff + contextMenuHeight.Value >= mainContentSize.Height) // if (_y - _diff + contextMenuHeight.Value >= mainContentSize.Height)
{ // {
// adjust the top of the context menu // // adjust the top of the context menu
var overshoot = Math.Abs(mainContentSize.Height - (_y - _diff + contextMenuHeight.Value)); // var overshoot = Math.Abs(mainContentSize.Height - (_y - _diff + contextMenuHeight.Value));
_y -= overshoot; // _y -= overshoot;
if (_y - _diff + contextMenuHeight >= mainContentSize.Height) // if (_y - _diff + contextMenuHeight >= mainContentSize.Height)
{ // {
MaxHeight = (int)(mainContentSize.Height - _y + _diff); // MaxHeight = (int)(mainContentSize.Height - _y + _diff);
} // }
} // }
if (_x + contextMenuWidth.Value > mainContentSize.Width) // if (_x + contextMenuWidth.Value > mainContentSize.Width)
{ // {
var overshoot = Math.Abs(mainContentSize.Width - (_x + contextMenuWidth.Value)); // var overshoot = Math.Abs(mainContentSize.Width - (_x + contextMenuWidth.Value));
_x -= overshoot; // _x -= overshoot;
} // }
SetPopoverStyle(_x, _y); // SetPopoverStyle(_x, _y);
_isResized = true; // _isResized = true;
await InvokeAsync(StateHasChanged); // await InvokeAsync(StateHasChanged);
} //}
private (double x, double y) GetPositionFromArgs(EventArgs eventArgs) private (double x, double y) GetPositionFromArgs(EventArgs eventArgs)
{ {

View File

@@ -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" />

View File

@@ -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,8 +168,29 @@ 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);
foreach (var column in filteredColumns) if (_columnOrder.Count == 0)
{ {
foreach (var column in filteredColumns)
{
if (_columnWidths.TryGetValue(column.Id, out var value))
{
column.Width = value;
}
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)) if (_columnWidths.TryGetValue(column.Id, out var value))
{ {
column.Width = value; column.Width = value;
@@ -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)

View File

@@ -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)

View File

@@ -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>

View File

@@ -20,12 +20,15 @@
<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"> @if (IsDebug)
<ErrorDisplay ErrorBoundary="ErrorBoundary" /> {
</MudDrawer> <MudDrawer Open="ErrorDrawerOpen" ClipMode="DrawerClipMode.Docked" Elevation="2" Anchor="Anchor.Right">
<ErrorDisplay ErrorBoundary="ErrorBoundary" />
</MudDrawer>
}
<CascadingValue Value="Theme"> <CascadingValue Value="Theme">
<CascadingValue Value="IsDarkMode" Name="IsDarkMode"> <CascadingValue Value="IsDarkMode" Name="IsDarkMode">
<CascadingValue Value="Menu"> <CascadingValue Value="Menu">

View File

@@ -44,6 +44,12 @@ namespace Lantean.QBTMud.Layout
protected MudTheme Theme { get; set; } protected MudTheme Theme { get; set; }
#if DEBUG
private bool IsDebug { get; } = true;
#else
private bool IsDebug { get; } = false;
#endif
public MainLayout() public MainLayout()
{ {
Theme = new MudTheme(); Theme = new MudTheme();

View File

@@ -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

View File

@@ -1,7 +1,7 @@
@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" />

View File

@@ -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),

View File

@@ -240,4 +240,15 @@ 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;
} }

View File

@@ -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
View 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>