using System.Linq; using Lantean.QBitTorrentClient; using ShareLimitAction = Lantean.QBitTorrentClient.Models.ShareLimitAction; using Lantean.QBTMud.Components.Dialogs; using Lantean.QBTMud.Filter; using Lantean.QBTMud.Models; using MudBlazor; namespace Lantean.QBTMud.Helpers { public static class DialogHelper { public const long _maxFileSize = 4194304; public static readonly DialogOptions ConfirmDialogOptions = new() { BackgroundClass = "background-blur", MaxWidth = MaxWidth.Small, FullWidth = true }; public static readonly DialogOptions FormDialogOptions = new() { CloseButton = true, MaxWidth = MaxWidth.Medium, BackgroundClass = "background-blur", FullWidth = true }; public static readonly DialogOptions FullScreenDialogOptions = new() { CloseButton = true, MaxWidth = MaxWidth.ExtraExtraLarge, BackgroundClass = "background-blur", FullWidth = true }; public static readonly DialogOptions NonBlurConfirmDialogOptions = new() { MaxWidth = MaxWidth.Small, FullWidth = true }; public static readonly DialogOptions NonBlurFormDialogOptions = new() { CloseButton = true, MaxWidth = MaxWidth.Medium, FullWidth = true }; public static async Task InvokeAddCategoryDialog(this IDialogService dialogService, IApiClient apiClient) { var reference = await dialogService.ShowAsync("Add Category", NonBlurFormDialogOptions); var dialogResult = await reference.Result; if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null) { return null; } var category = (Category)dialogResult.Data; await apiClient.AddCategory(category.Name, category.SavePath); return category.Name; } public static async Task InvokeAddTorrentFileDialog(this IDialogService dialogService, IApiClient apiClient) { var result = await dialogService.ShowAsync("Upload local torrent", FormDialogOptions); var dialogResult = await result.Result; if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null) { return; } var options = (AddTorrentFileOptions)dialogResult.Data; var streams = new List(); var files = new Dictionary(); foreach (var file in options.Files) { var stream = file.OpenReadStream(_maxFileSize); streams.Add(stream); files.Add(file.Name, stream); } var addTorrentParams = CreateAddTorrentParams(options); addTorrentParams.Torrents = files; _ = await apiClient.AddTorrent(addTorrentParams); foreach (var stream in streams) { await stream.DisposeAsync(); } } private static QBitTorrentClient.Models.AddTorrentParams CreateAddTorrentParams(TorrentOptions options) { var addTorrentParams = new QBitTorrentClient.Models.AddTorrentParams(); addTorrentParams.AddToTopOfQueue = options.AddToTopOfQueue; addTorrentParams.AutoTorrentManagement = options.TorrentManagementMode; addTorrentParams.Category = options.Category; if (!string.IsNullOrEmpty(options.ContentLayout)) { addTorrentParams.ContentLayout = Enum.Parse(options.ContentLayout); } addTorrentParams.DownloadLimit = options.DownloadLimit; addTorrentParams.DownloadPath = options.DownloadPath; addTorrentParams.FirstLastPiecePriority = options.DownloadFirstAndLastPiecesFirst; addTorrentParams.InactiveSeedingTimeLimit = options.InactiveSeedingTimeLimit; addTorrentParams.RatioLimit = options.RatioLimit; addTorrentParams.RenameTorrent = options.RenameTorrent; addTorrentParams.SavePath = options.SavePath; addTorrentParams.SeedingTimeLimit = options.SeedingTimeLimit; addTorrentParams.SequentialDownload = options.DownloadInSequentialOrder; if (!string.IsNullOrEmpty(options.ShareLimitAction)) { addTorrentParams.ShareLimitAction = Enum.Parse(options.ShareLimitAction); } addTorrentParams.SkipChecking = options.SkipHashCheck; if (!string.IsNullOrEmpty(options.StopCondition)) { addTorrentParams.StopCondition = Enum.Parse(options.StopCondition); } addTorrentParams.Stopped = !options.StartTorrent; addTorrentParams.Tags = options.Tags; addTorrentParams.UploadLimit = options.UploadLimit; addTorrentParams.UseDownloadPath = options.UseDownloadPath; return addTorrentParams; } public static async Task InvokeAddTorrentLinkDialog(this IDialogService dialogService, IApiClient apiClient, string? url = null) { var parameters = new DialogParameters { { nameof(AddTorrentLinkDialog.Url), url } }; var result = await dialogService.ShowAsync("Download from URLs", parameters, FormDialogOptions); var dialogResult = await result.Result; if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null) { return; } var options = (AddTorrentLinkOptions)dialogResult.Data; var addTorrentParams = CreateAddTorrentParams(options); addTorrentParams.Urls = options.Urls; _ = await apiClient.AddTorrent(addTorrentParams); } public static async Task InvokeDeleteTorrentDialog(this IDialogService dialogService, IApiClient apiClient, bool confirmTorrentDeletion, params string[] hashes) { if (hashes.Length == 0) { return false; } var parameters = new DialogParameters { { nameof(DeleteDialog.Count), hashes.Length } }; if (!confirmTorrentDeletion) { await apiClient.DeleteTorrents(hashes: hashes, deleteFiles: false); return true; } var reference = await dialogService.ShowAsync($"Remove torrent{(hashes.Length == 1 ? "" : "s")}?", parameters, ConfirmDialogOptions); var dialogResult = await reference.Result; if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null) { return false; } await apiClient.DeleteTorrents(hashes, (bool)dialogResult.Data); return true; } public static async Task ForceRecheckAsync(this IDialogService dialogService, IApiClient apiClient, IEnumerable hashes, bool confirmTorrentRecheck) { var hashArray = hashes?.ToArray() ?? []; if (hashArray.Length == 0) { return; } if (confirmTorrentRecheck) { var content = $"Are you sure you want to recheck the selected torrent{(hashArray.Length == 1 ? "" : "s")}?"; var confirmed = await dialogService.ShowConfirmDialog("Force recheck", content); if (!confirmed) { return; } } await apiClient.RecheckTorrents(null, hashArray); } public static async Task InvokeDownloadRateDialog(this IDialogService dialogService, IApiClient apiClient, long rate, IEnumerable hashes) { Func valueDisplayFunc = v => v == Limits.NoLimit ? "∞" : v.ToString(); Func valueGetFunc = v => v == "∞" ? Limits.NoLimit : long.Parse(v); var parameters = new DialogParameters { { nameof(SliderFieldDialog.Min), -1L }, { nameof(SliderFieldDialog.Max), 1000L }, { nameof(SliderFieldDialog.Value), rate / 1024 }, { nameof(SliderFieldDialog.ValueDisplayFunc), valueDisplayFunc }, { nameof(SliderFieldDialog.ValueGetFunc), valueGetFunc }, { nameof(SliderFieldDialog.Label), "Download rate limit" }, { nameof(SliderFieldDialog.Adornment), Adornment.End }, { nameof(SliderFieldDialog.AdornmentText), "KiB/s" }, }; var result = await dialogService.ShowAsync>("Download Rate", parameters, FormDialogOptions); var dialogResult = await result.Result; if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null) { return; } var kibs = (long)dialogResult.Data; await apiClient.SetTorrentDownloadLimit(kibs * 1024, null, hashes.ToArray()); } public static async Task InvokeEditCategoryDialog(this IDialogService dialogService, IApiClient apiClient, string categoryName) { var category = (await apiClient.GetAllCategories()).FirstOrDefault(c => c.Key == categoryName).Value; var parameters = new DialogParameters { { nameof(CategoryPropertiesDialog.Category), category?.Name }, { nameof(CategoryPropertiesDialog.SavePath), category?.SavePath }, }; var reference = await dialogService.ShowAsync("Edit Category", parameters, NonBlurFormDialogOptions); var dialogResult = await reference.Result; if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null) { return null; } var updatedCategory = (Category)dialogResult.Data; await apiClient.EditCategory(updatedCategory.Name, updatedCategory.SavePath); return updatedCategory.Name; } public static async Task InvokeRenameFilesDialog(this IDialogService dialogService, string hash) { var parameters = new DialogParameters { { nameof(RenameFilesDialog.Hash), hash } }; await dialogService.ShowAsync("Rename Files", parameters, FullScreenDialogOptions); } public static async Task InvokeRssRulesDialog(this IDialogService dialogService) { await dialogService.ShowAsync("Edit Rss Auto Downloading Rules", FullScreenDialogOptions); } public static async Task InvokeShareRatioDialog(this IDialogService dialogService, IApiClient apiClient, IEnumerable torrents) { var torrentList = torrents.ToList(); if (torrentList.Count == 0) { return; } var shareRatioValues = torrentList.Select(t => new ShareRatioMax { InactiveSeedingTimeLimit = t.InactiveSeedingTimeLimit, MaxInactiveSeedingTime = t.MaxInactiveSeedingTime, MaxRatio = t.MaxRatio, MaxSeedingTime = t.MaxSeedingTime, RatioLimit = t.RatioLimit, SeedingTimeLimit = t.SeedingTimeLimit, ShareLimitAction = t.ShareLimitAction, }).ToList(); var referenceValue = shareRatioValues.First(); var torrentsHaveSameShareRatio = shareRatioValues.Distinct().Count() == 1; var parameters = new DialogParameters { { nameof(ShareRatioDialog.Value), torrentsHaveSameShareRatio ? referenceValue : null }, { nameof(ShareRatioDialog.CurrentValue), referenceValue }, }; var result = await dialogService.ShowAsync("Share ratio", parameters, FormDialogOptions); var dialogResult = await result.Result; if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null) { return; } var shareRatio = (ShareRatio)dialogResult.Data; await apiClient.SetTorrentShareLimit(shareRatio.RatioLimit, shareRatio.SeedingTimeLimit, shareRatio.InactiveSeedingTimeLimit, shareRatio.ShareLimitAction ?? ShareLimitAction.Default, hashes: torrentList.Select(t => t.Hash).ToArray()); } public static async Task InvokeStringFieldDialog(this IDialogService dialogService, string title, string label, string? value, Func onSuccess) { var result = await dialogService.ShowStringFieldDialog(title, label, value); if (result is not null) { await onSuccess(result); } } public static async Task InvokeUploadRateDialog(this IDialogService dialogService, IApiClient apiClient, long rate, IEnumerable hashes) { Func valueDisplayFunc = v => v == Limits.NoLimit ? "∞" : v.ToString(); Func valueGetFunc = v => v == "∞" ? Limits.NoLimit : long.Parse(v); var parameters = new DialogParameters { { nameof(SliderFieldDialog.Min), -1L }, { nameof(SliderFieldDialog.Max), 1000L }, { nameof(SliderFieldDialog.Value), rate / 1024 }, { nameof(SliderFieldDialog.ValueDisplayFunc), valueDisplayFunc }, { nameof(SliderFieldDialog.ValueGetFunc), valueGetFunc }, { nameof(SliderFieldDialog.Label), "Upload rate limit" }, { nameof(SliderFieldDialog.Adornment), Adornment.End }, { nameof(SliderFieldDialog.AdornmentText), "KiB/s" }, }; var result = await dialogService.ShowAsync>("Upload Rate", parameters, FormDialogOptions); var dialogResult = await result.Result; if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null) { return; } var kibs = (long)dialogResult.Data; await apiClient.SetTorrentUploadLimit(kibs * 1024, null, hashes.ToArray()); } public static async Task?> ShowAddPeersDialog(this IDialogService dialogService) { var reference = await dialogService.ShowAsync("Add Peer", NonBlurFormDialogOptions); var dialogResult = await reference.Result; if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null) { return null; } var peers = (HashSet)dialogResult.Data; return peers; } public static async Task?> ShowAddTagsDialog(this IDialogService dialogService) { var reference = await dialogService.ShowAsync("Add Tags", NonBlurFormDialogOptions); var dialogResult = await reference.Result; if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null) { return null; } var tags = (HashSet)dialogResult.Data; return tags; } public static async Task?> ShowAddTrackersDialog(this IDialogService dialogService) { var reference = await dialogService.ShowAsync("Add Tracker", NonBlurFormDialogOptions); var dialogResult = await reference.Result; if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null) { return null; } var tags = (HashSet)dialogResult.Data; return tags; } public static async Task<(HashSet SelectedColumns, Dictionary ColumnWidths, Dictionary ColumnOrder)> ShowColumnsOptionsDialog(this IDialogService dialogService, List> columnDefinitions, HashSet selectedColumns, Dictionary widths, Dictionary order) { var parameters = new DialogParameters { { nameof(ColumnOptionsDialog.Columns), columnDefinitions }, { nameof(ColumnOptionsDialog.SelectedColumns), selectedColumns }, { nameof(ColumnOptionsDialog.Widths), widths }, { nameof(ColumnOptionsDialog.Order), order }, }; var reference = await dialogService.ShowAsync>("Column Options", parameters, FormDialogOptions); var dialogResult = await reference.Result; if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null) { return default; } return ((HashSet, Dictionary, Dictionary))dialogResult.Data; } public static async Task ShowConfirmDialog(this IDialogService dialogService, string title, string content) { var parameters = new DialogParameters { { nameof(ConfirmDialog.Content), content } }; var result = await dialogService.ShowAsync(title, parameters, ConfirmDialogOptions); var dialogResult = await result.Result; return dialogResult is not null && !dialogResult.Canceled; } public static async Task ShowConfirmDialog(this IDialogService dialogService, string title, string content, Func onSuccess) { var parameters = new DialogParameters { { nameof(ConfirmDialog.Content), content } }; var result = await dialogService.ShowAsync(title, parameters, ConfirmDialogOptions); var dialogResult = await result.Result; if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null) { return; } await onSuccess(); } public static async Task ShowConfirmDialog(this IDialogService dialogService, string title, string content, Action onSuccess) { await dialogService.ShowConfirmDialog(title, content, () => { onSuccess(); return Task.CompletedTask; }); } public static async Task>?> ShowFilterOptionsDialog(this IDialogService dialogService, List>? propertyFilterDefinitions) { var parameters = new DialogParameters { { nameof(FilterOptionsDialog.FilterDefinitions), propertyFilterDefinitions }, }; var result = await dialogService.ShowAsync>("Filters", parameters, FormDialogOptions); var dialogResult = await result.Result; if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null) { return null; } return (List>?)dialogResult.Data; } public static async Task ShowStringFieldDialog(this IDialogService dialogService, string title, string label, string? value) { var parameters = new DialogParameters { { nameof(StringFieldDialog.Label), label }, { nameof(StringFieldDialog.Value), value } }; var result = await dialogService.ShowAsync(title, parameters, FormDialogOptions); var dialogResult = await result.Result; if (dialogResult is null || dialogResult.Canceled || dialogResult.Data is null) { return null; } return (string)dialogResult.Data; } public static async Task ShowSubMenu(this IDialogService dialogService, IEnumerable hashes, UIAction parent, Dictionary torrents, QBitTorrentClient.Models.Preferences? preferences) { var parameters = new DialogParameters { { nameof(SubMenuDialog.ParentAction), parent }, { nameof(SubMenuDialog.Hashes), hashes }, { nameof(SubMenuDialog.Torrents), torrents }, { nameof(SubMenuDialog.Preferences), preferences }, }; await dialogService.ShowAsync(parent.Text, parameters, FormDialogOptions); } } }