Add audiobook Trash Bin

This commit is contained in:
Mbucari 2023-03-02 13:12:32 -07:00
parent fe55b90ee3
commit 52a863c62a
18 changed files with 656 additions and 29 deletions

View File

@ -242,18 +242,16 @@ namespace ApplicationServices
#endregion #endregion
#region remove/restore books #region remove/restore books
public static Task<int> RemoveBooksAsync(List<string> idsToRemove) => Task.Run(() => removeBooks(idsToRemove)); public static Task<int> RemoveBooksAsync(this IEnumerable<LibraryBook> idsToRemove) => Task.Run(() => removeBooks(idsToRemove));
public static int RemoveBook(string idToRemove) => removeBooks(new() { idToRemove }); public static int RemoveBook(this LibraryBook idToRemove) => removeBooks(new[] { idToRemove });
private static int removeBooks(List<string> idsToRemove) private static int removeBooks(IEnumerable<LibraryBook> removeLibraryBooks)
{ {
try try
{ {
if (idsToRemove is null || !idsToRemove.Any()) if (removeLibraryBooks is null || !removeLibraryBooks.Any())
return 0; return 0;
using var context = DbContexts.GetContext(); 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() // Attach() NoTracking entities before SaveChanges()
foreach (var lb in removeLibraryBooks) foreach (var lb in removeLibraryBooks)
@ -275,7 +273,7 @@ namespace ApplicationServices
} }
} }
public static int RestoreBooks(this List<LibraryBook> libraryBooks) public static int RestoreBooks(this IEnumerable<LibraryBook> libraryBooks)
{ {
try try
{ {
@ -303,6 +301,31 @@ namespace ApplicationServices
throw; throw;
} }
} }
public static int PermanentlyDeleteBooks(this IEnumerable<LibraryBook> 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 #endregion
// call this whenever books are added or removed from library // call this whenever books are added or removed from library

View File

@ -0,0 +1,30 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="LibationAvalonia.Controls.CheckedListBox">
<UserControl.Resources>
<RecyclePool x:Key="RecyclePool" />
<DataTemplate x:Key="queuedBook">
<CheckBox HorizontalAlignment="Stretch" Margin="10,0,0,0" Content="{Binding Item}" IsChecked="{Binding IsChecked, Mode=TwoWay}" />
</DataTemplate>
<RecyclingElementFactory x:Key="elementFactory" RecyclePool="{StaticResource RecyclePool}">
<RecyclingElementFactory.Templates>
<StaticResource x:Key="queuedBook" ResourceKey="queuedBook" />
</RecyclingElementFactory.Templates>
</RecyclingElementFactory>
</UserControl.Resources>
<ScrollViewer
Name="scroller"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<ItemsRepeater IsVisible="True"
VerticalCacheLength="1.2"
HorizontalCacheLength="1"
Items="{Binding CheckboxItems}"
ItemTemplate="{StaticResource elementFactory}" />
</ScrollViewer>
</UserControl>

View File

@ -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<AvaloniaList<CheckBoxViewModel>> ItemsProperty =
AvaloniaProperty.Register<CheckedListBox, AvaloniaList<CheckBoxViewModel>>(nameof(Items));
public AvaloniaList<CheckBoxViewModel> 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<CheckBoxViewModel> _checkboxItems;
public AvaloniaList<CheckBoxViewModel> 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); }
}
}

View File

