From b784bd6b8de9a9fec142577696381262ab9ffe6f Mon Sep 17 00:00:00 2001 From: Robert McRackan Date: Wed, 11 May 2022 22:16:15 -0400 Subject: [PATCH 1/4] Batch actions for visible books: LIberate complete --- Source/LibationWinForms/Form1.Designer.cs | 28 ++++++++++++------- Source/LibationWinForms/Form1.cs | 33 +++++++++++++++++++---- 2 files changed, 47 insertions(+), 14 deletions(-) diff --git a/Source/LibationWinForms/Form1.Designer.cs b/Source/LibationWinForms/Form1.Designer.cs index 21c6c13e..fb916fe2 100644 --- a/Source/LibationWinForms/Form1.Designer.cs +++ b/Source/LibationWinForms/Form1.Designer.cs @@ -47,6 +47,7 @@ this.beginBookBackupsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.beginPdfBackupsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.convertAllM4bToMp3ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.liberateVisible2ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.exportToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.exportLibraryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.quickFiltersToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -55,7 +56,7 @@ this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); this.scanningToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.visibleBooksToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.liberateToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); + this.liberateVisibleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.replaceTagsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.setDownloadedToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.removeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -214,7 +215,8 @@ this.liberateToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.beginBookBackupsToolStripMenuItem, this.beginPdfBackupsToolStripMenuItem, - this.convertAllM4bToMp3ToolStripMenuItem}); + this.convertAllM4bToMp3ToolStripMenuItem, + this.liberateVisible2ToolStripMenuItem}); this.liberateToolStripMenuItem.Name = "liberateToolStripMenuItem"; this.liberateToolStripMenuItem.Size = new System.Drawing.Size(148, 48); this.liberateToolStripMenuItem.Text = "&Liberate"; @@ -240,6 +242,13 @@ this.convertAllM4bToMp3ToolStripMenuItem.Text = "Convert all &M4b to Mp3 [Long-running]..."; this.convertAllM4bToMp3ToolStripMenuItem.Click += new System.EventHandler(this.convertAllM4bToMp3ToolStripMenuItem_Click); // + // liberateVisible2ToolStripMenuItem + // + this.liberateVisible2ToolStripMenuItem.Name = "liberateVisible2ToolStripMenuItem"; + this.liberateVisible2ToolStripMenuItem.Size = new System.Drawing.Size(728, 54); + this.liberateVisible2ToolStripMenuItem.Text = "Liberate &Visible Books: {0}"; + this.liberateVisible2ToolStripMenuItem.Click += new System.EventHandler(this.liberateVisible); + // // exportToolStripMenuItem // this.exportToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { @@ -297,7 +306,7 @@ // visibleBooksToolStripMenuItem // this.visibleBooksToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.liberateToolStripMenuItem1, + this.liberateVisibleToolStripMenuItem, this.replaceTagsToolStripMenuItem, this.setDownloadedToolStripMenuItem, this.removeToolStripMenuItem}); @@ -305,12 +314,12 @@ this.visibleBooksToolStripMenuItem.Size = new System.Drawing.Size(267, 48); this.visibleBooksToolStripMenuItem.Text = "&Visible Books: {0}"; // - // liberateToolStripMenuItem1 + // liberateVisibleToolStripMenuItem // - this.liberateToolStripMenuItem1.Name = "liberateToolStripMenuItem1"; - this.liberateToolStripMenuItem1.Size = new System.Drawing.Size(525, 54); - this.liberateToolStripMenuItem1.Text = "&Liberate"; - this.liberateToolStripMenuItem1.Click += new System.EventHandler(this.liberateToolStripMenuItem1_Click); + this.liberateVisibleToolStripMenuItem.Name = "liberateVisibleToolStripMenuItem"; + this.liberateVisibleToolStripMenuItem.Size = new System.Drawing.Size(525, 54); + this.liberateVisibleToolStripMenuItem.Text = "&Liberate: {0}"; + this.liberateVisibleToolStripMenuItem.Click += new System.EventHandler(this.liberateVisible); // // replaceTagsToolStripMenuItem // @@ -486,9 +495,10 @@ private System.Windows.Forms.ToolStripMenuItem scanningToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem autoScanLibraryToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem visibleBooksToolStripMenuItem; - private System.Windows.Forms.ToolStripMenuItem liberateToolStripMenuItem1; + private System.Windows.Forms.ToolStripMenuItem liberateVisibleToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem replaceTagsToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem setDownloadedToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem removeToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem liberateVisible2ToolStripMenuItem; } } diff --git a/Source/LibationWinForms/Form1.cs b/Source/LibationWinForms/Form1.cs index dcfde7b5..eec78937 100644 --- a/Source/LibationWinForms/Form1.cs +++ b/Source/LibationWinForms/Form1.cs @@ -15,10 +15,13 @@ namespace LibationWinForms { public partial class Form1 : Form { - private string visibleBooksToolStripMenuItem_format { get; } private string beginBookBackupsToolStripMenuItem_format { get; } private string beginPdfBackupsToolStripMenuItem_format { get; } + private string visibleBooksToolStripMenuItem_format { get; } + private string liberateVisibleToolStripMenuItem_format { get; } + private string liberateVisible2ToolStripMenuItem_format { get; } + private ProductsGrid productsGrid { get; } public Form1() @@ -30,9 +33,11 @@ namespace LibationWinForms gridPanel.Controls.Add(productsGrid); // back up string formats - visibleBooksToolStripMenuItem_format = visibleBooksToolStripMenuItem.Text; beginBookBackupsToolStripMenuItem_format = beginBookBackupsToolStripMenuItem.Text; beginPdfBackupsToolStripMenuItem_format = beginPdfBackupsToolStripMenuItem.Text; + visibleBooksToolStripMenuItem_format = visibleBooksToolStripMenuItem.Text; + liberateVisibleToolStripMenuItem_format = liberateVisibleToolStripMenuItem.Text; + liberateVisible2ToolStripMenuItem_format = liberateVisible2ToolStripMenuItem.Text; if (this.DesignMode) return; @@ -534,11 +539,29 @@ namespace LibationWinForms => await Task.Run(setLiberatedVisibleMenuItem); void setLiberatedVisibleMenuItem() { - var notLiberated = productsGrid.GetVisible().Any(lb => lb.Book.UserDefinedItem.BookStatus == DataLayer.LiberatedStatus.NotLiberated); - this.UIThreadSync(() => liberateToolStripMenuItem1.Enabled = notLiberated); + var notLiberated = productsGrid.GetVisible().Count(lb => lb.Book.UserDefinedItem.BookStatus == DataLayer.LiberatedStatus.NotLiberated); + this.UIThreadSync(() => + { + if (notLiberated > 0) + { + liberateVisibleToolStripMenuItem.Text = string.Format(liberateVisibleToolStripMenuItem_format, notLiberated); + liberateVisibleToolStripMenuItem.Enabled = true; + + liberateVisible2ToolStripMenuItem.Text = string.Format(liberateVisible2ToolStripMenuItem_format, notLiberated); + liberateVisible2ToolStripMenuItem.Enabled = true; + } + else + { + liberateVisibleToolStripMenuItem.Text = "All visible books are liberated"; + liberateVisibleToolStripMenuItem.Enabled = false; + + liberateVisible2ToolStripMenuItem.Text = "All visible books are liberated"; + liberateVisible2ToolStripMenuItem.Enabled = false; + } + }); } - private async void liberateToolStripMenuItem1_Click(object sender, EventArgs e) + private async void liberateVisible(object sender, EventArgs e) { var visibleBooks = productsGrid.GetVisible(); await BookLiberation.ProcessorAutomationController.BackupAllBooksAsync(visibleBooks); From 75c5f662dcbef6ec7bc4bc463f2d055cd2381469 Mon Sep 17 00:00:00 2001 From: Robert McRackan Date: Thu, 12 May 2022 09:53:21 -0400 Subject: [PATCH 2/4] * Batch actions for visible books: 'remove from library' complete * refactor entity properties into extension methods * refactor shared simple message boxes => MessageBoxLib --- Source/ApplicationServices/LibraryCommands.cs | 4 +- Source/ApplicationServices/LibraryExporter.cs | 10 +- Source/DataLayer/EfClasses/Book.cs | 46 -------- Source/DataLayer/EfClasses/Rating.cs | 35 ------ Source/DataLayer/EntityExtensions.cs | 102 ++++++++++++++++++ Source/FileLiberator/DownloadDecryptBook.cs | 6 +- Source/FileLiberator/DownloadPdf.cs | 2 +- Source/LibationSearchEngine/SearchEngine.cs | 58 +++++----- .../BookLiberation/AudioDecodeForm.cs | 4 +- .../ProcessorAutomationController.cs | 4 +- .../Dialogs/AccountsDialog.cs | 2 +- .../Dialogs/BookDetailsDialog.cs | 11 +- .../Dialogs/EditTemplateDialog.cs | 2 +- .../Dialogs/RemoveBooksDialog.cs | 38 +++---- .../Dialogs/SettingsDialog.cs | 2 +- Source/LibationWinForms/Form1.cs | 21 ++-- .../LibationWinForms/MessageBoxAlertAdmin.cs | 27 ----- Source/LibationWinForms/MessageBoxLib.cs | 72 +++++++++++++ .../MessageBoxWarnIfVerboseLogging.cs | 28 ----- Source/LibationWinForms/Program.cs | 8 +- Source/LibationWinForms/grid/GridEntry.cs | 18 ++-- Source/LibationWinForms/grid/ProductsGrid.cs | 3 +- 22 files changed, 272 insertions(+), 231 deletions(-) create mode 100644 Source/DataLayer/EntityExtensions.cs delete mode 100644 Source/LibationWinForms/MessageBoxAlertAdmin.cs create mode 100644 Source/LibationWinForms/MessageBoxLib.cs delete mode 100644 Source/LibationWinForms/MessageBoxWarnIfVerboseLogging.cs diff --git a/Source/ApplicationServices/LibraryCommands.cs b/Source/ApplicationServices/LibraryCommands.cs index 8c181354..773a3567 100644 --- a/Source/ApplicationServices/LibraryCommands.cs +++ b/Source/ApplicationServices/LibraryCommands.cs @@ -313,7 +313,7 @@ namespace ApplicationServices // must be here instead of in db layer due to AaxcExists public static LiberatedStatus Liberated_Status(Book book) - => book.Audio_Exists ? book.UserDefinedItem.BookStatus + => book.Audio_Exists() ? book.UserDefinedItem.BookStatus : AudibleFileStorage.AaxcExists(book.AudibleProductId) ? LiberatedStatus.PartialDownload : LiberatedStatus.NotLiberated; @@ -340,7 +340,7 @@ namespace ApplicationServices var boolResults = libraryBooks .AsParallel() - .Where(lb => lb.Book.HasPdf) + .Where(lb => lb.Book.HasPdf()) .Select(lb => Pdf_Status(lb.Book)) .ToList(); var pdfsDownloaded = boolResults.Count(r => r == LiberatedStatus.Liberated); diff --git a/Source/ApplicationServices/LibraryExporter.cs b/Source/ApplicationServices/LibraryExporter.cs index 4508d7ed..3f948e66 100644 --- a/Source/ApplicationServices/LibraryExporter.cs +++ b/Source/ApplicationServices/LibraryExporter.cs @@ -111,13 +111,13 @@ namespace ApplicationServices AudibleProductId = a.Book.AudibleProductId, Locale = a.Book.Locale, Title = a.Book.Title, - AuthorNames = a.Book.AuthorNames, - NarratorNames = a.Book.NarratorNames, + AuthorNames = a.Book.AuthorNames(), + NarratorNames = a.Book.NarratorNames(), LengthInMinutes = a.Book.LengthInMinutes, Description = a.Book.Description, Publisher = a.Book.Publisher, - HasPdf = a.Book.HasPdf, - SeriesNames = a.Book.SeriesNames, + HasPdf = a.Book.HasPdf(), + SeriesNames = a.Book.SeriesNames(), SeriesOrder = a.Book.SeriesLink.Any() ? a.Book.SeriesLink?.Select(sl => $"{sl.Order} : {sl.Series.Name}").Aggregate((a, b) => $"{a}, {b}") : "", CommunityRatingOverall = a.Book.Rating?.OverallRating, CommunityRatingPerformance = a.Book.Rating?.PerformanceRating, @@ -125,7 +125,7 @@ namespace ApplicationServices PictureId = a.Book.PictureId, IsAbridged = a.Book.IsAbridged, DatePublished = a.Book.DatePublished, - CategoriesNames = a.Book.CategoriesNames.Any() ? a.Book.CategoriesNames.Aggregate((a, b) => $"{a}, {b}") : "", + CategoriesNames = a.Book.CategoriesNames().Any() ? a.Book.CategoriesNames().Aggregate((a, b) => $"{a}, {b}") : "", MyRatingOverall = a.Book.UserDefinedItem.Rating.OverallRating, MyRatingPerformance = a.Book.UserDefinedItem.Rating.PerformanceRating, MyRatingStory = a.Book.UserDefinedItem.Rating.StoryRating, diff --git a/Source/DataLayer/EfClasses/Book.cs b/Source/DataLayer/EfClasses/Book.cs index 63d3e7c3..804df40a 100644 --- a/Source/DataLayer/EfClasses/Book.cs +++ b/Source/DataLayer/EfClasses/Book.cs @@ -43,27 +43,10 @@ namespace DataLayer // non-null. use "empty pattern" internal int CategoryId { get; private set; } public Category Category { get; private set; } - public string[] CategoriesNames - => Category is null ? new string[0] - : Category.ParentCategory is null ? new[] { Category.Name } - : new[] { Category.ParentCategory.Name, Category.Name }; - public string[] CategoriesIds - => Category is null ? null - : Category.ParentCategory is null ? new[] { Category.AudibleCategoryId } - : new[] { Category.ParentCategory.AudibleCategoryId, Category.AudibleCategoryId }; - - public string TitleSortable => Formatters.GetSortName(Title); - public string SeriesSortable => Formatters.GetSortName(SeriesNames); // is owned, not optional 1:1 public UserDefinedItem UserDefinedItem { get; private set; } - // UserDefinedItem convenience properties - /// True if IsLiberated or Error. False if NotLiberated - public bool Audio_Exists => UserDefinedItem.BookStatus != LiberatedStatus.NotLiberated; - /// True if exists and IsLiberated. Else false - public bool PDF_Exists => UserDefinedItem.PdfStatus == LiberatedStatus.Liberated; - // is owned, not optional 1:1 /// The product's aggregate community rating public Rating Rating { get; private set; } = new Rating(0, 0, 0); @@ -125,11 +108,7 @@ namespace DataLayer .ToList(); public IEnumerable Authors => getContributions(Role.Author).Select(bc => bc.Contributor).ToList(); - public string AuthorNames => string.Join(", ", Authors.Select(a => a.Name)); - public IEnumerable Narrators => getContributions(Role.Narrator).Select(bc => bc.Contributor).ToList(); - public string NarratorNames => string.Join(", ", Narrators.Select(n => n.Name)); - public string Publisher => getContributions(Role.Publisher).SingleOrDefault()?.Contributor.Name; public void ReplaceAuthors(IEnumerable authors, DbContext context = null) @@ -185,30 +164,6 @@ namespace DataLayer #region series private HashSet _seriesLink; public IEnumerable SeriesLink => _seriesLink?.ToList(); - public string SeriesNames - { - get - { - if (_seriesLink is null) - return ""; - - // first: alphabetical by name - var withNames = _seriesLink - .Where(s => !string.IsNullOrWhiteSpace(s.Series.Name)) - .Select(s => s.Series.Name) - .OrderBy(a => a) - .ToList(); - // then un-named are alpha by series id - var nullNames = _seriesLink - .Where(s => string.IsNullOrWhiteSpace(s.Series.Name)) - .Select(s => s.Series.AudibleSeriesId) - .OrderBy(a => a) - .ToList(); - - var all = withNames.Union(nullNames).ToList(); - return string.Join(", ", all); - } - } public void UpsertSeries(Series series, string order, DbContext context = null) { @@ -230,7 +185,6 @@ namespace DataLayer #region supplements private HashSet _supplements; public IEnumerable Supplements => _supplements?.ToList(); - public bool HasPdf => Supplements.Any(); public void AddSupplementDownloadUrl(string url) { diff --git a/Source/DataLayer/EfClasses/Rating.cs b/Source/DataLayer/EfClasses/Rating.cs index 25ee3e28..761633be 100644 --- a/Source/DataLayer/EfClasses/Rating.cs +++ b/Source/DataLayer/EfClasses/Rating.cs @@ -38,41 +38,6 @@ namespace DataLayer yield return StoryRating; } - public float FirstScore - => OverallRating > 0 ? OverallRating - : PerformanceRating > 0 ? PerformanceRating - : StoryRating; - - /// character: ★ - const char STAR = '\u2605'; - /// character: ½ - const char HALF = '\u00BD'; - string getStars(float score) - { - var fullStars = (int)Math.Floor(score); - - var starString = "".PadLeft(fullStars, STAR); - - if (score - fullStars == 0.5f) - starString += HALF; - - return starString; - } - - public string ToStarString() - { - var items = new List(); - - if (OverallRating > 0) - items.Add($"Overall: {getStars(OverallRating)}"); - if (PerformanceRating > 0) - items.Add($"Perform: {getStars(PerformanceRating)}"); - if (StoryRating > 0) - items.Add($"Story: {getStars(StoryRating)}"); - - return string.Join("\r\n", items); - } - public override string ToString() => $"Overall={OverallRating} Perf={PerformanceRating} Story={StoryRating}"; } } diff --git a/Source/DataLayer/EntityExtensions.cs b/Source/DataLayer/EntityExtensions.cs new file mode 100644 index 00000000..ef18a232 --- /dev/null +++ b/Source/DataLayer/EntityExtensions.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DataLayer +{ + public static class EntityExtensions + { + public static string TitleSortable(this Book book) => Formatters.GetSortName(book.Title); + + public static string AuthorNames(this Book book) => string.Join(", ", book.Authors.Select(a => a.Name)); + public static string NarratorNames(this Book book) => string.Join(", ", book.Narrators.Select(n => n.Name)); + + /// True if IsLiberated or Error. False if NotLiberated + public static bool Audio_Exists(this Book book) => book.UserDefinedItem.BookStatus != LiberatedStatus.NotLiberated; + /// True if exists and IsLiberated. Else false + public static bool PDF_Exists(this Book book) => book.UserDefinedItem.PdfStatus == LiberatedStatus.Liberated; + + public static string SeriesSortable(this Book book) => Formatters.GetSortName(book.SeriesNames()); + public static bool HasPdf(this Book book) => book.Supplements.Any(); + public static string SeriesNames(this Book book) + { + if (book.SeriesLink is null) + return ""; + + // first: alphabetical by name + var withNames = book.SeriesLink + .Where(s => !string.IsNullOrWhiteSpace(s.Series.Name)) + .Select(s => s.Series.Name) + .OrderBy(a => a) + .ToList(); + // then un-named are alpha by series id + var nullNames = book.SeriesLink + .Where(s => string.IsNullOrWhiteSpace(s.Series.Name)) + .Select(s => s.Series.AudibleSeriesId) + .OrderBy(a => a) + .ToList(); + + var all = withNames.Union(nullNames).ToList(); + return string.Join(", ", all); + } + public static string[] CategoriesNames(this Book book) + => book.Category is null ? new string[0] + : book.Category.ParentCategory is null ? new[] { book.Category.Name } + : new[] { book.Category.ParentCategory.Name, book.Category.Name }; + public static string[] CategoriesIds(this Book book) + => book.Category is null ? null + : book.Category.ParentCategory is null ? new[] { book.Category.AudibleCategoryId } + : new[] { book.Category.ParentCategory.AudibleCategoryId, book.Category.AudibleCategoryId }; + + public static string AggregateTitles(this IEnumerable libraryBooks, int max = 5) + { + if (libraryBooks is null || !libraryBooks.Any()) + return ""; + + max = Math.Max(max, 1); + + var titles = libraryBooks.Select(lb => "- " + lb.Book.Title).ToList(); + var titlesAgg = titles.Take(max).Aggregate((a, b) => $"{a}\r\n{b}"); + if (titles.Count == max + 1) + titlesAgg += $"\r\n\r\nand 1 other"; + else if (titles.Count > max + 1) + titlesAgg += $"\r\n\r\nand {titles.Count - max } others"; + return titlesAgg; + } + + public static float FirstScore(this Rating rating) + => rating.OverallRating > 0 ? rating.OverallRating + : rating.PerformanceRating > 0 ? rating.PerformanceRating + : rating.StoryRating; + public static string ToStarString(this Rating rating) + { + var items = new List(); + + if (rating.OverallRating > 0) + items.Add($"Overall: {getStars(rating.OverallRating)}"); + if (rating.PerformanceRating > 0) + items.Add($"Perform: {getStars(rating.PerformanceRating)}"); + if (rating.StoryRating > 0) + items.Add($"Story: {getStars(rating.StoryRating)}"); + + return string.Join("\r\n", items); + } + /// character: ★ + const char STAR = '\u2605'; + /// character: ½ + const char HALF = '\u00BD'; + private static string getStars(float score) + { + var fullStars = (int)Math.Floor(score); + + var starString = new string(STAR, fullStars); + + if (score - fullStars >= 0.25f) + starString += HALF; + + return starString; + } + } +} diff --git a/Source/FileLiberator/DownloadDecryptBook.cs b/Source/FileLiberator/DownloadDecryptBook.cs index 0717ddc4..3cfc687f 100644 --- a/Source/FileLiberator/DownloadDecryptBook.cs +++ b/Source/FileLiberator/DownloadDecryptBook.cs @@ -17,7 +17,7 @@ namespace FileLiberator { private AudiobookDownloadBase abDownloader; - public override bool Validate(LibraryBook libraryBook) => !libraryBook.Book.Audio_Exists; + public override bool Validate(LibraryBook libraryBook) => !libraryBook.Book.Audio_Exists(); public override void Cancel() => abDownloader?.Cancel(); @@ -42,7 +42,7 @@ namespace FileLiberator try { - if (libraryBook.Book.Audio_Exists) + if (libraryBook.Book.Audio_Exists()) return new StatusHandler { "Cannot find decrypt. Final audio file already exists" }; bool success = false; @@ -117,7 +117,7 @@ namespace FileLiberator : new AaxcDownloadSingleConverter(outFileName, cacheDir, audiobookDlLic); if (config.AllowLibationFixup) - converter.RetrievedMetadata += (_, tags) => tags.Generes = string.Join(", ", libraryBook.Book.CategoriesNames); + converter.RetrievedMetadata += (_, tags) => tags.Generes = string.Join(", ", libraryBook.Book.CategoriesNames()); abDownloader = converter; } diff --git a/Source/FileLiberator/DownloadPdf.cs b/Source/FileLiberator/DownloadPdf.cs index bd4817e2..a9e047a7 100644 --- a/Source/FileLiberator/DownloadPdf.cs +++ b/Source/FileLiberator/DownloadPdf.cs @@ -16,7 +16,7 @@ namespace FileLiberator { public override bool Validate(LibraryBook libraryBook) => !string.IsNullOrWhiteSpace(getdownloadUrl(libraryBook)) - && !libraryBook.Book.PDF_Exists; + && !libraryBook.Book.PDF_Exists(); public override async Task ProcessAsync(LibraryBook libraryBook) { diff --git a/Source/LibationSearchEngine/SearchEngine.cs b/Source/LibationSearchEngine/SearchEngine.cs index ac365a35..f10ea71c 100644 --- a/Source/LibationSearchEngine/SearchEngine.cs +++ b/Source/LibationSearchEngine/SearchEngine.cs @@ -30,8 +30,14 @@ namespace LibationSearchEngine // the workaround which allows displaying all books when query is empty public const string ALL_QUERY = "*:*"; - #region index rules - private static ReadOnlyDictionary> idIndexRules { get; } + #region index rules + // common fields used in the "all" default search field + public const string ALL_AUDIBLE_PRODUCT_ID = nameof(Book.AudibleProductId); + public const string ALL_TITLE = nameof(Book.Title); + public const string ALL_AUTHOR_NAMES = "AuthorNames"; + public const string ALL_NARRATOR_NAMES = "NarratorNames"; + + private static ReadOnlyDictionary> idIndexRules { get; } = new ReadOnlyDictionary>( new Dictionary> { @@ -50,15 +56,15 @@ namespace LibationSearchEngine [nameof(Book.DatePublished)] = lb => lb.Book.DatePublished?.ToLuceneString(), [nameof(Book.Title)] = lb => lb.Book.Title, - [nameof(Book.AuthorNames)] = lb => lb.Book.AuthorNames, - ["Author"] = lb => lb.Book.AuthorNames, - ["Authors"] = lb => lb.Book.AuthorNames, - [nameof(Book.NarratorNames)] = lb => lb.Book.NarratorNames, - ["Narrator"] = lb => lb.Book.NarratorNames, - ["Narrators"] = lb => lb.Book.NarratorNames, + [ALL_AUTHOR_NAMES] = lb => lb.Book.AuthorNames(), + ["Author"] = lb => lb.Book.AuthorNames(), + ["Authors"] = lb => lb.Book.AuthorNames(), + [ALL_NARRATOR_NAMES] = lb => lb.Book.NarratorNames(), + ["Narrator"] = lb => lb.Book.NarratorNames(), + ["Narrators"] = lb => lb.Book.NarratorNames(), [nameof(Book.Publisher)] = lb => lb.Book.Publisher, - [nameof(Book.SeriesNames)] = lb => string.Join( + ["SeriesNames"] = lb => string.Join( ", ", lb.Book.SeriesLink .Where(s => !string.IsNullOrWhiteSpace(s.Series.Name)) @@ -70,11 +76,11 @@ namespace LibationSearchEngine .Select(s => s.Series.AudibleSeriesId)), ["SeriesId"] = lb => string.Join(", ", lb.Book.SeriesLink.Select(s => s.Series.AudibleSeriesId)), - [nameof(Book.CategoriesNames)] = lb => lb.Book.CategoriesIds is null ? null : string.Join(", ", lb.Book.CategoriesIds), - [nameof(Book.Category)] = lb => lb.Book.CategoriesIds is null ? null : string.Join(", ", lb.Book.CategoriesIds), - ["Categories"] = lb => lb.Book.CategoriesIds is null ? null : string.Join(", ", lb.Book.CategoriesIds), - ["CategoriesId"] = lb => lb.Book.CategoriesIds is null ? null : string.Join(", ", lb.Book.CategoriesIds), - ["CategoryId"] = lb => lb.Book.CategoriesIds is null ? null : string.Join(", ", lb.Book.CategoriesIds), + ["CategoriesNames"] = lb => lb.Book.CategoriesIds() is null ? null : string.Join(", ", lb.Book.CategoriesIds()), + [nameof(Book.Category)] = lb => lb.Book.CategoriesIds() is null ? null : string.Join(", ", lb.Book.CategoriesIds()), + ["Categories"] = lb => lb.Book.CategoriesIds() is null ? null : string.Join(", ", lb.Book.CategoriesIds()), + ["CategoriesId"] = lb => lb.Book.CategoriesIds() is null ? null : string.Join(", ", lb.Book.CategoriesIds()), + ["CategoryId"] = lb => lb.Book.CategoriesIds() is null ? null : string.Join(", ", lb.Book.CategoriesIds()), [TAGS.FirstCharToUpper()] = lb => lb.Book.UserDefinedItem.Tags, @@ -107,14 +113,14 @@ namespace LibationSearchEngine = new ReadOnlyDictionary>( new Dictionary> { - ["HasDownloads"] = lb => lb.Book.HasPdf, - ["HasDownload"] = lb => lb.Book.HasPdf, - ["Downloads"] = lb => lb.Book.HasPdf, - ["Download"] = lb => lb.Book.HasPdf, - ["HasPDFs"] = lb => lb.Book.HasPdf, - ["HasPDF"] = lb => lb.Book.HasPdf, - ["PDFs"] = lb => lb.Book.HasPdf, - ["PDF"] = lb => lb.Book.HasPdf, + ["HasDownloads"] = lb => lb.Book.HasPdf(), + ["HasDownload"] = lb => lb.Book.HasPdf(), + ["Downloads"] = lb => lb.Book.HasPdf(), + ["Download"] = lb => lb.Book.HasPdf(), + ["HasPDFs"] = lb => lb.Book.HasPdf(), + ["HasPDF"] = lb => lb.Book.HasPdf(), + ["PDFs"] = lb => lb.Book.HasPdf(), + ["PDF"] = lb => lb.Book.HasPdf(), ["IsRated"] = lb => lb.Book.UserDefinedItem.Rating.OverallRating > 0f, ["Rated"] = lb => lb.Book.UserDefinedItem.Rating.OverallRating > 0f, @@ -151,10 +157,10 @@ namespace LibationSearchEngine private static IEnumerable> allFieldIndexRules { get; } = new List> { - idIndexRules[nameof(Book.AudibleProductId)], - stringIndexRules[nameof(Book.Title)], - stringIndexRules[nameof(Book.AuthorNames)], - stringIndexRules[nameof(Book.NarratorNames)] + idIndexRules[ALL_AUDIBLE_PRODUCT_ID], + stringIndexRules[ALL_TITLE], + stringIndexRules[ALL_AUTHOR_NAMES], + stringIndexRules[ALL_NARRATOR_NAMES] }; #endregion diff --git a/Source/LibationWinForms/BookLiberation/AudioDecodeForm.cs b/Source/LibationWinForms/BookLiberation/AudioDecodeForm.cs index 56775e8e..73d43562 100644 --- a/Source/LibationWinForms/BookLiberation/AudioDecodeForm.cs +++ b/Source/LibationWinForms/BookLiberation/AudioDecodeForm.cs @@ -26,8 +26,8 @@ namespace LibationWinForms.BookLiberation //Set default values from library AudioDecodable_TitleDiscovered(sender, libraryBook.Book.Title); - AudioDecodable_AuthorsDiscovered(sender, libraryBook.Book.AuthorNames); - AudioDecodable_NarratorsDiscovered(sender, libraryBook.Book.NarratorNames); + AudioDecodable_AuthorsDiscovered(sender, libraryBook.Book.AuthorNames()); + AudioDecodable_NarratorsDiscovered(sender, libraryBook.Book.NarratorNames()); AudioDecodable_CoverImageDiscovered(sender, PictureStorage.GetPicture( new PictureDefinition( diff --git a/Source/LibationWinForms/BookLiberation/ProcessorAutomationController.cs b/Source/LibationWinForms/BookLiberation/ProcessorAutomationController.cs index eb6ac3af..299f5aec 100644 --- a/Source/LibationWinForms/BookLiberation/ProcessorAutomationController.cs +++ b/Source/LibationWinForms/BookLiberation/ProcessorAutomationController.cs @@ -240,8 +240,8 @@ namespace LibationWinForms.BookLiberation details = $@" Title: {libraryBook.Book.Title} ID: {libraryBook.Book.AudibleProductId} - Author: {trunc(libraryBook.Book.AuthorNames)} - Narr: {trunc(libraryBook.Book.NarratorNames)}"; + Author: {trunc(libraryBook.Book.AuthorNames())} + Narr: {trunc(libraryBook.Book.NarratorNames())}"; } catch { diff --git a/Source/LibationWinForms/Dialogs/AccountsDialog.cs b/Source/LibationWinForms/Dialogs/AccountsDialog.cs index 14c67523..b31009b3 100644 --- a/Source/LibationWinForms/Dialogs/AccountsDialog.cs +++ b/Source/LibationWinForms/Dialogs/AccountsDialog.cs @@ -128,7 +128,7 @@ namespace LibationWinForms.Dialogs } catch (Exception ex) { - MessageBoxAlertAdmin.Show("Error attempting to save accounts", "Error saving accounts", ex); + MessageBoxLib.ShowAdminAlert("Error attempting to save accounts", "Error saving accounts", ex); } } diff --git a/Source/LibationWinForms/Dialogs/BookDetailsDialog.cs b/Source/LibationWinForms/Dialogs/BookDetailsDialog.cs index 89184b00..fdbc7ac7 100644 --- a/Source/LibationWinForms/Dialogs/BookDetailsDialog.cs +++ b/Source/LibationWinForms/Dialogs/BookDetailsDialog.cs @@ -46,15 +46,16 @@ namespace LibationWinForms.Dialogs var t = @$" Title: {Book.Title} -Author(s): {Book.AuthorNames} -Narrator(s): {Book.NarratorNames} +Author(s): {Book.AuthorNames()} +Narrator(s): {Book.NarratorNames()} Length: {(Book.LengthInMinutes == 0 ? "" : $"{Book.LengthInMinutes / 60} hr {Book.LengthInMinutes % 60} min")} -Category: {string.Join(" > ", Book.CategoriesNames)} +Category: {string.Join(" > ", Book.CategoriesNames())} Purchase Date: {_libraryBook.DateAdded.ToString("d")} ".Trim(); - if (!string.IsNullOrWhiteSpace(Book.SeriesNames)) - t += $"\r\nSeries: {Book.SeriesNames}"; + var seriesNames = Book.SeriesNames(); + if (!string.IsNullOrWhiteSpace(seriesNames)) + t += $"\r\nSeries: {seriesNames}"; var bookRating = Book.Rating?.ToStarString(); if (!string.IsNullOrWhiteSpace(bookRating)) diff --git a/Source/LibationWinForms/Dialogs/EditTemplateDialog.cs b/Source/LibationWinForms/Dialogs/EditTemplateDialog.cs index 734a49d4..693072f7 100644 --- a/Source/LibationWinForms/Dialogs/EditTemplateDialog.cs +++ b/Source/LibationWinForms/Dialogs/EditTemplateDialog.cs @@ -46,7 +46,7 @@ namespace LibationWinForms.Dialogs if (template is null) { - MessageBoxAlertAdmin.Show($"Programming error. {nameof(EditTemplateDialog)} was not created correctly", "Edit template error", new NullReferenceException($"{nameof(template)} is null")); + MessageBoxLib.ShowAdminAlert($"Programming error. {nameof(EditTemplateDialog)} was not created correctly", "Edit template error", new NullReferenceException($"{nameof(template)} is null")); return; } diff --git a/Source/LibationWinForms/Dialogs/RemoveBooksDialog.cs b/Source/LibationWinForms/Dialogs/RemoveBooksDialog.cs index 1f7e18d9..2dac8bd9 100644 --- a/Source/LibationWinForms/Dialogs/RemoveBooksDialog.cs +++ b/Source/LibationWinForms/Dialogs/RemoveBooksDialog.cs @@ -77,7 +77,7 @@ namespace LibationWinForms.Dialogs } catch (Exception ex) { - MessageBoxAlertAdmin.Show( + MessageBoxLib.ShowAdminAlert( "Error scanning library. You may still manually select books to remove from Libation's library.", "Error scanning library", ex); @@ -95,34 +95,22 @@ namespace LibationWinForms.Dialogs if (selectedBooks.Count == 0) return; - var titles = selectedBooks.Select(rge => "- " + rge.Title).ToList(); - var titlesAgg = titles.Take(5).Aggregate((a, b) => $"{a}\r\n{b}"); - if (titles.Count == 6) - titlesAgg += $"\r\n\r\nand 1 other"; - else if (titles.Count > 6) - titlesAgg += $"\r\n\r\nand {titles.Count - 5} others"; + var libraryBooks = selectedBooks.Select(rge => rge.LibraryBook).ToList(); + var result = MessageBoxLib.ShowConfirmationDialog( + libraryBooks, + $"Are you sure you want to remove {0} from Libation's library?", + "Remove books from Libation?"); - string thisThese = selectedBooks.Count > 1 ? "these" : "this"; - string bookBooks = selectedBooks.Count > 1 ? "books" : "book"; + if (result != DialogResult.Yes) + return; - var result = MessageBox.Show( - this, - $"Are you sure you want to remove {thisThese} {selectedBooks.Count} {bookBooks} from Libation's library?\r\n\r\n{titlesAgg}", - "Remove books from Libation?", - MessageBoxButtons.YesNo, - MessageBoxIcon.Question, - MessageBoxDefaultButton.Button1); + var idsToRemove = libraryBooks.Select(lb => lb.Book.AudibleProductId).ToList(); + var removeLibraryBooks = await LibraryCommands.RemoveBooksAsync(idsToRemove); - if (result == DialogResult.Yes) - { - var idsToRemove = selectedBooks.Select(rge => rge.AudibleProductId).ToList(); - var removeLibraryBooks = await LibraryCommands.RemoveBooksAsync(idsToRemove); + foreach (var rEntry in selectedBooks) + _removableGridEntries.Remove(rEntry); - foreach (var rEntry in selectedBooks) - _removableGridEntries.Remove(rEntry); - - UpdateSelection(); - } + UpdateSelection(); } private void UpdateSelection() diff --git a/Source/LibationWinForms/Dialogs/SettingsDialog.cs b/Source/LibationWinForms/Dialogs/SettingsDialog.cs index 0741a0f3..cc84ecc2 100644 --- a/Source/LibationWinForms/Dialogs/SettingsDialog.cs +++ b/Source/LibationWinForms/Dialogs/SettingsDialog.cs @@ -181,7 +181,7 @@ namespace LibationWinForms.Dialogs // only warn if changed during this time. don't want to warn every time user happens to change settings while level is verbose if (logLevelOld != logLevelNew) - MessageBoxVerboseLoggingWarning.ShowIfTrue(); + MessageBoxLib.VerboseLoggingWarning_ShowIfTrue(); } config.AllowLibationFixup = allowLibationFixupCbox.Checked; diff --git a/Source/LibationWinForms/Form1.cs b/Source/LibationWinForms/Form1.cs index eec78937..1d4101e8 100644 --- a/Source/LibationWinForms/Form1.cs +++ b/Source/LibationWinForms/Form1.cs @@ -404,7 +404,7 @@ namespace LibationWinForms } catch (Exception ex) { - MessageBoxAlertAdmin.Show( + MessageBoxLib.ShowAdminAlert( "Error importing library. Please try again. If this still happens after 2 or 3 tries, stop and contact administrator", "Error importing library", ex); @@ -468,7 +468,7 @@ namespace LibationWinForms } catch (Exception ex) { - MessageBoxAlertAdmin.Show("Error attempting to export your library.", "Error exporting", ex); + MessageBoxLib.ShowAdminAlert("Error attempting to export your library.", "Error exporting", ex); } } #endregion @@ -562,10 +562,7 @@ namespace LibationWinForms } private async void liberateVisible(object sender, EventArgs e) - { - var visibleBooks = productsGrid.GetVisible(); - await BookLiberation.ProcessorAutomationController.BackupAllBooksAsync(visibleBooks); - } + => await BookLiberation.ProcessorAutomationController.BackupAllBooksAsync(productsGrid.GetVisible()); private void replaceTagsToolStripMenuItem_Click(object sender, EventArgs e) { @@ -585,7 +582,17 @@ namespace LibationWinForms private async void removeToolStripMenuItem_Click(object sender, EventArgs e) { - var visibleIds = productsGrid.GetVisible().Select(lb => lb.Book.AudibleProductId).ToList(); + var libraryBooks = productsGrid.GetVisible(); + + var result = MessageBoxLib.ShowConfirmationDialog( + libraryBooks, + $"Are you sure you want to remove {0} from Libation's library?", + "Remove books from Libation?"); + + if (result != DialogResult.Yes) + return; + + var visibleIds = libraryBooks.Select(lb => lb.Book.AudibleProductId).ToList(); await LibraryCommands.RemoveBooksAsync(visibleIds); } #endregion diff --git a/Source/LibationWinForms/MessageBoxAlertAdmin.cs b/Source/LibationWinForms/MessageBoxAlertAdmin.cs deleted file mode 100644 index 3d68836f..00000000 --- a/Source/LibationWinForms/MessageBoxAlertAdmin.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using LibationWinForms.Dialogs; - -namespace LibationWinForms -{ - public static class MessageBoxAlertAdmin - { - /// - /// Logs error. Displays a message box dialog with specified text and caption. - /// - /// The text to display in the message box. - /// The text to display in the title bar of the message box. - /// Exception to log - /// One of the System.Windows.Forms.DialogResult values. - public static System.Windows.Forms.DialogResult Show(string text, string caption, Exception exception) - { - try - { - Serilog.Log.Logger.Error(exception, "Alert admin error: {@DebugText}", new { text, caption }); - } - catch { } - - using var form = new MessageBoxAlertAdminDialog(text, caption, exception); - return form.ShowDialog(); - } - } -} diff --git a/Source/LibationWinForms/MessageBoxLib.cs b/Source/LibationWinForms/MessageBoxLib.cs new file mode 100644 index 00000000..926cf747 --- /dev/null +++ b/Source/LibationWinForms/MessageBoxLib.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; +using DataLayer; +using Dinah.Core.Logging; +using LibationWinForms.Dialogs; +using Serilog; + +namespace LibationWinForms +{ + public static class MessageBoxLib + { + /// + /// Logs error. Displays a message box dialog with specified text and caption. + /// + /// The text to display in the message box. + /// The text to display in the title bar of the message box. + /// Exception to log + /// One of the System.Windows.Forms.DialogResult values. + public static DialogResult ShowAdminAlert(string text, string caption, Exception exception) + { + try + { + Serilog.Log.Logger.Error(exception, "Alert admin error: {@DebugText}", new { text, caption }); + } + catch { } + + using var form = new MessageBoxAlertAdminDialog(text, caption, exception); + return form.ShowDialog(); + } + + public static void VerboseLoggingWarning_ShowIfTrue() + { + // when turning on debug (and especially Verbose) to share logs, some privacy settings may not be obscured + if (Log.Logger.IsVerboseEnabled()) + MessageBox.Show(@" +Warning: verbose logging is enabled. + +This should be used for debugging only. It creates many +more logs and debug files, neither of which are as +strictly anonymous. + +When you are finished debugging, it's highly recommended +to set your debug MinimumLevel to Information and restart +Libation. +".Trim(), "Verbose logging enabled", MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + + public static DialogResult ShowConfirmationDialog(IEnumerable libraryBooks, string format, string title) + { + if (libraryBooks is null || !libraryBooks.Any()) + return DialogResult.Cancel; + + var count = libraryBooks.Count(); + + string thisThese = count > 1 ? "these" : "this"; + string bookBooks = count > 1 ? "books" : "book"; + string titlesAgg = libraryBooks.AggregateTitles(); + + var message + = string.Format(format, $"{thisThese} {count} {bookBooks}") + + $"\r\n\r\n{titlesAgg}"; + return MessageBox.Show( + message, + title, + MessageBoxButtons.YesNo, + MessageBoxIcon.Question, + MessageBoxDefaultButton.Button1); + } + } +} diff --git a/Source/LibationWinForms/MessageBoxWarnIfVerboseLogging.cs b/Source/LibationWinForms/MessageBoxWarnIfVerboseLogging.cs deleted file mode 100644 index 0f47ef3c..00000000 --- a/Source/LibationWinForms/MessageBoxWarnIfVerboseLogging.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Linq; -using Dinah.Core.Logging; -using Serilog; -using System.Windows.Forms; - -namespace LibationWinForms -{ - public static class MessageBoxVerboseLoggingWarning - { - public static void ShowIfTrue() - { - // when turning on debug (and especially Verbose) to share logs, some privacy settings may not be obscured - if (Log.Logger.IsVerboseEnabled()) - MessageBox.Show(@" -Warning: verbose logging is enabled. - -This should be used for debugging only. It creates many -more logs and debug files, neither of which are as -strictly anonymous. - -When you are finished debugging, it's highly recommended -to set your debug MinimumLevel to Information and restart -Libation. -".Trim(), "Verbose logging enabled", MessageBoxButtons.OK, MessageBoxIcon.Warning); - } - } -} diff --git a/Source/LibationWinForms/Program.cs b/Source/LibationWinForms/Program.cs index 30343ac0..f02df0ba 100644 --- a/Source/LibationWinForms/Program.cs +++ b/Source/LibationWinForms/Program.cs @@ -49,7 +49,7 @@ namespace LibationWinForms // migrations which require Forms or are long-running RunWindowsOnlyMigrations(config); - MessageBoxVerboseLoggingWarning.ShowIfTrue(); + MessageBoxLib.VerboseLoggingWarning_ShowIfTrue(); #if !DEBUG checkForUpdate(); @@ -63,7 +63,7 @@ namespace LibationWinForms var body = "An unrecoverable error occurred. Since this error happened before logging could be initialized, this error can not be written to the log file."; try { - MessageBoxAlertAdmin.Show(body, title, ex); + MessageBoxLib.ShowAdminAlert(body, title, ex); } catch { @@ -178,7 +178,7 @@ namespace LibationWinForms } catch (Exception ex) { - MessageBoxAlertAdmin.Show("Error checking for update", "Error checking for update", ex); + MessageBoxLib.ShowAdminAlert("Error checking for update", "Error checking for update", ex); return; } @@ -203,7 +203,7 @@ namespace LibationWinForms } catch (Exception ex) { - MessageBoxAlertAdmin.Show("Error downloading update", "Error downloading update", ex); + MessageBoxLib.ShowAdminAlert("Error downloading update", "Error downloading update", ex); } } } diff --git a/Source/LibationWinForms/grid/GridEntry.cs b/Source/LibationWinForms/grid/GridEntry.cs index aee51d4c..0d20ef1f 100644 --- a/Source/LibationWinForms/grid/GridEntry.cs +++ b/Source/LibationWinForms/grid/GridEntry.cs @@ -128,14 +128,14 @@ namespace LibationWinForms // Immutable properties { Title = Book.Title; - Series = Book.SeriesNames; + Series = Book.SeriesNames(); Length = Book.LengthInMinutes == 0 ? "" : $"{Book.LengthInMinutes / 60} hr {Book.LengthInMinutes % 60} min"; MyRating = Book.UserDefinedItem.Rating?.ToStarString()?.DefaultIfNullOrWhiteSpace(""); PurchaseDate = libraryBook.DateAdded.ToString("d"); ProductRating = Book.Rating?.ToStarString()?.DefaultIfNullOrWhiteSpace(""); - Authors = Book.AuthorNames; - Narrators = Book.NarratorNames; - Category = string.Join(" > ", Book.CategoriesNames); + Authors = Book.AuthorNames(); + Narrators = Book.NarratorNames(); + Category = string.Join(" > ", Book.CategoriesNames()); Misc = GetMiscDisplay(libraryBook); LongDescription = GetDescriptionDisplay(Book); Description = TrimTextToWord(LongDescription, 62); @@ -232,12 +232,12 @@ namespace LibationWinForms /// private Dictionary> CreateMemberValueDictionary() => new() { - { nameof(Title), () => Book.TitleSortable }, - { nameof(Series), () => Book.SeriesSortable }, + { nameof(Title), () => Book.TitleSortable() }, + { nameof(Series), () => Book.SeriesSortable() }, { nameof(Length), () => Book.LengthInMinutes }, - { nameof(MyRating), () => Book.UserDefinedItem.Rating.FirstScore }, + { nameof(MyRating), () => Book.UserDefinedItem.Rating.FirstScore() }, { nameof(PurchaseDate), () => LibraryBook.DateAdded }, - { nameof(ProductRating), () => Book.Rating.FirstScore }, + { nameof(ProductRating), () => Book.Rating.FirstScore() }, { nameof(Authors), () => Authors }, { nameof(Narrators), () => Narrators }, { nameof(Description), () => Description }, @@ -292,7 +292,7 @@ namespace LibationWinForms details.Add($"Account: {locale} - {acct}"); - if (libraryBook.Book.HasPdf) + if (libraryBook.Book.HasPdf()) details.Add("Has PDF"); if (libraryBook.Book.IsAbridged) details.Add("Abridged"); diff --git a/Source/LibationWinForms/grid/ProductsGrid.cs b/Source/LibationWinForms/grid/ProductsGrid.cs index 5b98859e..842576cc 100644 --- a/Source/LibationWinForms/grid/ProductsGrid.cs +++ b/Source/LibationWinForms/grid/ProductsGrid.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading.Tasks; using System.Windows.Forms; using ApplicationServices; +using DataLayer; using Dinah.Core; using Dinah.Core.DataBinding; using Dinah.Core.Threading; @@ -128,7 +129,7 @@ namespace LibationWinForms var libraryBook = liveGridEntry.LibraryBook; // liberated: open explorer to file - if (libraryBook.Book.Audio_Exists) + if (libraryBook.Book.Audio_Exists()) { var filePath = AudibleFileStorage.Audio.GetPath(libraryBook.Book.AudibleProductId); if (!Go.To.File(filePath)) From 360f077da30adb77710de04ab4a2d547fc6da9e1 Mon Sep 17 00:00:00 2001 From: Robert McRackan Date: Thu, 12 May 2022 10:28:30 -0400 Subject: [PATCH 3/4] * fixed where the filter was being called multiple times on launch * simplified productsGrid init means a lot of defensive code is no longer needed --- Source/LibationWinForms/Form1.cs | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/Source/LibationWinForms/Form1.cs b/Source/LibationWinForms/Form1.cs index 1d4101e8..ad278c35 100644 --- a/Source/LibationWinForms/Form1.cs +++ b/Source/LibationWinForms/Form1.cs @@ -28,9 +28,19 @@ namespace LibationWinForms { InitializeComponent(); + if (this.DesignMode) + return; + productsGrid = new ProductsGrid { Dock = DockStyle.Fill }; productsGrid.VisibleCountChanged += (_, qty) => visibleCountLbl.Text = string.Format("Visible: {0}", qty); gridPanel.Controls.Add(productsGrid); + this.Load += (_, __) => + { + productsGrid.Display(); + + // also applies filter. ONLY call AFTER loading grid + loadInitialQuickFilterState(); + }; // back up string formats beginBookBackupsToolStripMenuItem_format = beginBookBackupsToolStripMenuItem.Text; @@ -39,13 +49,11 @@ namespace LibationWinForms liberateVisibleToolStripMenuItem_format = liberateVisibleToolStripMenuItem.Text; liberateVisible2ToolStripMenuItem_format = liberateVisible2ToolStripMenuItem.Text; - if (this.DesignMode) - return; - // independent UI updates this.Load += (_, _) => this.RestoreSizeAndLocation(Configuration.Instance); this.FormClosing += (_, _) => this.SaveSizeAndLocation(Configuration.Instance); LibraryCommands.LibrarySizeChanged += reloadGridAndUpdateBottomNumbers; + this.Load += setBackupCounts; LibraryCommands.BookUserDefinedItemCommitted += setBackupCounts; QuickFilters.Updated += updateFiltersMenu; LibraryCommands.ScanBegin += LibraryCommands_ScanBegin; @@ -71,21 +79,12 @@ namespace LibationWinForms if (this.DesignMode) return; - // can't refactor into "this.Load => reloadGridAndUpdateBottomNumbers" - // because loadInitialQuickFilterState must follow it - reloadGridAndUpdateBottomNumbers(); - - // also applies filter. ONLY call AFTER loading grid - loadInitialQuickFilterState(); + // I'm leaving this empty call here as a reminder that if we use this, it should probably be after DesignMode check } - private void reloadGridAndUpdateBottomNumbers(object _ = null, object __ = null) + private void reloadGridAndUpdateBottomNumbers(object _, object __) { - // suppressed filter while init'ing UI - var prev_isProcessingGridSelect = isProcessingGridSelect; - isProcessingGridSelect = true; this.UIThreadSync(() => productsGrid.Display()); - isProcessingGridSelect = prev_isProcessingGridSelect; // UI init complete. now we can apply filter this.UIThreadAsync(() => doFilter(lastGoodFilter)); @@ -198,7 +197,6 @@ namespace LibationWinForms } private void filterBtn_Click(object sender, EventArgs e) => doFilter(); - private bool isProcessingGridSelect = false; private string lastGoodFilter = ""; private void doFilter(string filterString) { @@ -207,9 +205,6 @@ namespace LibationWinForms } private void doFilter() { - if (isProcessingGridSelect || productsGrid is null) - return; - try { productsGrid.Filter(filterSearchTb.Text); From bce3bdba7ed2f263284cc4be99537ce54b906fb7 Mon Sep 17 00:00:00 2001 From: Robert McRackan Date: Thu, 12 May 2022 11:57:56 -0400 Subject: [PATCH 4/4] Feature requests #229 , #148 : Bulk actions on filtered books --- Source/AppScaffolding/AppScaffolding.csproj | 2 +- .../LiberatedStatusBatchDialog.Designer.cs | 120 ++++++++++++++++++ .../Dialogs/LiberatedStatusBatchDialog.cs | 45 +++++++ .../Dialogs/LiberatedStatusBatchDialog.resx | 60 +++++++++ .../Dialogs/TagsBatchDialog.Designer.cs | 112 ++++++++++++++++ .../Dialogs/TagsBatchDialog.cs | 35 +++++ .../Dialogs/TagsBatchDialog.resx | 60 +++++++++ Source/LibationWinForms/Form1.cs | 61 ++++++--- 8 files changed, 475 insertions(+), 20 deletions(-) create mode 100644 Source/LibationWinForms/Dialogs/LiberatedStatusBatchDialog.Designer.cs create mode 100644 Source/LibationWinForms/Dialogs/LiberatedStatusBatchDialog.cs create mode 100644 Source/LibationWinForms/Dialogs/LiberatedStatusBatchDialog.resx create mode 100644 Source/LibationWinForms/Dialogs/TagsBatchDialog.Designer.cs create mode 100644 Source/LibationWinForms/Dialogs/TagsBatchDialog.cs create mode 100644 Source/LibationWinForms/Dialogs/TagsBatchDialog.resx diff --git a/Source/AppScaffolding/AppScaffolding.csproj b/Source/AppScaffolding/AppScaffolding.csproj index c4ee9183..ad40a990 100644 --- a/Source/AppScaffolding/AppScaffolding.csproj +++ b/Source/AppScaffolding/AppScaffolding.csproj @@ -3,7 +3,7 @@ net6.0-windows - 7.4.0.1 + 7.5.0.1 diff --git a/Source/LibationWinForms/Dialogs/LiberatedStatusBatchDialog.Designer.cs b/Source/LibationWinForms/Dialogs/LiberatedStatusBatchDialog.Designer.cs new file mode 100644 index 00000000..7c51c134 --- /dev/null +++ b/Source/LibationWinForms/Dialogs/LiberatedStatusBatchDialog.Designer.cs @@ -0,0 +1,120 @@ +namespace LibationWinForms.Dialogs +{ + partial class LiberatedStatusBatchDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.bookLiberatedCb = new System.Windows.Forms.ComboBox(); + this.bookLiberatedLbl = new System.Windows.Forms.Label(); + this.liberatedDescLbl = new System.Windows.Forms.Label(); + this.cancelBtn = new System.Windows.Forms.Button(); + this.saveBtn = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // bookLiberatedCb + // + this.bookLiberatedCb.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.bookLiberatedCb.FormattingEnabled = true; + this.bookLiberatedCb.Location = new System.Drawing.Point(52, 54); + this.bookLiberatedCb.Name = "bookLiberatedCb"; + this.bookLiberatedCb.Size = new System.Drawing.Size(121, 23); + this.bookLiberatedCb.TabIndex = 7; + // + // bookLiberatedLbl + // + this.bookLiberatedLbl.AutoSize = true; + this.bookLiberatedLbl.Location = new System.Drawing.Point(12, 57); + this.bookLiberatedLbl.Name = "bookLiberatedLbl"; + this.bookLiberatedLbl.Size = new System.Drawing.Size(34, 15); + this.bookLiberatedLbl.TabIndex = 6; + this.bookLiberatedLbl.Text = "Book"; + // + // liberatedDescLbl + // + this.liberatedDescLbl.AutoSize = true; + this.liberatedDescLbl.Location = new System.Drawing.Point(12, 9); + this.liberatedDescLbl.Name = "liberatedDescLbl"; + this.liberatedDescLbl.Size = new System.Drawing.Size(312, 30); + this.liberatedDescLbl.TabIndex = 5; + this.liberatedDescLbl.Text = "To download again next time: change to Not Downloaded\r\nTo not download: change to" + + " Downloaded"; + // + // cancelBtn + // + this.cancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelBtn.Location = new System.Drawing.Point(464, 79); + this.cancelBtn.Name = "cancelBtn"; + this.cancelBtn.Size = new System.Drawing.Size(88, 27); + this.cancelBtn.TabIndex = 9; + this.cancelBtn.Text = "Cancel"; + this.cancelBtn.UseVisualStyleBackColor = true; + this.cancelBtn.Click += new System.EventHandler(this.cancelBtn_Click); + // + // saveBtn + // + this.saveBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.saveBtn.Location = new System.Drawing.Point(346, 79); + this.saveBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + this.saveBtn.Name = "saveBtn"; + this.saveBtn.Size = new System.Drawing.Size(88, 27); + this.saveBtn.TabIndex = 8; + this.saveBtn.Text = "Save"; + this.saveBtn.UseVisualStyleBackColor = true; + this.saveBtn.Click += new System.EventHandler(this.saveBtn_Click); + // + // LiberatedStatusBatchDialog + // + this.AcceptButton = this.saveBtn; + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancelBtn; + this.ClientSize = new System.Drawing.Size(564, 118); + this.Controls.Add(this.cancelBtn); + this.Controls.Add(this.saveBtn); + this.Controls.Add(this.bookLiberatedCb); + this.Controls.Add(this.bookLiberatedLbl); + this.Controls.Add(this.liberatedDescLbl); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "LiberatedStatusBatchDialog"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Liberated status: Whether the book has been downloaded"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + private System.Windows.Forms.ComboBox bookLiberatedCb; + private System.Windows.Forms.Label bookLiberatedLbl; + private System.Windows.Forms.Label liberatedDescLbl; + private System.Windows.Forms.Button cancelBtn; + private System.Windows.Forms.Button saveBtn; + } +} \ No newline at end of file diff --git a/Source/LibationWinForms/Dialogs/LiberatedStatusBatchDialog.cs b/Source/LibationWinForms/Dialogs/LiberatedStatusBatchDialog.cs new file mode 100644 index 00000000..ff3682f7 --- /dev/null +++ b/Source/LibationWinForms/Dialogs/LiberatedStatusBatchDialog.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; +using DataLayer; +using Dinah.Core; +using LibationFileManager; + +namespace LibationWinForms.Dialogs +{ + public partial class LiberatedStatusBatchDialog : Form + { + public LiberatedStatus BookLiberatedStatus { get; private set; } + + public class liberatedComboBoxItem + { + public LiberatedStatus Status { get; set; } + public string Text { get; set; } + public override string ToString() => Text; + } + + public LiberatedStatusBatchDialog() + { + InitializeComponent(); + this.SetLibationIcon(); + + this.bookLiberatedCb.Items.Add(new liberatedComboBoxItem { Status = LiberatedStatus.Liberated, Text = "Downloaded" }); + this.bookLiberatedCb.Items.Add(new liberatedComboBoxItem { Status = LiberatedStatus.NotLiberated, Text = "Not Downloaded" }); + + this.bookLiberatedCb.SelectedIndex = 0; + } + + private void saveBtn_Click(object sender, EventArgs e) + { + BookLiberatedStatus = ((liberatedComboBoxItem)this.bookLiberatedCb.SelectedItem).Status; + this.DialogResult = DialogResult.OK; + } + + private void cancelBtn_Click(object sender, EventArgs e) + { + this.DialogResult = DialogResult.Cancel; + this.Close(); + } + } +} diff --git a/Source/LibationWinForms/Dialogs/LiberatedStatusBatchDialog.resx b/Source/LibationWinForms/Dialogs/LiberatedStatusBatchDialog.resx new file mode 100644 index 00000000..f298a7be --- /dev/null +++ b/Source/LibationWinForms/Dialogs/LiberatedStatusBatchDialog.resx @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Source/LibationWinForms/Dialogs/TagsBatchDialog.Designer.cs b/Source/LibationWinForms/Dialogs/TagsBatchDialog.Designer.cs new file mode 100644 index 00000000..e0b73d59 --- /dev/null +++ b/Source/LibationWinForms/Dialogs/TagsBatchDialog.Designer.cs @@ -0,0 +1,112 @@ +namespace LibationWinForms.Dialogs +{ + partial class TagsBatchDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.tagsDescLbl = new System.Windows.Forms.Label(); + this.newTagsTb = new System.Windows.Forms.TextBox(); + this.cancelBtn = new System.Windows.Forms.Button(); + this.saveBtn = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // tagsDescLbl + // + this.tagsDescLbl.AutoSize = true; + this.tagsDescLbl.Location = new System.Drawing.Point(13, 9); + this.tagsDescLbl.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.tagsDescLbl.Name = "tagsDescLbl"; + this.tagsDescLbl.Size = new System.Drawing.Size(458, 15); + this.tagsDescLbl.TabIndex = 2; + this.tagsDescLbl.Text = "Tags are separated by a space. Each tag can contain letters, numbers, and undersc" + + "ores"; + // + // newTagsTb + // + this.newTagsTb.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.newTagsTb.Location = new System.Drawing.Point(13, 30); + this.newTagsTb.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + this.newTagsTb.Name = "newTagsTb"; + this.newTagsTb.ScrollBars = System.Windows.Forms.ScrollBars.Both; + this.newTagsTb.Size = new System.Drawing.Size(591, 23); + this.newTagsTb.TabIndex = 3; + // + // cancelBtn + // + this.cancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelBtn.Location = new System.Drawing.Point(517, 71); + this.cancelBtn.Name = "cancelBtn"; + this.cancelBtn.Size = new System.Drawing.Size(88, 27); + this.cancelBtn.TabIndex = 6; + this.cancelBtn.Text = "Cancel"; + this.cancelBtn.UseVisualStyleBackColor = true; + this.cancelBtn.Click += new System.EventHandler(this.cancelBtn_Click); + // + // saveBtn + // + this.saveBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.saveBtn.Location = new System.Drawing.Point(399, 71); + this.saveBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + this.saveBtn.Name = "saveBtn"; + this.saveBtn.Size = new System.Drawing.Size(88, 27); + this.saveBtn.TabIndex = 5; + this.saveBtn.Text = "Save"; + this.saveBtn.UseVisualStyleBackColor = true; + this.saveBtn.Click += new System.EventHandler(this.saveBtn_Click); + // + // TagsBatchDialog + // + this.AcceptButton = this.saveBtn; + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancelBtn; + this.ClientSize = new System.Drawing.Size(617, 110); + this.Controls.Add(this.cancelBtn); + this.Controls.Add(this.saveBtn); + this.Controls.Add(this.tagsDescLbl); + this.Controls.Add(this.newTagsTb); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "TagsBatchDialog"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Replace Tags"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label tagsDescLbl; + private System.Windows.Forms.TextBox newTagsTb; + private System.Windows.Forms.Button cancelBtn; + private System.Windows.Forms.Button saveBtn; + } +} \ No newline at end of file diff --git a/Source/LibationWinForms/Dialogs/TagsBatchDialog.cs b/Source/LibationWinForms/Dialogs/TagsBatchDialog.cs new file mode 100644 index 00000000..bd74c532 --- /dev/null +++ b/Source/LibationWinForms/Dialogs/TagsBatchDialog.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace LibationWinForms.Dialogs +{ + public partial class TagsBatchDialog : Form + { + public string NewTags { get; private set; } + + public TagsBatchDialog() + { + InitializeComponent(); + this.SetLibationIcon(); + } + + private void saveBtn_Click(object sender, EventArgs e) + { + NewTags = this.newTagsTb.Text; + this.DialogResult = DialogResult.OK; + } + + private void cancelBtn_Click(object sender, EventArgs e) + { + this.DialogResult = DialogResult.Cancel; + this.Close(); + } + } +} diff --git a/Source/LibationWinForms/Dialogs/TagsBatchDialog.resx b/Source/LibationWinForms/Dialogs/TagsBatchDialog.resx new file mode 100644 index 00000000..f298a7be --- /dev/null +++ b/Source/LibationWinForms/Dialogs/TagsBatchDialog.resx @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Source/LibationWinForms/Form1.cs b/Source/LibationWinForms/Form1.cs index ad278c35..a4abb1ed 100644 --- a/Source/LibationWinForms/Form1.cs +++ b/Source/LibationWinForms/Form1.cs @@ -52,7 +52,12 @@ namespace LibationWinForms // independent UI updates this.Load += (_, _) => this.RestoreSizeAndLocation(Configuration.Instance); this.FormClosing += (_, _) => this.SaveSizeAndLocation(Configuration.Instance); - LibraryCommands.LibrarySizeChanged += reloadGridAndUpdateBottomNumbers; + LibraryCommands.LibrarySizeChanged += (_, __) => + { + this.UIThreadSync(() => productsGrid.Display()); + this.UIThreadAsync(() => doFilter(lastGoodFilter)); + }; + LibraryCommands.LibrarySizeChanged += setBackupCounts; this.Load += setBackupCounts; LibraryCommands.BookUserDefinedItemCommitted += setBackupCounts; QuickFilters.Updated += updateFiltersMenu; @@ -82,21 +87,11 @@ namespace LibationWinForms // I'm leaving this empty call here as a reminder that if we use this, it should probably be after DesignMode check } - private void reloadGridAndUpdateBottomNumbers(object _, object __) - { - this.UIThreadSync(() => productsGrid.Display()); - - // UI init complete. now we can apply filter - this.UIThreadAsync(() => doFilter(lastGoodFilter)); - - setBackupCounts(); - } - #region bottom: backup counts private System.ComponentModel.BackgroundWorker updateCountsBw; private bool runBackupCountsAgain; - private void setBackupCounts(object _ = null, object __ = null) + private void setBackupCounts(object _, object __) { runBackupCountsAgain = true; @@ -561,33 +556,61 @@ namespace LibationWinForms private void replaceTagsToolStripMenuItem_Click(object sender, EventArgs e) { + var dialog = new TagsBatchDialog(); + var result = dialog.ShowDialog(); + if (result != DialogResult.OK) + return; + var visibleLibraryBooks = productsGrid.GetVisible(); + + var confirmationResult = MessageBoxLib.ShowConfirmationDialog( + visibleLibraryBooks, + $"Are you sure you want to replace tags in {0}?", + "Replace tags?"); + + if (confirmationResult != DialogResult.Yes) + return; + foreach (var libraryBook in visibleLibraryBooks) - libraryBook.Book.UserDefinedItem.Tags = "ggggg"; + libraryBook.Book.UserDefinedItem.Tags = dialog.NewTags; LibraryCommands.UpdateUserDefinedItem(visibleLibraryBooks.Select(lb => lb.Book)); } private void setDownloadedToolStripMenuItem_Click(object sender, EventArgs e) { + var dialog = new LiberatedStatusBatchDialog(); + var result = dialog.ShowDialog(); + if (result != DialogResult.OK) + return; + var visibleLibraryBooks = productsGrid.GetVisible(); + + var confirmationResult = MessageBoxLib.ShowConfirmationDialog( + visibleLibraryBooks, + $"Are you sure you want to replace downloaded status in {0}?", + "Replace downloaded status?"); + + if (confirmationResult != DialogResult.Yes) + return; + foreach (var libraryBook in visibleLibraryBooks) - libraryBook.Book.UserDefinedItem.BookStatus = DataLayer.LiberatedStatus.NotLiberated; + libraryBook.Book.UserDefinedItem.BookStatus = dialog.BookLiberatedStatus; LibraryCommands.UpdateUserDefinedItem(visibleLibraryBooks.Select(lb => lb.Book)); } private async void removeToolStripMenuItem_Click(object sender, EventArgs e) { - var libraryBooks = productsGrid.GetVisible(); + var visibleLibraryBooks = productsGrid.GetVisible(); - var result = MessageBoxLib.ShowConfirmationDialog( - libraryBooks, + var confirmationResult = MessageBoxLib.ShowConfirmationDialog( + visibleLibraryBooks, $"Are you sure you want to remove {0} from Libation's library?", "Remove books from Libation?"); - if (result != DialogResult.Yes) + if (confirmationResult != DialogResult.Yes) return; - var visibleIds = libraryBooks.Select(lb => lb.Book.AudibleProductId).ToList(); + var visibleIds = visibleLibraryBooks.Select(lb => lb.Book.AudibleProductId).ToList(); await LibraryCommands.RemoveBooksAsync(visibleIds); } #endregion