diff --git a/Source/ApplicationServices/LibraryCommands.cs b/Source/ApplicationServices/LibraryCommands.cs index a051fa84..4691c67b 100644 --- a/Source/ApplicationServices/LibraryCommands.cs +++ b/Source/ApplicationServices/LibraryCommands.cs @@ -242,18 +242,16 @@ namespace ApplicationServices #endregion #region remove/restore books - public static Task RemoveBooksAsync(List idsToRemove) => Task.Run(() => removeBooks(idsToRemove)); - public static int RemoveBook(string idToRemove) => removeBooks(new() { idToRemove }); - private static int removeBooks(List idsToRemove) + public static Task RemoveBooksAsync(this IEnumerable idsToRemove) => Task.Run(() => removeBooks(idsToRemove)); + public static int RemoveBook(this LibraryBook idToRemove) => removeBooks(new[] { idToRemove }); + private static int removeBooks(IEnumerable removeLibraryBooks) { try { - if (idsToRemove is null || !idsToRemove.Any()) + if (removeLibraryBooks is null || !removeLibraryBooks.Any()) return 0; using var context = DbContexts.GetContext(); - var libBooks = context.GetLibrary_Flat_NoTracking(); - var removeLibraryBooks = libBooks.Where(lb => idsToRemove.Contains(lb.Book.AudibleProductId)).ToList(); // Attach() NoTracking entities before SaveChanges() foreach (var lb in removeLibraryBooks) @@ -275,7 +273,7 @@ namespace ApplicationServices } } - public static int RestoreBooks(this List libraryBooks) + public static int RestoreBooks(this IEnumerable libraryBooks) { try { @@ -303,6 +301,31 @@ namespace ApplicationServices throw; } } + + public static int PermanentlyDeleteBooks(this IEnumerable libraryBooks) + { + try + { + if (libraryBooks is null || !libraryBooks.Any()) + return 0; + + using var context = DbContexts.GetContext(); + + context.LibraryBooks.RemoveRange(libraryBooks); + context.Books.RemoveRange(libraryBooks.Select(lb => lb.Book)); + + var qtyChanges = context.SaveChanges(); + if (qtyChanges > 0) + finalizeLibrarySizeChange(); + + return qtyChanges; + } + catch (Exception ex) + { + Log.Logger.Error(ex, "Error restoring books"); + throw; + } + } #endregion // call this whenever books are added or removed from library diff --git a/Source/LibationAvalonia/Controls/CheckedListBox.axaml b/Source/LibationAvalonia/Controls/CheckedListBox.axaml new file mode 100644 index 00000000..cf70be9a --- /dev/null +++ b/Source/LibationAvalonia/Controls/CheckedListBox.axaml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + diff --git a/Source/LibationAvalonia/Controls/CheckedListBox.axaml.cs b/Source/LibationAvalonia/Controls/CheckedListBox.axaml.cs new file mode 100644 index 00000000..f0ab1e61 --- /dev/null +++ b/Source/LibationAvalonia/Controls/CheckedListBox.axaml.cs @@ -0,0 +1,46 @@ +using Avalonia; +using Avalonia.Collections; +using Avalonia.Controls; +using LibationAvalonia.ViewModels; +using ReactiveUI; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace LibationAvalonia.Controls +{ + public partial class CheckedListBox : UserControl + { + public static readonly StyledProperty> ItemsProperty = + AvaloniaProperty.Register>(nameof(Items)); + + public AvaloniaList Items { get => GetValue(ItemsProperty); set => SetValue(ItemsProperty, value); } + private CheckedListBoxViewModel _viewModel = new(); + + public CheckedListBox() + { + InitializeComponent(); + scroller.DataContext = _viewModel; + } + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + if (change.Property.Name == nameof(Items) && Items != null) + _viewModel.CheckboxItems = Items; + base.OnPropertyChanged(change); + } + + private class CheckedListBoxViewModel : ViewModelBase + { + private AvaloniaList _checkboxItems; + public AvaloniaList CheckboxItems { get => _checkboxItems; set => this.RaiseAndSetIfChanged(ref _checkboxItems, value); } + } + } + + public class CheckBoxViewModel : ViewModelBase + { + private bool _isChecked; + public bool IsChecked { get => _isChecked; set => this.RaiseAndSetIfChanged(ref _isChecked, value); } + private object _bookText; + public object Item { get => _bookText; set => this.RaiseAndSetIfChanged(ref _bookText, value); } + } +} diff --git a/Source/LibationAvalonia/Dialogs/TrashBinDialog.axaml b/Source/LibationAvalonia/Dialogs/TrashBinDialog.axaml new file mode 100644 index 00000000..1738a67e --- /dev/null +++ b/Source/LibationAvalonia/Dialogs/TrashBinDialog.axaml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + diff --git a/Source/LibationAvalonia/Dialogs/TrashBinDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/TrashBinDialog.axaml.cs new file mode 100644 index 00000000..073a0845 --- /dev/null +++ b/Source/LibationAvalonia/Dialogs/TrashBinDialog.axaml.cs @@ -0,0 +1,145 @@ +using ApplicationServices; +using Avalonia.Collections; +using Avalonia.Controls; +using Avalonia.Threading; +using DataLayer; +using LibationAvalonia.Controls; +using LibationAvalonia.ViewModels; +using LibationFileManager; +using ReactiveUI; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Threading.Tasks; + +namespace LibationAvalonia.Dialogs +{ + public partial class TrashBinDialog : Window + { + TrashBinViewModel _viewModel; + + public TrashBinDialog() + { + InitializeComponent(); + this.RestoreSizeAndLocation(Configuration.Instance); + DataContext = _viewModel = new(); + + this.Closing += (_,_) => this.SaveSizeAndLocation(Configuration.Instance); + } + + public async void EmptyTrash_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) + => await _viewModel.PermanentlyDeleteCheckedAsync(); + public async void Restore_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) + => await _viewModel.RestoreCheckedAsync(); + } + + public class TrashBinViewModel : ViewModelBase, IDisposable + { + public AvaloniaList DeletedBooks { get; } + public string CheckedCountText => $"Checked : {_checkedBooksCount} of {_totalBooksCount}"; + + private bool _controlsEnabled = true; + public bool ControlsEnabled { get => _controlsEnabled; set=> this.RaiseAndSetIfChanged(ref _controlsEnabled, value); } + + private bool? everythingChecked = false; + public bool? EverythingChecked + { + get => everythingChecked; + set + { + everythingChecked = value ?? false; + + if (everythingChecked is true) + CheckAll(); + else if (everythingChecked is false) + UncheckAll(); + } + } + + private int _totalBooksCount = 0; + private int _checkedBooksCount = -1; + public int CheckedBooksCount + { + get => _checkedBooksCount; + set + { + if (_checkedBooksCount != value) + { + _checkedBooksCount = value; + this.RaisePropertyChanged(nameof(CheckedCountText)); + } + + everythingChecked + = _checkedBooksCount == 0 || _totalBooksCount == 0 ? false + : _checkedBooksCount == _totalBooksCount ? true + : null; + + this.RaisePropertyChanged(nameof(EverythingChecked)); + } + } + + public IEnumerable CheckedBooks => DeletedBooks.Where(i => i.IsChecked).Select(i => i.Item).Cast(); + + public TrashBinViewModel() + { + DeletedBooks = new() + { + ResetBehavior = ResetBehavior.Remove + }; + + tracker = DeletedBooks.TrackItemPropertyChanged(CheckboxPropertyChanged); + Reload(); + } + + public void CheckAll() + { + foreach (var item in DeletedBooks) + item.IsChecked = true; + } + + public void UncheckAll() + { + foreach (var item in DeletedBooks) + item.IsChecked = false; + } + + public async Task RestoreCheckedAsync() + { + ControlsEnabled = false; + var qtyChanges = await Task.Run(CheckedBooks.RestoreBooks); + if (qtyChanges > 0) + Reload(); + ControlsEnabled = true; + } + + public async Task PermanentlyDeleteCheckedAsync() + { + ControlsEnabled = false; + var qtyChanges = await Task.Run(CheckedBooks.PermanentlyDeleteBooks); + if (qtyChanges > 0) + Reload(); + ControlsEnabled = true; + } + + private void Reload() + { + var deletedBooks = DbContexts.GetContext().GetDeletedLibraryBooks(); + + DeletedBooks.Clear(); + DeletedBooks.AddRange(deletedBooks.Select(lb => new CheckBoxViewModel { Item = lb })); + + _totalBooksCount = DeletedBooks.Count; + CheckedBooksCount = 0; + } + + private IDisposable tracker; + private void CheckboxPropertyChanged(Tuple e) + { + if (e.Item2.PropertyName == nameof(CheckBoxViewModel.IsChecked)) + CheckedBooksCount = DeletedBooks.Count(b => b.IsChecked); + } + + public void Dispose() => tracker?.Dispose(); + } +} diff --git a/Source/LibationAvalonia/ViewModels/ProductsDisplayViewModel.cs b/Source/LibationAvalonia/ViewModels/ProductsDisplayViewModel.cs index 554e877e..e7785de4 100644 --- a/Source/LibationAvalonia/ViewModels/ProductsDisplayViewModel.cs +++ b/Source/LibationAvalonia/ViewModels/ProductsDisplayViewModel.cs @@ -337,10 +337,10 @@ namespace LibationAvalonia.ViewModels if (selectedBooks.Count == 0) return; - var libraryBooks = selectedBooks.Select(rge => rge.LibraryBook).ToList(); + var booksToRemove = selectedBooks.Select(rge => rge.LibraryBook).ToList(); var result = await MessageBox.ShowConfirmationDialog( null, - libraryBooks, + booksToRemove, // do not use `$` string interpolation. See impl. "Are you sure you want to remove {0} from Libation's library?", "Remove books from Libation?"); @@ -351,8 +351,6 @@ namespace LibationAvalonia.ViewModels foreach (var book in selectedBooks) book.PropertyChanged -= GridEntry_PropertyChanged; - var idsToRemove = libraryBooks.Select(lb => lb.Book.AudibleProductId).ToList(); - void BindingList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { if (e.Action != System.Collections.Specialized.NotifyCollectionChangedAction.Reset) @@ -371,7 +369,7 @@ namespace LibationAvalonia.ViewModels //The RemoveBooksAsync will fire LibrarySizeChanged, which calls ProductsDisplay2.Display(), //so there's no need to remove books from the grid display here. - var removeLibraryBooks = await LibraryCommands.RemoveBooksAsync(idsToRemove); + await booksToRemove.RemoveBooksAsync(); RemovableCountChanged?.Invoke(this, 0); } diff --git a/Source/LibationAvalonia/Views/MainWindow.RemoveBooks.cs b/Source/LibationAvalonia/Views/MainWindow.RemoveBooks.cs index 85e3b135..e95b2bd4 100644 --- a/Source/LibationAvalonia/Views/MainWindow.RemoveBooks.cs +++ b/Source/LibationAvalonia/Views/MainWindow.RemoveBooks.cs @@ -1,4 +1,5 @@ using AudibleUtilities; +using LibationAvalonia.Dialogs; using System; using System.Linq; @@ -15,6 +16,12 @@ namespace LibationAvalonia.Views _viewModel.RemoveButtonsVisible = false; } + public async void openTrashBinToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) + { + var trash = new TrashBinDialog(); + await trash.ShowDialog(this); + } + public void removeLibraryBooksToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) { // if 0 accounts, this will not be visible diff --git a/Source/LibationAvalonia/Views/MainWindow.VisibleBooks.cs b/Source/LibationAvalonia/Views/MainWindow.VisibleBooks.cs index d3a2402f..c237f9f2 100644 --- a/Source/LibationAvalonia/Views/MainWindow.VisibleBooks.cs +++ b/Source/LibationAvalonia/Views/MainWindow.VisibleBooks.cs @@ -144,11 +144,8 @@ namespace LibationAvalonia.Views "Remove books from Libation?", MessageBoxDefaultButton.Button2); - if (confirmationResult != DialogResult.Yes) - return; - - var visibleIds = visibleLibraryBooks.Select(lb => lb.Book.AudibleProductId).ToList(); - await LibraryCommands.RemoveBooksAsync(visibleIds); + if (confirmationResult is DialogResult.Yes) + await visibleLibraryBooks.RemoveBooksAsync(); } public async void ProductsDisplay_VisibleCountChanged(object sender, int qty) { diff --git a/Source/LibationAvalonia/Views/MainWindow.axaml b/Source/LibationAvalonia/Views/MainWindow.axaml index 8b2fffb8..e86e40ce 100644 --- a/Source/LibationAvalonia/Views/MainWindow.axaml +++ b/Source/LibationAvalonia/Views/MainWindow.axaml @@ -131,6 +131,7 @@ + diff --git a/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs b/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs index e168e6ef..70f85e78 100644 --- a/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs +++ b/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs @@ -102,7 +102,7 @@ namespace LibationAvalonia.Views setNotDownloadMenuItem.Click += (_, __) => entry.Book.UpdateBookStatus(LiberatedStatus.NotLiberated); var removeMenuItem = new MenuItem() { Header = "_Remove from library" }; - removeMenuItem.Click += async (_, __) => await Task.Run(() => LibraryCommands.RemoveBook(entry.AudibleProductId)); + removeMenuItem.Click += async (_, __) => await Task.Run(entry.LibraryBook.RemoveBook); var locateFileMenuItem = new MenuItem() { Header = "_Locate file..." }; locateFileMenuItem.Click += async (_, __) => diff --git a/Source/LibationWinForms/Dialogs/TrashBinDialog.Designer.cs b/Source/LibationWinForms/Dialogs/TrashBinDialog.Designer.cs new file mode 100644 index 00000000..947ab2f9 --- /dev/null +++ b/Source/LibationWinForms/Dialogs/TrashBinDialog.Designer.cs @@ -0,0 +1,129 @@ +namespace LibationWinForms.Dialogs +{ + partial class TrashBinDialog + { + /// + /// 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() + { + deletedCbl = new System.Windows.Forms.CheckedListBox(); + label1 = new System.Windows.Forms.Label(); + restoreBtn = new System.Windows.Forms.Button(); + permanentlyDeleteBtn = new System.Windows.Forms.Button(); + everythingCb = new System.Windows.Forms.CheckBox(); + deletedCheckedLbl = new System.Windows.Forms.Label(); + SuspendLayout(); + // + // deletedCbl + // + deletedCbl.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right; + deletedCbl.FormattingEnabled = true; + deletedCbl.Location = new System.Drawing.Point(12, 27); + deletedCbl.Name = "deletedCbl"; + deletedCbl.Size = new System.Drawing.Size(776, 364); + deletedCbl.TabIndex = 3; + deletedCbl.ItemCheck += deletedCbl_ItemCheck; + // + // label1 + // + label1.AutoSize = true; + label1.Location = new System.Drawing.Point(12, 9); + label1.Name = "label1"; + label1.Size = new System.Drawing.Size(388, 15); + label1.TabIndex = 4; + label1.Text = "Check books you want to permanently delete from or restore to Libation"; + // + // restoreBtn + // + restoreBtn.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; + restoreBtn.Location = new System.Drawing.Point(572, 398); + restoreBtn.Name = "restoreBtn"; + restoreBtn.Size = new System.Drawing.Size(75, 40); + restoreBtn.TabIndex = 5; + restoreBtn.Text = "Restore"; + restoreBtn.UseVisualStyleBackColor = true; + restoreBtn.Click += restoreBtn_Click; + // + // permanentlyDeleteBtn + // + permanentlyDeleteBtn.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; + permanentlyDeleteBtn.Location = new System.Drawing.Point(653, 398); + permanentlyDeleteBtn.Name = "permanentlyDeleteBtn"; + permanentlyDeleteBtn.Size = new System.Drawing.Size(135, 40); + permanentlyDeleteBtn.TabIndex = 5; + permanentlyDeleteBtn.Text = "Permanently Remove\r\nfrom Libation"; + permanentlyDeleteBtn.UseVisualStyleBackColor = true; + permanentlyDeleteBtn.Click += permanentlyDeleteBtn_Click; + // + // everythingCb + // + everythingCb.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left; + everythingCb.AutoSize = true; + everythingCb.Location = new System.Drawing.Point(12, 410); + everythingCb.Name = "everythingCb"; + everythingCb.Size = new System.Drawing.Size(82, 19); + everythingCb.TabIndex = 6; + everythingCb.Text = "Everything"; + everythingCb.ThreeState = true; + everythingCb.UseVisualStyleBackColor = true; + everythingCb.CheckStateChanged += everythingCb_CheckStateChanged; + // + // deletedCheckedLbl + // + deletedCheckedLbl.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left; + deletedCheckedLbl.AutoSize = true; + deletedCheckedLbl.Location = new System.Drawing.Point(126, 411); + deletedCheckedLbl.Name = "deletedCheckedLbl"; + deletedCheckedLbl.Size = new System.Drawing.Size(104, 15); + deletedCheckedLbl.TabIndex = 7; + deletedCheckedLbl.Text = "Checked: {0} of {1}"; + // + // TrashBinDialog + // + AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + ClientSize = new System.Drawing.Size(800, 450); + Controls.Add(deletedCheckedLbl); + Controls.Add(everythingCb); + Controls.Add(permanentlyDeleteBtn); + Controls.Add(restoreBtn); + Controls.Add(label1); + Controls.Add(deletedCbl); + Name = "TrashBinDialog"; + Text = "Trash Bin"; + ResumeLayout(false); + PerformLayout(); + } + + #endregion + + private System.Windows.Forms.CheckedListBox deletedCbl; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Button restoreBtn; + private System.Windows.Forms.Button permanentlyDeleteBtn; + private System.Windows.Forms.CheckBox everythingCb; + private System.Windows.Forms.Label deletedCheckedLbl; + } +} \ No newline at end of file diff --git a/Source/LibationWinForms/Dialogs/TrashBinDialog.cs b/Source/LibationWinForms/Dialogs/TrashBinDialog.cs new file mode 100644 index 00000000..2a5fdac8 --- /dev/null +++ b/Source/LibationWinForms/Dialogs/TrashBinDialog.cs @@ -0,0 +1,116 @@ +using ApplicationServices; +using System; +using System.Data; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; +using DataLayer; +using LibationFileManager; +using System.Collections; + +namespace LibationWinForms.Dialogs +{ + public partial class TrashBinDialog : Form + { + private readonly string deletedCheckedTemplate; + public TrashBinDialog() + { + InitializeComponent(); + + this.SetLibationIcon(); + this.RestoreSizeAndLocation(Configuration.Instance); + this.Closing += (_, _) => this.SaveSizeAndLocation(Configuration.Instance); + + deletedCheckedTemplate = deletedCheckedLbl.Text; + + var deletedBooks = DbContexts.GetContext().GetDeletedLibraryBooks(); + foreach (var lb in deletedBooks) + deletedCbl.Items.Add(lb); + + setLabel(); + } + + private void deletedCbl_ItemCheck(object sender, ItemCheckEventArgs e) + { + // CheckedItems.Count is not updated until after the event fires + setLabel(e.NewValue); + } + + private async void permanentlyDeleteBtn_Click(object sender, EventArgs e) + { + setControlsEnabled(false); + + var removed = deletedCbl.CheckedItems.Cast().ToList(); + + removeFromCheckList(removed); + await Task.Run(removed.PermanentlyDeleteBooks); + + setControlsEnabled(true); + } + + private async void restoreBtn_Click(object sender, EventArgs e) + { + setControlsEnabled(false); + + var removed = deletedCbl.CheckedItems.Cast().ToList(); + + removeFromCheckList(removed); + await Task.Run(removed.RestoreBooks); + + setControlsEnabled(true); + } + + private void removeFromCheckList(IEnumerable objects) + { + foreach (var o in objects) + deletedCbl.Items.Remove(o); + + deletedCbl.Refresh(); + setLabel(); + } + + private void setControlsEnabled(bool enabled) + => restoreBtn.Enabled = permanentlyDeleteBtn.Enabled = deletedCbl.Enabled = everythingCb.Enabled = enabled; + + private void everythingCb_CheckStateChanged(object sender, EventArgs e) + { + if (everythingCb.CheckState is CheckState.Indeterminate) + { + everythingCb.CheckState = CheckState.Unchecked; + return; + } + + deletedCbl.ItemCheck -= deletedCbl_ItemCheck; + + for (var i = 0; i < deletedCbl.Items.Count; i++) + deletedCbl.SetItemChecked(i, everythingCb.CheckState is CheckState.Checked); + + setLabel(); + + deletedCbl.ItemCheck += deletedCbl_ItemCheck; + } + + + private void setLabel(CheckState? checkedState = null) + { + var pre = deletedCbl.CheckedItems.Count; + int count = checkedState switch + { + CheckState.Checked => pre + 1, + CheckState.Unchecked => pre - 1, + _ => pre, + }; + + everythingCb.CheckStateChanged -= everythingCb_CheckStateChanged; + + everythingCb.CheckState + = count > 0 && count == deletedCbl.Items.Count ? CheckState.Checked + : count == 0 ? CheckState.Unchecked + : CheckState.Indeterminate; + + everythingCb.CheckStateChanged += everythingCb_CheckStateChanged; + + deletedCheckedLbl.Text = string.Format(deletedCheckedTemplate, count, deletedCbl.Items.Count); + } + } +} diff --git a/Source/LibationWinForms/Dialogs/TrashBinDialog.resx b/Source/LibationWinForms/Dialogs/TrashBinDialog.resx new file mode 100644 index 00000000..f298a7be --- /dev/null +++ b/Source/LibationWinForms/Dialogs/TrashBinDialog.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.Designer.cs b/Source/LibationWinForms/Form1.Designer.cs index dc37bc58..e62fd0a4 100644 --- a/Source/LibationWinForms/Form1.Designer.cs +++ b/Source/LibationWinForms/Form1.Designer.cs @@ -63,6 +63,7 @@ this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator(); this.removeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator4 = new System.Windows.Forms.ToolStripSeparator(); + this.openTrashBinToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.launchHangoverToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.locateAudiobooksToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.settingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -383,8 +384,9 @@ this.accountsToolStripMenuItem, this.basicSettingsToolStripMenuItem, this.toolStripSeparator4, + this.openTrashBinToolStripMenuItem, this.launchHangoverToolStripMenuItem, - this.toolStripSeparator2, + this.toolStripSeparator2, this.aboutToolStripMenuItem}); this.settingsToolStripMenuItem.Name = "settingsToolStripMenuItem"; this.settingsToolStripMenuItem.Size = new System.Drawing.Size(61, 20); @@ -592,6 +594,13 @@ this.locateAudiobooksToolStripMenuItem.Text = "L&ocate Audiobooks"; this.locateAudiobooksToolStripMenuItem.Click += new System.EventHandler(this.locateAudiobooksToolStripMenuItem_Click); // + // openTrashBinToolStripMenuItem + // + this.openTrashBinToolStripMenuItem.Name = "openTrashBinToolStripMenuItem"; + this.openTrashBinToolStripMenuItem.Size = new System.Drawing.Size(247, 22); + this.openTrashBinToolStripMenuItem.Text = "Trash Bin"; + this.openTrashBinToolStripMenuItem.Click += new System.EventHandler(this.openTrashBinToolStripMenuItem_Click); + // // launchHangoverToolStripMenuItem // this.launchHangoverToolStripMenuItem.Name = "launchHangoverToolStripMenuItem"; @@ -676,6 +685,7 @@ private System.Windows.Forms.ToolStripSeparator toolStripSeparator3; private System.Windows.Forms.ToolStripMenuItem locateAudiobooksToolStripMenuItem; private System.Windows.Forms.ToolStripSeparator toolStripSeparator4; + private System.Windows.Forms.ToolStripMenuItem openTrashBinToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem launchHangoverToolStripMenuItem; private LibationWinForms.FormattableToolStripMenuItem liberateVisibleToolStripMenuItem_LiberateMenu; private System.Windows.Forms.SplitContainer splitContainer1; diff --git a/Source/LibationWinForms/Form1.RemoveBooks.cs b/Source/LibationWinForms/Form1.RemoveBooks.cs index 4a5753e8..51b710fb 100644 --- a/Source/LibationWinForms/Form1.RemoveBooks.cs +++ b/Source/LibationWinForms/Form1.RemoveBooks.cs @@ -16,6 +16,9 @@ namespace LibationWinForms private async void removeBooksBtn_Click(object sender, EventArgs e) => await productsDisplay.RemoveCheckedBooksAsync(); + private void openTrashBinToolStripMenuItem_Click(object sender, EventArgs e) + => new TrashBinDialog().ShowDialog(this); + private void doneRemovingBtn_Click(object sender, EventArgs e) { removeBooksBtn.Visible = false; diff --git a/Source/LibationWinForms/Form1.VisibleBooks.cs b/Source/LibationWinForms/Form1.VisibleBooks.cs index 26619024..2ecd336b 100644 --- a/Source/LibationWinForms/Form1.VisibleBooks.cs +++ b/Source/LibationWinForms/Form1.VisibleBooks.cs @@ -168,11 +168,8 @@ namespace LibationWinForms "Are you sure you want to remove {0} from Libation's library?", "Remove books from Libation?"); - if (confirmationResult != DialogResult.Yes) - return; - - var visibleIds = visibleLibraryBooks.Select(lb => lb.Book.AudibleProductId).ToList(); - await LibraryCommands.RemoveBooksAsync(visibleIds); + if (confirmationResult is DialogResult.Yes) + await visibleLibraryBooks.RemoveBooksAsync(); } private async void productsDisplay_VisibleCountChanged(object sender, int qty) diff --git a/Source/LibationWinForms/GridView/ProductsDisplay.cs b/Source/LibationWinForms/GridView/ProductsDisplay.cs index 75027385..411c516c 100644 --- a/Source/LibationWinForms/GridView/ProductsDisplay.cs +++ b/Source/LibationWinForms/GridView/ProductsDisplay.cs @@ -107,9 +107,9 @@ namespace LibationWinForms.GridView if (selectedBooks.Count == 0) return; - var libraryBooks = selectedBooks.Select(rge => rge.LibraryBook).ToList(); + var booksToRemove = selectedBooks.Select(rge => rge.LibraryBook).ToList(); var result = MessageBoxLib.ShowConfirmationDialog( - libraryBooks, + booksToRemove, // do not use `$` string interpolation. See impl. "Are you sure you want to remove {0} from Libation's library?", "Remove books from Libation?"); @@ -118,8 +118,7 @@ namespace LibationWinForms.GridView return; productsGrid.RemoveBooks(selectedBooks); - var idsToRemove = libraryBooks.Select(lb => lb.Book.AudibleProductId).ToList(); - var removeLibraryBooks = await LibraryCommands.RemoveBooksAsync(idsToRemove); + await booksToRemove.RemoveBooksAsync(); } public async Task ScanAndRemoveBooksAsync(params Account[] accounts) diff --git a/Source/LibationWinForms/GridView/ProductsGrid.cs b/Source/LibationWinForms/GridView/ProductsGrid.cs index 75355bf9..ec5e0cac 100644 --- a/Source/LibationWinForms/GridView/ProductsGrid.cs +++ b/Source/LibationWinForms/GridView/ProductsGrid.cs @@ -154,7 +154,7 @@ namespace LibationWinForms.GridView setNotDownloadMenuItem.Click += (_, __) => entry.Book.UpdateBookStatus(LiberatedStatus.NotLiberated); var removeMenuItem = new ToolStripMenuItem() { Text = "&Remove from library" }; - removeMenuItem.Click += async (_, __) => await Task.Run(() => LibraryCommands.RemoveBook(entry.AudibleProductId)); + removeMenuItem.Click += async (_, __) => await Task.Run(entry.LibraryBook.RemoveBook); var locateFileMenuItem = new ToolStripMenuItem() { Text = "&Locate file..." }; locateFileMenuItem.Click += (_, __) =>