@ -0,0 +1,66 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="630" d:DesignHeight="480"
x:Class="LibationAvalonia.Dialogs.TrashBinDialog"
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
MinWidth="630" MinHeight="480"
Title="Trash Bin"
WindowStartupLocation="CenterOwner"
Icon="/Assets/libation.ico">
<Grid
RowDefinitions="Auto,*,Auto">
<TextBlock
Grid.Row="0"
Margin="5"
Text="Check books you want to permanently delete from or restore to Libation" />
<controls:CheckedListBox
Grid.Row="1"
Margin="5,0,5,0"
BorderThickness="1"
BorderBrush="Gray"
IsEnabled="{Binding ControlsEnabled}"
Items="{Binding DeletedBooks}" />
<Grid
Grid.Row="2"
Margin="5"
ColumnDefinitions="Auto,Auto,*,Auto">
<CheckBox
IsEnabled="{Binding ControlsEnabled}"
IsThreeState="True"
Margin="0,0,20,0"
IsChecked="{Binding EverythingChecked}"
Content="Everything" />
<TextBlock
Grid.Column="1"
VerticalAlignment="Center"
Text="{Binding CheckedCountText}" />
<Button
IsEnabled="{Binding ControlsEnabled}"
Grid.Column="2"
Margin="0,0,20,0"
HorizontalAlignment="Right"
VerticalAlignment="Stretch"
VerticalContentAlignment="Center"
Content="Restore"
Click="Restore_Click" />
<Button
IsEnabled="{Binding ControlsEnabled}"
Grid.Column="3"
Click="EmptyTrash_Click" >
<TextBlock
TextAlignment="Center"
Text="Permanently Delete&#xa;from Libation" />
</Button>
</Grid>
</Grid>
</Window>

View File

@ -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<CheckBoxViewModel> 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<LibraryBook> CheckedBooks => DeletedBooks.Where(i => i.IsChecked).Select(i => i.Item).Cast<LibraryBook>();
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<object, PropertyChangedEventArgs> e)
{
if (e.Item2.PropertyName == nameof(CheckBoxViewModel.IsChecked))
CheckedBooksCount = DeletedBooks.Count(b => b.IsChecked);
}
public void Dispose() => tracker?.Dispose();
}
}

View File

@ -337,10 +337,10 @@ namespace LibationAvalonia.ViewModels
if (selectedBooks.Count == 0) if (selectedBooks.Count == 0)
return; return;
var libraryBooks = selectedBooks.Select(rge => rge.LibraryBook).ToList(); var booksToRemove = selectedBooks.Select(rge => rge.LibraryBook).ToList();
var result = await MessageBox.ShowConfirmationDialog( var result = await MessageBox.ShowConfirmationDialog(
null, null,
libraryBooks, booksToRemove,
// do not use `$` string interpolation. See impl. // do not use `$` string interpolation. See impl.
"Are you sure you want to remove {0} from Libation's library?", "Are you sure you want to remove {0} from Libation's library?",
"Remove books from Libation?"); "Remove books from Libation?");
@ -351,8 +351,6 @@ namespace LibationAvalonia.ViewModels
foreach (var book in selectedBooks) foreach (var book in selectedBooks)
book.PropertyChanged -= GridEntry_PropertyChanged; book.PropertyChanged -= GridEntry_PropertyChanged;
var idsToRemove = libraryBooks.Select(lb => lb.Book.AudibleProductId).ToList();
void BindingList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) void BindingList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{ {
if (e.Action != System.Collections.Specialized.NotifyCollectionChangedAction.Reset) if (e.Action != System.Collections.Specialized.NotifyCollectionChangedAction.Reset)
@ -371,7 +369,7 @@ namespace LibationAvalonia.ViewModels
//The RemoveBooksAsync will fire LibrarySizeChanged, which calls ProductsDisplay2.Display(), //The RemoveBooksAsync will fire LibrarySizeChanged, which calls ProductsDisplay2.Display(),
//so there's no need to remove books from the grid display here. //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); RemovableCountChanged?.Invoke(this, 0);
} }

View File

