Files
Libation/Source/LibationWinForms/Form1.VisibleBooks.cs
T
Cursor Agentandrmcrackan bf017ac28c fix: restore the LibationWinForms build
setLiberatedVisibleMenuItem takes the books to count as a parameter, but the
body that reads it was extracted into setLiberatedVisibleMenuItemCore, which
did not, so the extracted method referenced a name that is not in its scope.

Two changes landed on the same method within hours and git merged them with no
textual conflict: 75f1d753 pulled the body out behind a try/catch, and #1958
brought in the 'visible ??=' fallback that the thread-safety fix had added to
the body back in June. Neither is wrong on its own.

Thread the parameter through. The whole solution builds again.

Co-authored-by: rmcrackan <rmcrackan@gmail.com>
2026-08-24 20:51:09 +00:00

213 lines
6.8 KiB
C#

using ApplicationServices;
using DataLayer;
using Dinah.Core.Threading;
using LibationWinForms.Dialogs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace LibationWinForms;
public partial class Form1
{
protected void Configure_VisibleBooks()
{
// init formattable
visibleCountLbl.Format(0);
liberateVisibleToolStripMenuItem_VisibleBooksMenu.Format(0);
liberateVisibleToolStripMenuItem_LiberateMenu.Format(0);
// top menu strip
visibleBooksToolStripMenuItem.Format(0);
LibraryCommands.BookUserDefinedItemCommitted += setLiberatedVisibleMenuItemAsync;
}
private async void setLiberatedVisibleMenuItemAsync(object? _, object __)
{
var visible = (List<DataLayer.LibraryBook>)this.Invoke(() => productsDisplay.GetVisible());
await Task.Run(() => setLiberatedVisibleMenuItem(visible));
}
private static DateTime lastVisibleCountUpdated;
void setLiberatedVisibleMenuItem(List<DataLayer.LibraryBook>? visible = null)
{
try
{
setLiberatedVisibleMenuItemCore(visible);
}
// Both callers are async void event handlers, where a failure is not a faulted task anyone awaits but
// an unhandled exception that closes Libation. Counting these books reads the file system, which can
// fail at any moment for reasons that have nothing to do with the app - a Books folder on a drive that
// was just unplugged is enough. A stale number above a menu item is not worth the session.
catch (Exception ex)
{
Serilog.Log.Logger.Error(ex, "Error counting the visible books which are not yet liberated");
}
}
/// <param name="visible">
/// The books to count, which every caller reads off the grid on the UI thread before handing the counting
/// to a thread pool thread. This runs on that thread, so reaching for the grid here instead would race.
/// </param>
private void setLiberatedVisibleMenuItemCore(List<DataLayer.LibraryBook>? visible)
{
//Assume that all calls to update arrive in order,
//Only display results of the latest book count.
var updaterTime = lastVisibleCountUpdated = DateTime.UtcNow;
visible ??= (List<DataLayer.LibraryBook>)this.Invoke(() => productsDisplay.GetVisible());
var libraryStats = LibraryCommands.GetCounts(visible);
if (updaterTime < lastVisibleCountUpdated)
return;
this.UIThreadSync(() =>
{
if (libraryStats.HasPendingBooks)
{
liberateVisibleToolStripMenuItem_VisibleBooksMenu.Format(libraryStats.PendingBooks);
liberateVisibleToolStripMenuItem_LiberateMenu.Format(libraryStats.PendingBooks);
}
else
{
liberateVisibleToolStripMenuItem_VisibleBooksMenu.Text = "All visible books are liberated";
liberateVisibleToolStripMenuItem_LiberateMenu.Text = "All visible books are liberated";
}
liberateVisibleToolStripMenuItem_VisibleBooksMenu.Enabled = libraryStats.HasPendingBooks;
liberateVisibleToolStripMenuItem_LiberateMenu.Enabled = libraryStats.HasPendingBooks;
});
}
private async void liberateVisible(object sender, EventArgs e)
{
try
{
if (await processBookQueue1.ViewModel.QueueDownloadDecryptAsync(productsDisplay.GetVisible().UnLiberated().ToArray()))
SetQueueCollapseState(false);
}
catch (Exception ex)
{
Serilog.Log.Logger.Error(ex, "An error occurred while backing up visible library books");
}
}
private async void replaceTagsToolStripMenuItem_Click(object sender, EventArgs e)
{
var dialog = new TagsBatchDialog();
var result = dialog.ShowDialog();
if (result != DialogResult.OK)
return;
var visibleLibraryBooks = productsDisplay.GetVisible();
var confirmationResult = MessageBoxLib.ShowConfirmationDialog(
visibleLibraryBooks,
// do not use `$` string interpolation. See impl.
"Are you sure you want to replace tags in {0}?",
"Replace tags?");
if (confirmationResult != DialogResult.Yes)
return;
await visibleLibraryBooks.UpdateTagsAsync(dialog.NewTags);
}
private async void setBookDownloadedManualToolStripMenuItem_Click(object sender, EventArgs e)
{
var dialog = new LiberatedStatusBatchManualDialog();
var result = dialog.ShowDialog();
if (result != DialogResult.OK)
return;
var visibleLibraryBooks = productsDisplay.GetVisible();
var confirmationResult = MessageBoxLib.ShowConfirmationDialog(
visibleLibraryBooks,
// do not use `$` string interpolation. See impl.
"Are you sure you want to replace book downloaded status in {0}?",
"Replace downloaded status?");
if (confirmationResult != DialogResult.Yes)
return;
await visibleLibraryBooks.UpdateBookStatusAsync(dialog.BookLiberatedStatus);
}
private async void setPdfDownloadedManualToolStripMenuItem_Click(object sender, EventArgs e)
{
var dialog = new LiberatedStatusBatchManualDialog(isPdf: true);
var result = dialog.ShowDialog();
if (result != DialogResult.OK)
return;
var visibleLibraryBooks = productsDisplay.GetVisible();
var confirmationResult = MessageBoxLib.ShowConfirmationDialog(
visibleLibraryBooks,
// do not use `$` string interpolation. See impl.
"Are you sure you want to replace PDF downloaded status in {0}?",
"Replace downloaded status?");
if (confirmationResult != DialogResult.Yes)
return;
await visibleLibraryBooks.UpdatePdfStatusAsync(dialog.BookLiberatedStatus);
}
private async void setDownloadedAutoToolStripMenuItem_Click(object sender, EventArgs e)
{
var dialog = new LiberatedStatusBatchAutoDialog();
var result = dialog.ShowDialog();
if (result != DialogResult.OK)
return;
var bulkSetStatus = new BulkSetDownloadStatus(productsDisplay.GetVisible(), dialog.SetDownloaded, dialog.SetNotDownloaded);
var count = await Task.Run(() => bulkSetStatus.Discover());
if (count == 0)
return;
var confirmationResult = MessageBox.Show(
bulkSetStatus.AggregateMessage,
"Replace downloaded status?",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question,
MessageBoxDefaultButton.Button1);
if (confirmationResult != DialogResult.Yes)
return;
await bulkSetStatus.ExecuteAsync();
}
private async void removeToolStripMenuItem_Click(object sender, EventArgs e)
{
var visibleLibraryBooks = productsDisplay.GetVisible();
var confirmationResult = MessageBoxLib.ShowConfirmationDialog(
visibleLibraryBooks,
// do not use `$` string interpolation. See impl.
"Are you sure you want to remove {0} from Libation's library?",
"Remove books from Libation?");
if (confirmationResult is DialogResult.Yes)
await visibleLibraryBooks.RemoveBooksAsync();
}
private async void productsDisplay_VisibleCountChanged(object sender, int qty)
{
visibleCount = qty;
// bottom-left visible count
visibleCountLbl.Format(qty);
// top menu strip
visibleBooksToolStripMenuItem.Format(qty);
visibleBooksToolStripMenuItem.Enabled = qty > 0;
var visible = productsDisplay.GetVisible();
await Task.Run(() => setLiberatedVisibleMenuItem(visible));
}
}