Rework actions to mirror original implmentation and add missing torrent actions

This commit is contained in:
ahjephson
2024-06-12 13:04:23 +01:00
parent f49b876666
commit 09ac7befe9
7 changed files with 354 additions and 100 deletions

View File

@@ -22,34 +22,30 @@ else if (RenderType == RenderType.InitialIconsOnly)
{ {
@foreach (var action in Actions.Take(5)) @foreach (var action in Actions.Take(5))
{ {
@if (action is Divider) @if (action.SeparatorBefore)
{ {
<MudDivider Vertical="true" /> <MudDivider Vertical="true" />
} }
else
{ <MudIconButton Title="@action.Text" Icon="@action.Icon" Color="action.Color" OnClick="action.Callback" Disabled="Disabled" />
<MudIconButton Title="@action.Name" Icon="@action.Icon" Color="action.Color" OnClick="action.Callback" Disabled="Disabled" />
}
} }
@Menu(Actions.Skip(5)) @Menu(Actions.Skip(5))
} }
else if (RenderType == RenderType.Children) else if (RenderType == RenderType.Children)
{ {
var parent = Actions.FirstOrDefault(a => a.Name == ParentAction?.Name); var parent = Actions.FirstOrDefault(a => a.Text == ParentAction?.Text);
if (parent is not null) if (parent is not null)
{ {
<MudList Clickable="true"> <MudList Clickable="true">
@foreach (var action in parent.Children) @foreach (var action in parent.Children)
{ {
@if (action is Divider) @if (action.SeparatorBefore)
{ {
<MudDivider /> <MudDivider />
} }
else
{ <MudListItem Icon="@action.Icon" IconColor="action.Color" OnClick="action.Callback" Disabled="Disabled">@action.Text</MudListItem>
<MudListItem Icon="@action.Icon" IconColor="action.Color" OnClick="action.Callback" Disabled="Disabled">@action.Name</MudListItem>
}
} }
</MudList> </MudList>
} }
@@ -68,24 +64,25 @@ else
{ {
foreach (var action in Actions) foreach (var action in Actions)
{ {
if (action is Divider) if (action.SeparatorBefore)
{ {
<MudDivider Vertical="true" /> <MudDivider Vertical="true" />
} }
else if (!action.Children.Any())
if (!action.Children.Any())
{ {
if (action.Icon is null) if (action.Icon is null)
{ {
<MudButton Color="action.Color" OnClick="action.Callback">@action.Name</MudButton> <MudButton Color="action.Color" OnClick="action.Callback">@action.Text</MudButton>
} }
else else
{ {
<MudIconButton Title="@action.Name" Icon="@action.Icon" Color="action.Color" OnClick="action.Callback" Disabled="Disabled" /> <MudIconButton Title="@action.Text" Icon="@action.Icon" Color="action.Color" OnClick="action.Callback" Disabled="Disabled" />
} }
} }
else else
{ {
<MudMenu Icon="@action.Icon" IconColor="@action.Color" Label="@action.Name" title="@action.Name" AnchorOrigin="Origin.BottomLeft" TransformOrigin="Origin.TopLeft"> <MudMenu Icon="@action.Icon" IconColor="@action.Color" Label="@action.Text" title="@action.Text" AnchorOrigin="Origin.BottomLeft" TransformOrigin="Origin.TopLeft">
@foreach (var childItem in action.Children) @foreach (var childItem in action.Children)
{ {
@ChildItem(childItem) @ChildItem(childItem)
@@ -105,24 +102,25 @@ else
{ {
foreach (var action in Actions) foreach (var action in Actions)
{ {
if (action is Divider) if (action.SeparatorBefore)
{ {
<MudDivider Vertical="true" /> <MudDivider Vertical="true" />
} }
else if (!action.Children.Any())
if (!action.Children.Any())
{ {
if (action.Icon is null) if (action.Icon is null)
{ {
<MudButton Color="action.Color" OnClick="action.Callback" Disabled="Disabled">@action.Name</MudButton> <MudButton Color="action.Color" OnClick="action.Callback" Disabled="Disabled">@action.Text</MudButton>
} }
else else
{ {
<MudIconButton Title="@action.Name" Icon="@action.Icon" Color="action.Color" OnClick="action.Callback" Disabled="Disabled" /> <MudIconButton Title="@action.Text" Icon="@action.Icon" Color="action.Color" OnClick="action.Callback" Disabled="Disabled" />
} }
} }
else else
{ {
<MudMenu Label="@action.Name" title="@action.Name" AnchorOrigin="Origin.BottomLeft" TransformOrigin="Origin.TopLeft" EndIcon="@Icons.Material.Filled.ArrowDropDown"> <MudMenu Label="@action.Text" title="@action.Text" AnchorOrigin="Origin.BottomLeft" TransformOrigin="Origin.TopLeft" EndIcon="@Icons.Material.Filled.ArrowDropDown">
@foreach (var childItem in action.Children) @foreach (var childItem in action.Children)
{ {
@ChildItem(childItem) @ChildItem(childItem)
@@ -138,14 +136,12 @@ else
{ {
return __builder => return __builder =>
{ {
if (action is Divider) if (action.SeparatorBefore)
{ {
<MudDivider /> <MudDivider />
} }
else
{ <MudMenuItem Icon="@action.Icon" IconColor="action.Color" OnClick="action.Callback" OnTouch="action.Callback" Disabled="Disabled">@action.Text</MudMenuItem>
<MudMenuItem Icon="@action.Icon" IconColor="action.Color" OnClick="action.Callback" OnTouch="action.Callback" Disabled="Disabled">@action.Name</MudMenuItem>
}
}; };
} }
@@ -156,14 +152,15 @@ else
<MudMenu Dense="true" AnchorOrigin="Origin.BottomLeft" TransformOrigin="Origin.TopLeft" Label="Actions" EndIcon="@Icons.Material.Filled.ArrowDropDown" @ref="ActionsMenu" Disabled="@(!Hashes.Any())"> <MudMenu Dense="true" AnchorOrigin="Origin.BottomLeft" TransformOrigin="Origin.TopLeft" Label="Actions" EndIcon="@Icons.Material.Filled.ArrowDropDown" @ref="ActionsMenu" Disabled="@(!Hashes.Any())">
@foreach (var action in actions) @foreach (var action in actions)
{ {
@if (action is Divider) @if (action.SeparatorBefore)
{ {
<MudDivider /> <MudDivider />
} }
else if (!action.Children.Any())
if (!action.Children.Any())
{ {
<MudMenuItem Icon="@action.Icon" IconColor="action.Color" OnClick="action.Callback" OnTouch="action.Callback" Disabled="Disabled"> <MudMenuItem Icon="@action.Icon" IconColor="action.Color" OnClick="action.Callback" OnTouch="action.Callback" Disabled="Disabled">
@action.Name @action.Text
</MudMenuItem> </MudMenuItem>
} }
else else
@@ -171,7 +168,7 @@ else
<MudMenuItem Icon="@action.Icon" IconColor="action.Color" OnTouch="@(t => SubMenuTouch(action))" OnClick="@(t => SubMenuTouch(action))"> <MudMenuItem Icon="@action.Icon" IconColor="action.Color" OnTouch="@(t => SubMenuTouch(action))" OnClick="@(t => SubMenuTouch(action))">
<MudMenu Dense="true" AnchorOrigin="Origin.TopRight" TransformOrigin="Origin.TopLeft" ActivationEvent="MouseEvent.MouseOver" Icon="@Icons.Material.Filled.ArrowDropDown" DisableElevation="true" DisableRipple="true" Class="sub-menu"> <MudMenu Dense="true" AnchorOrigin="Origin.TopRight" TransformOrigin="Origin.TopLeft" ActivationEvent="MouseEvent.MouseOver" Icon="@Icons.Material.Filled.ArrowDropDown" DisableElevation="true" DisableRipple="true" Class="sub-menu">
<ActivatorContent> <ActivatorContent>
@action.Name @action.Text
</ActivatorContent> </ActivatorContent>
<ChildContent> <ChildContent>

View File

@@ -11,6 +11,8 @@ namespace Lantean.QBTMudBlade.Components
{ {
public partial class TorrentActions public partial class TorrentActions
{ {
private readonly List<TorrentAction> _actions;
[Inject] [Inject]
public IApiClient ApiClient { get; set; } = default!; public IApiClient ApiClient { get; set; } = default!;
@@ -58,6 +60,47 @@ namespace Lantean.QBTMudBlade.Components
protected bool Disabled => !Hashes.Any(); protected bool Disabled => !Hashes.Any();
public TorrentActions()
{
_actions = new List<TorrentAction>
{
new TorrentAction("start", "Start", Icons.Material.Filled.PlayArrow, Color.Success, CreateCallback(Resume)),
new TorrentAction("pause", "Pause", Icons.Material.Filled.Pause, Color.Warning, CreateCallback(Pause)),
new TorrentAction("forceStart", "Force start", Icons.Material.Filled.Pause, Color.Warning, CreateCallback(ForceStart)),
new TorrentAction("delete", "Remove", Icons.Material.Filled.Delete, Color.Error, CreateCallback(Remove), separatorBefore: true),
new TorrentAction("setLocation", "Set location", Icons.Material.Filled.MyLocation, Color.Info, CreateCallback(SetLocation), separatorBefore: true),
new TorrentAction("rename", "Rename", Icons.Material.Filled.DriveFileRenameOutline, Color.Info, CreateCallback(Rename)),
new TorrentAction("renameFiles", "Rename files", Icons.Material.Filled.DriveFileRenameOutline, Color.Warning, CreateCallback(Rename)),
new TorrentAction("category", "Category", Icons.Material.Filled.List, Color.Info, CreateCallback(ShowCategories)),
new TorrentAction("tags", "Tags", Icons.Material.Filled.Label, Color.Info, CreateCallback(ShowTags)),
new TorrentAction("autoTorrentManagement", "Automatic Torrent Management", Icons.Material.Filled.Check, Color.Info, CreateCallback(ToggleAutoTMM)),
new TorrentAction("downloadLimit", "Limit download rate", Icons.Material.Filled.KeyboardDoubleArrowUp, Color.Info, CreateCallback(LimitDownloadRate), separatorBefore: true),
new TorrentAction("uploadLimit", "Limit upload rate", Icons.Material.Filled.KeyboardDoubleArrowUp, Color.Info, CreateCallback(LimitUploadRate)),
new TorrentAction("shareRatio", "Limit share ratio", Icons.Material.Filled.Percent, Color.Warning, CreateCallback(LimitShareRatio)),
new TorrentAction("superSeeding", "Super seeding mode", Icons.Material.Filled.Check, Color.Info, CreateCallback(ToggleSuperSeeding)),
new TorrentAction("sequentialDownload", "Download in sequential order", Icons.Material.Filled.Reorder, Color.Info, CreateCallback(DownloadSequential), separatorBefore: true),
new TorrentAction("firstLastPiecePrio", "Download first and last pieces first", Icons.Material.Filled.Navigation, Color.Info, CreateCallback(DownloadFirstLast)),
new TorrentAction("forceRecheck", "Force recheck", Icons.Material.Filled.Loop, Color.Info, CreateCallback(ForceRecheck), separatorBefore: true),
new TorrentAction("forceReannounce", "Force reannounce", Icons.Material.Filled.BroadcastOnHome, Color.Info, CreateCallback(ForceReannounce)),
new TorrentAction("queue", "Queue", Icons.Material.Filled.Queue, Color.Transparent, new List<TorrentAction>
{
new TorrentAction("queueTop", "Move to top", Icons.Material.Filled.VerticalAlignTop, Color.Inherit, CreateCallback(MoveToTop)),
new TorrentAction("queueUp", "Move up", Icons.Material.Filled.ArrowUpward, Color.Inherit, CreateCallback(MoveUp)),
new TorrentAction("queueDown", "Move down", Icons.Material.Filled.ArrowDownward, Color.Inherit, CreateCallback(MoveDown)),
new TorrentAction("queueBottom", "Move to bottom", Icons.Material.Filled.VerticalAlignBottom, Color.Inherit, CreateCallback(MoveToBottom)),
}, separatorBefore: true),
new TorrentAction("copy", "Copy", Icons.Material.Filled.FolderCopy, Color.Info, new List<TorrentAction>
{
new TorrentAction("copyName", "Name", Icons.Material.Filled.TextFields, Color.Info, CreateCallback(() => Copy(t => t.Name))),
new TorrentAction("copyHashv1", "Info hash v1", Icons.Material.Filled.Tag, Color.Info, CreateCallback(() => Copy(t => t.InfoHashV1))),
new TorrentAction("copyHashv2", "Info hash v2", Icons.Material.Filled.Tag, Color.Info, CreateCallback(() => Copy(t => t.InfoHashV2))),
new TorrentAction("copyMagnet", "Magnet link", Icons.Material.Filled.TextFields, Color.Info, CreateCallback(() => Copy(t => t.MagnetUri))),
new TorrentAction("copyId", "Torrent ID", Icons.Material.Filled.TextFields, Color.Info, CreateCallback(() => Copy(t => t.Hash))),
}),
new TorrentAction("export", "Export", Icons.Material.Filled.SaveAlt, Color.Info, CreateCallback(Export)),
};
}
protected async Task Pause() protected async Task Pause()
{ {
await ApiClient.PauseTorrents(Hashes); await ApiClient.PauseTorrents(Hashes);
@@ -70,6 +113,12 @@ namespace Lantean.QBTMudBlade.Components
Snackbar.Add("Torrent resumed."); Snackbar.Add("Torrent resumed.");
} }
protected async Task ForceStart()
{
await ApiClient.SetForceStart(true, null, Hashes.ToArray());
Snackbar.Add("Torrent force started.");
}
protected async Task Remove() protected async Task Remove()
{ {
await DialogService.InvokeDeleteTorrentDialog(ApiClient, Hashes.ToArray()); await DialogService.InvokeDeleteTorrentDialog(ApiClient, Hashes.ToArray());
@@ -117,6 +166,18 @@ namespace Lantean.QBTMudBlade.Components
await ApiClient.SetAutomaticTorrentManagement(true, null, torrents.Where(t => !t.AutomaticTorrentManagement).Select(t => t.Hash).ToArray()); await ApiClient.SetAutomaticTorrentManagement(true, null, torrents.Where(t => !t.AutomaticTorrentManagement).Select(t => t.Hash).ToArray());
} }
protected async Task LimitDownloadRate()
{
long downloadLimit = -1;
string hash = Hashes.First();
if (Hashes.Any() && MainData.Torrents.TryGetValue(hash, out var torrent))
{
downloadLimit = torrent.UploadLimit;
}
await DialogService.InvokeDownloadRateDialog(ApiClient, downloadLimit, Hashes);
}
protected async Task LimitUploadRate() protected async Task LimitUploadRate()
{ {
long uploadLimit = -1; long uploadLimit = -1;
@@ -220,6 +281,16 @@ namespace Lantean.QBTMudBlade.Components
await DialogService.ShowAsync<ManageCategoriesDialog>("Manage Torrent Categories", parameters, DialogHelper.FormDialogOptions); await DialogService.ShowAsync<ManageCategoriesDialog>("Manage Torrent Categories", parameters, DialogHelper.FormDialogOptions);
} }
protected async Task DownloadSequential()
{
await ApiClient.ToggleSequentialDownload(null, Hashes.ToArray());
}
protected async Task DownloadFirstLast()
{
await ApiClient.SetFirstLastPiecePriority(null, Hashes.ToArray());
}
protected async Task SubMenuTouch(TorrentAction action) protected async Task SubMenuTouch(TorrentAction action)
{ {
await DialogService.ShowSubMenu(Hashes, action, MainData, Preferences); await DialogService.ShowSubMenu(Hashes, action, MainData, Preferences);
@@ -236,71 +307,214 @@ namespace Lantean.QBTMudBlade.Components
} }
} }
private List<TorrentAction>? _actions; private IEnumerable<TorrentAction> Actions => GetActions();
private IEnumerable<TorrentAction> Actions private IEnumerable<TorrentAction> GetActions()
{ {
get var allAreSequentialDownload = true;
var thereAreSequentialDownload = false;
var allAreFirstLastPiecePrio = true;
var thereAreFirstLastPiecePrio = false;
var allAreDownloaded = true;
var allArePaused = true;
var thereArePaused = false;
var allAreForceStart = true;
var thereAreForceStart = false;
var allAreSuperSeeding = true;
var allAreAutoTmm = true;
var thereAreAutoTmm = false;
Torrent? firstTorrent = null;
foreach (var torrent in GetTorrents())
{ {
if (_actions is not null) if (firstTorrent is null)
{ {
if (Preferences?.QueueingEnabled == false) firstTorrent = torrent;
{ }
return _actions.Where(a => a.Name != "Queue"); if (!torrent.SequentialDownload)
} {
return _actions; allAreSequentialDownload = false;
}
else
{
thereAreSequentialDownload = true;
} }
Torrent? torrent = null; if (!torrent.FirstLastPiecePriority)
if (Hashes.Any())
{ {
string key = Hashes.First(); allAreFirstLastPiecePrio = false;
if (!MainData.Torrents.TryGetValue(key, out torrent)) }
{ else
Hashes = Hashes.Except([key]); {
} thereAreFirstLastPiecePrio = true;
} }
_actions = new List<TorrentAction> if (torrent.Progress != 1.0) // not downloaded
{ {
new TorrentAction("Pause", Icons.Material.Filled.Pause, Color.Warning, CreateCallback(Pause)), allAreDownloaded = false;
new TorrentAction("Resume", Icons.Material.Filled.PlayArrow, Color.Success, CreateCallback(Resume)), }
new Divider(), else if (!torrent.SuperSeeding)
new TorrentAction("Remove", Icons.Material.Filled.Delete, Color.Error, CreateCallback(Remove)), {
new Divider(), allAreSuperSeeding = false;
new TorrentAction("Set location", Icons.Material.Filled.MyLocation, Color.Info, CreateCallback(SetLocation)), }
new TorrentAction("Rename", Icons.Material.Filled.DriveFileRenameOutline, Color.Info, CreateCallback(Rename)),
new TorrentAction("Category", Icons.Material.Filled.List, Color.Info, CreateCallback(ShowCategories)),
new TorrentAction("Tags", Icons.Material.Filled.Label, Color.Info, CreateCallback(ShowTags)),
new TorrentAction("Automatic Torrent Management", Icons.Material.Filled.Check, (torrent?.AutomaticTorrentManagement == true) ? Color.Info : Color.Transparent, CreateCallback(ToggleAutoTMM)),
new Divider(),
new TorrentAction("Limit upload rate", Icons.Material.Filled.KeyboardDoubleArrowUp, Color.Info, CreateCallback(LimitUploadRate)),
new TorrentAction("Limit share ratio", Icons.Material.Filled.Percent, Color.Warning, CreateCallback(LimitShareRatio)),
new TorrentAction("Super seeding mode", Icons.Material.Filled.Check, (torrent?.SuperSeeding == true) ? Color.Info : Color.Transparent, CreateCallback(ToggleSuperSeeding)),
new Divider(),
new TorrentAction("Force recheck", Icons.Material.Filled.Loop, Color.Info, CreateCallback(ForceRecheck)),
new TorrentAction("Force reannounce", Icons.Material.Filled.BroadcastOnHome, Color.Info, CreateCallback(ForceReannounce)),
new Divider(),
new TorrentAction("Queue", Icons.Material.Filled.Queue, Color.Transparent, new List<TorrentAction>
{
new TorrentAction("Move to top", Icons.Material.Filled.VerticalAlignTop, Color.Inherit, CreateCallback(MoveToTop)),
new TorrentAction("Move up", Icons.Material.Filled.ArrowUpward, Color.Inherit, CreateCallback(MoveUp)),
new TorrentAction("Move down", Icons.Material.Filled.ArrowDownward, Color.Inherit, CreateCallback(MoveDown)),
new TorrentAction("Move to bottom", Icons.Material.Filled.VerticalAlignBottom, Color.Inherit, CreateCallback(MoveToBottom)),
}),
new TorrentAction("Copy", Icons.Material.Filled.FolderCopy, Color.Info, new List<TorrentAction>
{
new TorrentAction("Name", Icons.Material.Filled.TextFields, Color.Info, CreateCallback(() => Copy(t => t.Name))),
new TorrentAction("Info hash v1", Icons.Material.Filled.Tag, Color.Info, CreateCallback(() => Copy(t => t.InfoHashV1))),
new TorrentAction("Info hash v2", Icons.Material.Filled.Tag, Color.Info, CreateCallback(() => Copy(t => t.InfoHashV2))),
new TorrentAction("Magnet link", Icons.Material.Filled.TextFields, Color.Info, CreateCallback(() => Copy(t => t.MagnetUri))),
new TorrentAction("Torrent ID", Icons.Material.Filled.TextFields, Color.Info, CreateCallback(() => Copy(t => t.Hash))),
}),
new TorrentAction("Export", Icons.Material.Filled.SaveAlt, Color.Info, CreateCallback(Export)),
};
return _actions; if (torrent.State != "pausedUP" && torrent.State != "pausedDL")
{
allArePaused = false;
}
else
{
thereArePaused = true;
}
if (!torrent.ForceStart)
{
allAreForceStart = false;
}
else
{
thereAreForceStart = true;
}
if (torrent.AutomaticTorrentManagement)
{
thereAreAutoTmm = true;
}
else
{
allAreAutoTmm = false;
}
} }
bool showSequentialDownload = true;
if (!allAreSequentialDownload && thereAreSequentialDownload)
{
showSequentialDownload = false;
}
bool showAreFirstLastPiecePrio = true;
if (!allAreFirstLastPiecePrio && thereAreFirstLastPiecePrio)
{
showAreFirstLastPiecePrio = false;
}
var actionStates = new Dictionary<string, ActionState>();
var showRenameFiles = Hashes.Count() == 1 && firstTorrent!.MetaDownloaded();
if (!showRenameFiles)
{
actionStates["renameFiles"] = ActionState.Hidden;
}
if (allAreDownloaded)
{
actionStates["downloadLimit"] = ActionState.Hidden;
actionStates["uploadLimit"] = ActionState.HasSeperator;
actionStates["sequentialDownload"] = ActionState.Hidden;
actionStates["firstLastPiecePrio"] = ActionState.Hidden;
actionStates["superSeeding"] = new ActionState { IsChecked = allAreSuperSeeding };
}
else
{
if (!showSequentialDownload && showAreFirstLastPiecePrio)
{
actionStates["firstLastPiecePrio"] = ActionState.HasSeperator;
}
if (!showSequentialDownload)
{
actionStates["sequentialDownload"] = ActionState.Hidden;
}
if (!showAreFirstLastPiecePrio)
{
actionStates["firstLastPiecePrio"] = ActionState.Hidden;
}
if (!actionStates.TryGetValue("sequentialDownload", out var sequentialDownload))
{
actionStates["sequentialDownload"] = new ActionState { IsChecked = allAreSequentialDownload };
}
else
{
sequentialDownload.IsChecked = allAreSequentialDownload;
}
if (!actionStates.TryGetValue("firstLastPiecePrio", out var firstLastPiecePrio))
{
actionStates["firstLastPiecePrio"] = new ActionState { IsChecked = allAreFirstLastPiecePrio };
}
else
{
firstLastPiecePrio.IsChecked = allAreFirstLastPiecePrio;
}
actionStates["superSeeding"] = ActionState.Hidden;
}
if (allArePaused)
{
actionStates["pause"] = ActionState.Hidden;
}
else if (allAreForceStart)
{
actionStates["forceStart"] = ActionState.Hidden;
}
else if (!thereArePaused && !thereAreForceStart)
{
actionStates["start"] = ActionState.Hidden;
}
if (!allAreAutoTmm && thereAreAutoTmm)
{
actionStates["autoTorrentManagement"] = ActionState.Hidden;
}
else
{
actionStates["autoTorrentManagement"] = new ActionState { IsChecked = allAreAutoTmm };
}
return Filter(actionStates);
}
private IEnumerable<TorrentAction> Filter(Dictionary<string, ActionState> actionStates)
{
foreach (var action in _actions)
{
if (!actionStates.TryGetValue(action.Name, out var actionState))
{
yield return action;
}
else
{
if (actionState.Show is null || actionState.Show.Value)
{
var act = action with { };
if (actionState.HasSeparator.HasValue)
{
act.SeparatorBefore = actionState.HasSeparator.Value;
}
if (actionState.IsChecked.HasValue)
{
act.IsChecked = actionState.IsChecked.Value;
}
}
}
}
}
private sealed class ActionState
{
public bool? Show { get; set; }
public bool? HasSeparator { get; set; }
public bool? IsChecked { get; set; }
public static readonly ActionState Hidden = new ActionState { Show = false };
public static readonly ActionState HasSeperator = new ActionState { HasSeparator = true };
} }
private EventCallback CreateCallback(Func<Task> action, bool ignoreAfterAction = false) private EventCallback CreateCallback(Func<Task> action, bool ignoreAfterAction = false)
@@ -351,46 +565,53 @@ namespace Lantean.QBTMudBlade.Components
Children, Children,
} }
public class Divider : TorrentAction public record TorrentAction
{ {
public Divider() : base("-", default!, Color.Default, default(EventCallback)) private readonly Color _color;
{
}
}
public class TorrentAction public TorrentAction(string name, string text, string? icon, Color color, EventCallback callback, bool separatorBefore = false)
{
public TorrentAction(string name, string? icon, Color color, EventCallback callback)
{ {
Name = name; Name = name;
Text = text;
Icon = icon; Icon = icon;
Color = color; _color = color;
Callback = callback; Callback = callback;
SeparatorBefore = separatorBefore;
Children = []; Children = [];
} }
public TorrentAction(string name, string? icon, Color color, IEnumerable<TorrentAction> children, bool multiAction = false, bool useTextButton = false) public TorrentAction(string name, string text, string? icon, Color color, IEnumerable<TorrentAction> children, bool multiAction = false, bool useTextButton = false, bool separatorBefore = false)
{ {
Name = name; Name = name;
Text = text;
Icon = icon; Icon = icon;
Color = color; _color = color;
Callback = default; Callback = default;
Children = children; Children = children;
UseTextButton = useTextButton; UseTextButton = useTextButton;
SeparatorBefore = separatorBefore;
} }
public string Name { get; } public string Name { get; }
public string Text { get; }
public string? Icon { get; } public string? Icon { get; }
public Color Color { get; }
public Color Color => IsChecked is null || IsChecked.Value ? _color : Color.Transparent;
public EventCallback Callback { get; } public EventCallback Callback { get; }
public bool SeparatorBefore { get; set; }
public IEnumerable<TorrentAction> Children { get; } public IEnumerable<TorrentAction> Children { get; }
public bool UseTextButton { get; } public bool UseTextButton { get; }
public bool MultiAction { get; } public bool MultiAction { get; }
public bool? IsChecked { get; internal set; }
} }
} }

View File

@@ -194,6 +194,25 @@ namespace Lantean.QBTMudBlade
await onSuccess((T)dialogResult.Data); await onSuccess((T)dialogResult.Data);
} }
public static async Task InvokeDownloadRateDialog(this IDialogService dialogService, IApiClient apiClient, long rate, IEnumerable<string> hashes)
{
var parameters = new DialogParameters
{
{ nameof(SliderFieldDialog<long>.Value), rate },
{ nameof(SliderFieldDialog<long>.Min), 0L },
{ nameof(SliderFieldDialog<long>.Max), 100L },
};
var result = await dialogService.ShowAsync<SliderFieldDialog<long>>("Download Rate", parameters, FormDialogOptions);
var dialogResult = await result.Result;
if (dialogResult.Canceled)
{
return;
}
await apiClient.SetTorrentDownloadLimit((long)dialogResult.Data, null, hashes.ToArray());
}
public static async Task InvokeUploadRateDialog(this IDialogService dialogService, IApiClient apiClient, long rate, IEnumerable<string> hashes) public static async Task InvokeUploadRateDialog(this IDialogService dialogService, IApiClient apiClient, long rate, IEnumerable<string> hashes)
{ {
var parameters = new DialogParameters var parameters = new DialogParameters
@@ -282,7 +301,7 @@ namespace Lantean.QBTMudBlade
{ nameof(SubMenuDialog.Preferences), preferences }, { nameof(SubMenuDialog.Preferences), preferences },
}; };
await dialogService.ShowAsync<SubMenuDialog>(parent.Name, parameters, FormDialogOptions); await dialogService.ShowAsync<SubMenuDialog>(parent.Text, parameters, FormDialogOptions);
} }
} }
} }

View File

@@ -49,5 +49,15 @@ namespace Lantean.QBTMudBlade
// disposed // disposed
} }
} }
public static bool IsFinished(this Torrent torrent)
{
return torrent.TotalSize == torrent.Downloaded;
}
public static bool MetaDownloaded(this Torrent torrent)
{
return !(torrent.State == "metaDL" || torrent.State == "forcedMetaDL" || torrent.TotalSize == -1);
}
} }
} }

View File

@@ -45,7 +45,7 @@ namespace Lantean.QBTMudBlade.Models
public long Downloaded => (long)Math.Round(Size * Progress, 0); public long Downloaded => (long)Math.Round(Size * Progress, 0);
public long Remaining => Progress == 1 ? 0 : Size - Downloaded; public long Remaining => Progress == 1 || Priority == Priority.DoNotDownload ? 0 : Size - Downloaded;
public bool IsFolder { get; } public bool IsFolder { get; }

View File

@@ -47,12 +47,10 @@ namespace Lantean.QBTMudBlade.Pages
} }
#if DEBUG #if DEBUG
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
await DoLogin("admin", "23mIDZhvT"); await DoLogin("admin", "MX6r8xzTP");
} }
#endif #endif
} }

View File

@@ -0,0 +1,9 @@
# qbt-mud
## To-Do
- Files -> Context menu alternative
- Details -> Fixed height
- Rename multiple files dialog
- RSS feeds and dialogs
- Search and dialogs