@ -1,4 +1,5 @@
using AudibleUtilities; using AudibleUtilities;
using LibationAvalonia.Dialogs;
using System; using System;
using System.Linq; using System.Linq;
@ -15,6 +16,12 @@ namespace LibationAvalonia.Views
_viewModel.RemoveButtonsVisible = false; _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) public void removeLibraryBooksToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
{ {
// if 0 accounts, this will not be visible // if 0 accounts, this will not be visible

View File

@ -144,11 +144,8 @@ namespace LibationAvalonia.Views
"Remove books from Libation?", "Remove books from Libation?",
MessageBoxDefaultButton.Button2); MessageBoxDefaultButton.Button2);
if (confirmationResult != DialogResult.Yes) if (confirmationResult is DialogResult.Yes)
return; await visibleLibraryBooks.RemoveBooksAsync();
var visibleIds = visibleLibraryBooks.Select(lb => lb.Book.AudibleProductId).ToList();
await LibraryCommands.RemoveBooksAsync(visibleIds);
} }
public async void ProductsDisplay_VisibleCountChanged(object sender, int qty) public async void ProductsDisplay_VisibleCountChanged(object sender, int qty)
{ {

View File

@ -131,6 +131,7 @@
<MenuItem Click="accountsToolStripMenuItem_Click" Header="_Accounts..." /> <MenuItem Click="accountsToolStripMenuItem_Click" Header="_Accounts..." />
<MenuItem Click="basicSettingsToolStripMenuItem_Click" Header="_Settings..." /> <MenuItem Click="basicSettingsToolStripMenuItem_Click" Header="_Settings..." />
<Separator /> <Separator />
<MenuItem Click="openTrashBinToolStripMenuItem_Click" Header="Trash Bin" />
<MenuItem Click="launchHangoverToolStripMenuItem_Click" Header="Launch _Hangover" /> <MenuItem Click="launchHangoverToolStripMenuItem_Click" Header="Launch _Hangover" />
<Separator /> <Separator />
<MenuItem Click="aboutToolStripMenuItem_Click" Header="A_bout..." /> <MenuItem Click="aboutToolStripMenuItem_Click" Header="A_bout..." />

View File

@ -102,7 +102,7 @@ namespace LibationAvalonia.Views
setNotDownloadMenuItem.Click += (_, __) => entry.Book.UpdateBookStatus(LiberatedStatus.NotLiberated); setNotDownloadMenuItem.Click += (_, __) => entry.Book.UpdateBookStatus(LiberatedStatus.NotLiberated);
var removeMenuItem = new MenuItem() { Header = "_Remove from library" }; 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..." }; var locateFileMenuItem = new MenuItem() { Header = "_Locate file..." };
locateFileMenuItem.Click += async (_, __) => locateFileMenuItem.Click += async (_, __) =>

View File

@ -0,0 +1,129 @@
namespace LibationWinForms.Dialogs
{
partial class TrashBinDialog
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
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;
}
}

View File

@ -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<LibraryBook>().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<LibraryBook>().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);
}
}
}

View File

@ -0,0 +1,60 @@
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -63,6 +63,7 @@
this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator(); this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator();
this.removeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.removeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator4 = new System.Windows.Forms.ToolStripSeparator(); this.toolStripSeparator4 = new System.Windows.Forms.ToolStripSeparator();
this.openTrashBinToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.launchHangoverToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.launchHangoverToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.locateAudiobooksToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.locateAudiobooksToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.settingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.settingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
@ -383,8 +384,9 @@
this.accountsToolStripMenuItem, this.accountsToolStripMenuItem,
this.basicSettingsToolStripMenuItem, this.basicSettingsToolStripMenuItem,
this.toolStripSeparator4, this.toolStripSeparator4,
this.openTrashBinToolStripMenuItem,
this.launchHangoverToolStripMenuItem, this.launchHangoverToolStripMenuItem,
this.toolStripSeparator2, this.toolStripSeparator2,
this.aboutToolStripMenuItem}); this.aboutToolStripMenuItem});
this.settingsToolStripMenuItem.Name = "settingsToolStripMenuItem"; this.settingsToolStripMenuItem.Name = "settingsToolStripMenuItem";
this.settingsToolStripMenuItem.Size = new System.Drawing.Size(61, 20); this.settingsToolStripMenuItem.Size = new System.Drawing.Size(61, 20);
@ -592,6 +594,13 @@
this.locateAudiobooksToolStripMenuItem.Text = "L&ocate Audiobooks"; this.locateAudiobooksToolStripMenuItem.Text = "L&ocate Audiobooks";
this.locateAudiobooksToolStripMenuItem.Click += new System.EventHandler(this.locateAudiobooksToolStripMenuItem_Click); 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 // launchHangoverToolStripMenuItem
// //
this.launchHangoverToolStripMenuItem.Name = "launchHangoverToolStripMenuItem"; this.launchHangoverToolStripMenuItem.Name = "launchHangoverToolStripMenuItem";
@ -676,6 +685,7 @@
private System.Windows.Forms.ToolStripSeparator toolStripSeparator3; private System.Windows.Forms.ToolStripSeparator toolStripSeparator3;
private System.Windows.Forms.ToolStripMenuItem locateAudiobooksToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem locateAudiobooksToolStripMenuItem;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator4; private System.Windows.Forms.ToolStripSeparator toolStripSeparator4;
private System.Windows.Forms.ToolStripMenuItem openTrashBinToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem launchHangoverToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem launchHangoverToolStripMenuItem;
private LibationWinForms.FormattableToolStripMenuItem liberateVisibleToolStripMenuItem_LiberateMenu; private LibationWinForms.FormattableToolStripMenuItem liberateVisibleToolStripMenuItem_LiberateMenu;
private System.Windows.Forms.SplitContainer splitContainer1; private System.Windows.Forms.SplitContainer splitContainer1;

View File

@ -16,6 +16,9 @@ namespace LibationWinForms
private async void removeBooksBtn_Click(object sender, EventArgs e) private async void removeBooksBtn_Click(object sender, EventArgs e)
=> await productsDisplay.RemoveCheckedBooksAsync(); => await productsDisplay.RemoveCheckedBooksAsync();
private void openTrashBinToolStripMenuItem_Click(object sender, EventArgs e)
=> new TrashBinDialog().ShowDialog(this);
private void doneRemovingBtn_Click(object sender, EventArgs e) private void doneRemovingBtn_Click(object sender, EventArgs e)
{ {
removeBooksBtn.Visible = false; removeBooksBtn.Visible = false;

View File

@ -168,11 +168,8 @@ namespace LibationWinForms
"Are you sure you want to remove {0} from Libation's library?", "Are you sure you want to remove {0} from Libation's library?",
"Remove books from Libation?"); "Remove books from Libation?");
if (confirmationResult != DialogResult.Yes) if (confirmationResult is DialogResult.Yes)
return; await visibleLibraryBooks.RemoveBooksAsync();
var visibleIds = visibleLibraryBooks.Select(lb => lb.Book.AudibleProductId).ToList();
await LibraryCommands.RemoveBooksAsync(visibleIds);
} }
private async void productsDisplay_VisibleCountChanged(object sender, int qty) private async void productsDisplay_VisibleCountChanged(object sender, int qty)

View File

@ -107,9 +107,9 @@ namespace LibationWinForms.GridView
if (selectedBooks.Count == 0) if (selectedBooks.Count == 0)
return; return;
var libraryBooks = selectedBooks.Select(rge => rge.LibraryBook).ToList(); var booksToRemove = selectedBooks.Select(rge => rge.LibraryBook).ToList();
var result = MessageBoxLib.ShowConfirmationDialog( var result = MessageBoxLib.ShowConfirmationDialog(
libraryBooks, booksToRemove,
// do not use `$` string interpolation. See impl. // do not use `$` string interpolation. See impl.
"Are you sure you want to remove {0} from Libation's library?", "Are you sure you want to remove {0} from Libation's library?",
"Remove books from Libation?"); "Remove books from Libation?");
@ -118,8 +118,7 @@ namespace LibationWinForms.GridView
return; return;
productsGrid.RemoveBooks(selectedBooks); productsGrid.RemoveBooks(selectedBooks);
var idsToRemove = libraryBooks.Select(lb => lb.Book.AudibleProductId).ToList(); await booksToRemove.RemoveBooksAsync();
var removeLibraryBooks = await LibraryCommands.RemoveBooksAsync(idsToRemove);
} }
public async Task ScanAndRemoveBooksAsync(params Account[] accounts) public async Task ScanAndRemoveBooksAsync(params Account[] accounts)

View File

@ -154,7 +154,7 @@ namespace LibationWinForms.GridView
setNotDownloadMenuItem.Click += (_, __) => entry.Book.UpdateBookStatus(LiberatedStatus.NotLiberated); setNotDownloadMenuItem.Click += (_, __) => entry.Book.UpdateBookStatus(LiberatedStatus.NotLiberated);
var removeMenuItem = new ToolStripMenuItem() { Text = "&Remove from library" }; 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..." }; var locateFileMenuItem = new ToolStripMenuItem() { Text = "&Locate file..." };
locateFileMenuItem.Click += (_, __) => locateFileMenuItem.Click += (_, __) =>