Files
Libation/Source/LibationAvalonia/Views/MainWindow.axaml
T
Cursor Agentandrmcrackan cb01b9b3b1 Keep pointing at the trash when the library itself is empty
Two states were wrong, both when the live library is empty but the trash is not.
GetLibrary() filters IsDeleted, so an all-trashed library reads as empty and the
getting-started panel took over.

Without a filter it said 'Libation is empty' while 29 books sat in the trash -
the books are not gone, they are one click away. The panel now carries a trash
line when there is anything in there.

With a filter it was worse: searching for a book that IS in the trash showed
'Libation is empty. Add your Audible account' and suppressed the trash hint
entirely, which is the exact failure #1925 was about, reintroduced in the one
case where the library is empty.

The rule is now that someone searching is not someone getting started, so a
filter always answers for itself. That leaves one merely-suboptimal case, an
empty library filtered to something in neither place, which says 'no books
match' - true, and better than claiming the library is empty when it is being
filtered.

Also fixes what made this invisible while testing: GettingStartedVisible depends
on the filter but nothing raised it when the filter changed, so the panel never
re-evaluated.

Co-authored-by: rmcrackan <rmcrackan@gmail.com>
2026-08-18 20:17:32 +00:00

360 lines
17 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?xml version="1.0" encoding="UTF-8"?>
<Window xmlns="https://github.com/avaloniaui"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:LibationAvalonia.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:views="clr-namespace:LibationAvalonia.Views"
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
mc:Ignorable="d" d:DesignWidth="1850" d:DesignHeight="700"
x:Class="LibationAvalonia.Views.MainWindow"
Title="Libation: Liberate your Library"
x:DataType="vm:MainVM"
Name="Form1"
Icon="/Assets/libation.ico">
<NativeMenu.Menu>
<NativeMenu>
<NativeMenuItem Header="Import">
<NativeMenu>
<NativeMenuItem
Header="Auto Scan Library"
Command="{Binding ToggleAutoScan}"
IsChecked="{Binding AutoScanChecked}"
ToggleType="CheckBox" />
<NativeMenuItemSeparator />
</NativeMenu>
</NativeMenuItem>
<NativeMenuItem Header="Liberate">
<NativeMenu>
<NativeMenuItem Command="{Binding BackupAllBooks}" Header="{Binding BookBackupsToolStripText}" Gesture="{OnPlatform macOS='alt+⌘+B'}" />
<NativeMenuItem Command="{Binding BackupAllPdfs}" Header="{Binding PdfBackupsToolStripText}" Gesture="{OnPlatform macOS='alt+⌘+P'}"/>
<NativeMenuItem Command="{Binding ConvertAllToMp3Async}" Header="Convert all M4b to Mp3 [Long-running]..." />
<NativeMenuItem Command="{Binding LiberateVisible}" Header="{Binding LiberateVisibleToolStripText}" IsEnabled="{Binding AnyVisibleNotLiberated}" />
</NativeMenu>
</NativeMenuItem>
<NativeMenuItem Header="Export">
<NativeMenu>
<NativeMenuItem Command="{Binding ExportLibraryAsync}" Header="Export Library" Gesture="{OnPlatform macOS='alt+⌘+X'}"/>
</NativeMenu>
</NativeMenuItem>
<NativeMenuItem Header="Quick Filters">
<NativeMenu>
<NativeMenuItem
Header="Start Libation with 1st filter Default"
Command="{Binding ToggleFirstFilterIsDefault}"
IsChecked="{Binding FirstFilterIsDefault}"
ToggleType="CheckBox" />
<NativeMenuItem Command="{Binding EditQuickFiltersAsync}" Header="Edit quick filters..." Gesture="{OnPlatform macOS='alt+⌘+Q'}" />
<NativeMenuItemSeparator />
</NativeMenu>
</NativeMenuItem>
<NativeMenuItem Header="Visible Books">
<NativeMenu>
<NativeMenuItem Command="{Binding LiberateVisible}" Header="{Binding LiberateVisibleToolStripText_2}" IsEnabled="{Binding AnyVisibleNotLiberated}" Gesture="{OnPlatform macOS='alt+⌘+V'}" />
<NativeMenuItem Command="{Binding ReplaceTagsAsync}" Header="Replace Tags..." />
<NativeMenuItem Command="{Binding SetBookDownloadedAsync}" Header="Set book 'Downloaded' status manually..." />
<NativeMenuItem Command="{Binding SetPdfDownloadedAsync}" Header="Set PDF 'Downloaded' status manually..." />
<NativeMenuItem Command="{Binding SetDownloadedAutoAsync}" Header="Set 'Downloaded' status automatically..." />
<NativeMenuItem Command="{Binding RemoveVisibleAsync}" Header="Remove from library..." />
</NativeMenu>
</NativeMenuItem>
<NativeMenuItem Header="Settings">
<NativeMenu>
<NativeMenuItem Command="{Binding ShowAccountsAsync}" Header="Accounts..." Gesture="{OnPlatform macOS='⌘+.'}" />
<NativeMenuItem Command="{Binding ShowSettingsAsync}" Header="Settings..." Gesture="{OnPlatform macOS='⌘+,'}" />
<NativeMenuItemSeparator />
<NativeMenuItem Command="{Binding ShowTrashBinAsync}" Header="{Binding TrashBinMenuText}" Gesture="{OnPlatform macOS='alt+⌘+T'}" />
<NativeMenuItem Command="{Binding LaunchHangover}" Header="Launch Hangover" />
<NativeMenuItemSeparator />
<NativeMenuItem Command="{Binding StartWalkthroughAsync}" Header="Take a Guided Tour of Libation" />
<NativeMenuItem Command="{Binding ShowAboutAsync}" Header="About..." />
</NativeMenu>
</NativeMenuItem>
</NativeMenu>
</NativeMenu.Menu>
<Grid RowDefinitions="Auto,Auto,*,Auto">
<Border Grid.Row="0" BorderBrush="{DynamicResource SystemBaseLowColor}" BorderThickness="0,1">
<Grid ColumnDefinitions="*,Auto">
<!-- Menu Strip -->
<Menu VerticalAlignment="Top" IsVisible="{Binding MenuBarVisible}">
<!-- Decrease height of menu strop -->
<Menu.Styles>
<Style Selector="Menu /template/ ItemsPresenter#PART_ItemsPresenter">
<Setter Property="Height" Value="25"/>
</Style>
</Menu.Styles>
<!-- Import Menu -->
<MenuItem Name="importToolStripMenuItem" Header="_Import">
<MenuItem IsVisible="{Binding AnyAccounts}" Command="{Binding ToggleAutoScan}" Header="A_uto Scan Library">
<MenuItem.Icon>
<CheckBox BorderThickness="0" IsChecked="{Binding AutoScanChecked, Mode=TwoWay}" IsHitTestVisible="False" />
</MenuItem.Icon>
</MenuItem>
<MenuItem IsVisible="{Binding !AnyAccounts}" Command="{Binding AddAccountsAsync}" Header="No accounts yet. A_dd Account..." />
<!-- Scan Library -->
<MenuItem IsVisible="{Binding OneAccount}" IsEnabled="{Binding !ActivelyScanning}" Name="scanLibraryToolStripMenuItem" Command="{Binding ScanAccountAsync}" Header="Scan _Library" />
<MenuItem IsVisible="{Binding MultipleAccounts}" IsEnabled="{Binding !ActivelyScanning}" Name="scanLibraryOfAllAccountsToolStripMenuItem" Command="{Binding ScanAllAccountsAsync}" Header="Scan Library of _All Accounts" />
<MenuItem IsVisible="{Binding MultipleAccounts}" IsEnabled="{Binding !ActivelyScanning}" Command="{Binding ScanSomeAccountsAsync}" Header="Scan Library of _Some Accounts" />
<Separator IsVisible="{Binding AnyAccounts}" />
<!-- Remove Books -->
<MenuItem IsVisible="{Binding OneAccount}" IsEnabled="{Binding RemoveMenuItemsEnabled}" Command="{Binding RemoveBooksAsync}" Header="_Remove Library Books" />
<MenuItem IsVisible="{Binding MultipleAccounts}" IsEnabled="{Binding RemoveMenuItemsEnabled}" Command="{Binding RemoveBooksAllAsync}" Header="_Remove Books from All Accounts" />
<MenuItem IsVisible="{Binding MultipleAccounts}" IsEnabled="{Binding RemoveMenuItemsEnabled}" Command="{Binding RemoveBooksSomeAsync}" Header="_Remove Books from Some Accounts" />
<Separator />
<MenuItem Command="{Binding LocateAudiobooksAsync}" Header="L_ocate Audiobooks..." ToolTip.Tip="{Binding LocateAudiobooksTip}" />
</MenuItem>
<!-- Liberate Menu -->
<MenuItem Header="_Liberate">
<MenuItem Command="{Binding BackupAllBooks}" Header="{Binding BookBackupsToolStripText}" />
<MenuItem Command="{Binding BackupAllPdfs}" Header="{Binding PdfBackupsToolStripText}" />
<MenuItem Command="{Binding ConvertAllToMp3Async}" Header="Convert all _M4b to Mp3 [Long-running]..." />
<MenuItem Command="{Binding LiberateVisible}" Header="{Binding LiberateVisibleToolStripText}" IsEnabled="{Binding AnyVisibleNotLiberated}" />
</MenuItem>
<!-- Export Menu -->
<MenuItem Header="E_xport">
<!-- Remove height style property for menu item -->
<MenuItem IsEnabled="{Binding LibraryStats?.HasBookResults, TargetNullValue=false}" Command="{Binding ExportLibraryAsync}" Header="E_xport Library" InputGesture="ctrl+S" />
</MenuItem>
<!-- Quick Filters Menu -->
<MenuItem Name="quickFiltersToolStripMenuItem" Header="Quick _Filters" ItemsSource="{Binding QuickFilterMenuItems}" />
<!-- Visible Books Menu -->
<MenuItem Header="{Binding VisibleCountMenuItemText}" >
<MenuItem Command="{Binding LiberateVisible}" Header="{Binding LiberateVisibleToolStripText_2}" IsEnabled="{Binding AnyVisibleNotLiberated}" />
<MenuItem Command="{Binding ReplaceTagsAsync}" Header="Replace _Tags..." />
<MenuItem Command="{Binding SetBookDownloadedAsync}" Header="Set book '_Downloaded' status manually..." />
<MenuItem Command="{Binding SetPdfDownloadedAsync}" Header="Set _PDF 'Downloaded' status manually..." />
<MenuItem Command="{Binding SetDownloadedAutoAsync}" Header="Set '_Downloaded' status automatically..." />
<MenuItem Command="{Binding RemoveVisibleAsync}" Header="_Remove from library..." />
</MenuItem>
<!-- Settings Menu -->
<MenuItem Header="_Settings" Name="settingsToolStripMenuItem">
<MenuItem Name="accountsToolStripMenuItem" Command="{Binding ShowAccountsAsync}" Header="_Accounts..." InputGesture="ctrl+shift+A"/>
<MenuItem Name="basicSettingsToolStripMenuItem" Command="{Binding ShowSettingsAsync}" Header="_Settings..." InputGesture="ctrl+P" />
<Separator />
<MenuItem Command="{Binding ShowTrashBinAsync}" Header="{Binding TrashBinMenuText}" />
<MenuItem Command="{Binding LaunchHangover}" Header="Launch _Hangover" />
<Separator />
<MenuItem Command="{Binding ShowFindBetterQualityBooksAsync}" Header="Scan for Better Quality Audiobooks" ToolTip.Tip="{Binding FindBetterQualityBooksTip}"/>
<Separator />
<MenuItem Command="{Binding StartWalkthroughAsync}" Header="Take a Guided _Tour of Libation" />
<MenuItem Command="{Binding ShowAboutAsync}" Header="A_bout..." />
</MenuItem>
</Menu>
<StackPanel IsVisible="{Binding ActivelyScanning}" Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">
<Path VerticalAlignment="Center" Fill="{DynamicResource IconFill}" Data="{StaticResource ImportIcon}" />
<TextBlock Margin="5,0,5,0" VerticalAlignment="Center" Text="{Binding ScanningText}"/>
</StackPanel>
</Grid>
</Border>
<!-- Buttons and Search Box -->
<Grid Grid.Row="1" Margin="8" Height="30" ColumnDefinitions="Auto,*,Auto">
<Grid.Styles>
<Style Selector="TextBox">
<Setter Property="MinHeight" Value="10" />
</Style>
<Style Selector="Button">
<Setter Property="Padding" Value="15,6" />
<Setter Property="Margin" Value="10,0,0,0" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
</Style>
</Grid.Styles>
<StackPanel Grid.Column="0" IsVisible="{Binding !RemoveButtonsVisible}" Orientation="Horizontal">
<Button Name="filterHelpBtn" Margin="0" Command="{Binding FilterHelpBtn}" Content="?"/>
<Button Name="addQuickFilterBtn" Command="{Binding AddQuickFilterBtn}" Content="Add To Quick Filters"/>
</StackPanel>
<StackPanel Grid.Column="1" Orientation="Horizontal">
<Button IsVisible="{Binding RemoveButtonsVisible}" IsEnabled="{Binding RemoveBooksButtonEnabled}" Command="{Binding RemoveBooksBtn}" Content="{Binding RemoveBooksButtonText}"/>
<Button IsVisible="{Binding RemoveButtonsVisible}" Command="{Binding DoneRemovingBtn}" Content="Done Removing Books"/>
</StackPanel>
<Grid
Grid.Column="1"
Margin="10,0,0,0"
ColumnDefinitions="*,Auto"
IsVisible="{Binding !RemoveButtonsVisible}">
<TextBox
Grid.Column="0"
Name="filterSearchTb"
VerticalAlignment="Stretch"
InnerRightContent="{x:Null}"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
Text="{Binding SelectedNamedFilter.Filter, Mode=OneWay}"
KeyDown="filterSearchTb_KeyPress" />
<Button
Grid.Column="1"
Margin="6,0,0,0"
Padding="10,4"
VerticalAlignment="Stretch"
VerticalContentAlignment="Center"
Click="ClearFilterButton_Click"
Content="×"
ToolTip.Tip="Clear filter" />
</Grid>
<StackPanel Grid.Column="2" Height="30" Orientation="Horizontal">
<Button Name="filterBtn" IsVisible="{Binding !RemoveButtonsVisible}" Command="{Binding FilterBtn}" CommandParameter="{Binding #filterSearchTb.Text}" VerticalAlignment="Stretch" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Content="Filter"/>
<Button Padding="2,6" VerticalAlignment="Stretch" Command="{Binding ToggleQueueHideBtn}">
<Path Stretch="Uniform" Fill="{DynamicResource IconFill}" Data="{StaticResource LeftArrows}">
<Path.RenderTransform>
<RotateTransform Angle="{Binding QueueButtonAngle}"/>
</Path.RenderTransform>
</Path>
</Button>
</StackPanel>
</Grid>
<Border Grid.Row="2" Margin="8,0" BorderThickness="1" BorderBrush="{DynamicResource SystemBaseMediumLowColor}">
<SplitView IsPaneOpen="{Binding QueueOpen}" DisplayMode="Inline" OpenPaneLength="400" MinWidth="400" PanePlacement="Right">
<!-- Process Queue -->
<SplitView.Pane>
<Border BorderThickness="1,0,0,0" BorderBrush="{DynamicResource SystemBaseMediumLowColor}">
<views:ProcessQueueControl DataContext="{Binding ProcessQueue, Mode=OneTime}"/>
</Border>
</SplitView.Pane>
<!-- Product Display Grid -->
<Panel>
<views:ProductsDisplay
Name="productsDisplay"
DataContext="{Binding ProductsDisplay, Mode=OneTime}"
LiberateClicked="ProductsDisplay_LiberateClicked"
TagsButtonClicked="ProductsDisplay_TagsButtonClicked"
LiberateSeriesClicked="ProductsDisplay_LiberateSeriesClicked"
ConvertToMp3Clicked="ProductsDisplay_ConvertToMp3Clicked" />
<!-- A filter that matched nothing. A trashed book is filtered out of the library and out of
the search index, so searching for one looks the same as searching for a book that was
never imported. Say so here rather than leaving the grid silently blank. -->
<StackPanel
IsVisible="{Binding NoMatchesVisible}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Spacing="10">
<TextBlock
FontSize="16"
HorizontalAlignment="Center"
Foreground="{DynamicResource SystemBaseMediumColor}"
Text="{Binding NoMatchesText}" />
<StackPanel
IsVisible="{Binding NoMatchesTrashHintVisible}"
Orientation="Horizontal"
HorizontalAlignment="Center"
Spacing="10">
<TextBlock
FontSize="16"
VerticalAlignment="Center"
Text="{Binding NoMatchesTrashHintText}" />
<Button
Padding="10,4"
VerticalAlignment="Center"
Command="{Binding ShowTrashBinAsync}"
Content="Open Trash Bin" />
</StackPanel>
</StackPanel>
<!-- Nothing in the library at all. An empty library has two causes with different next
steps, so this offers the one that applies rather than the same advice to everybody. -->
<StackPanel
IsVisible="{Binding GettingStartedVisible}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Spacing="10">
<TextBlock
FontSize="18"
HorizontalAlignment="Center"
Text="{Binding GettingStartedHeadline}" />
<TextBlock
FontSize="14"
HorizontalAlignment="Center"
Foreground="{DynamicResource SystemBaseMediumColor}"
Text="{Binding GettingStartedDetail}" />
<StackPanel
Orientation="Horizontal"
HorizontalAlignment="Center"
Spacing="10">
<Button
Padding="14,5"
IsVisible="{Binding GettingStartedAddAccountVisible}"
Command="{Binding AddAccountsAsync}"
Content="Add Account" />
<Button
Padding="14,5"
IsVisible="{Binding GettingStartedScanVisible}"
Command="{Binding GettingStartedScanAsync}"
Content="Scan Library" />
<Button
Padding="14,5"
Command="{Binding StartWalkthroughAsync}"
Content="Take a Guided Tour" />
</StackPanel>
<!-- The books are not gone, they are in the trash. Saying so keeps the headline above
from reading as though they were never there. -->
<StackPanel
IsVisible="{Binding GettingStartedTrashVisible}"
Orientation="Horizontal"
HorizontalAlignment="Center"
Spacing="10">
<TextBlock
FontSize="14"
VerticalAlignment="Center"
Text="{Binding GettingStartedTrashText}" />
<Button
Padding="10,4"
VerticalAlignment="Center"
Command="{Binding ShowTrashBinAsync}"
Content="Open Trash Bin" />
</StackPanel>
</StackPanel>
</Panel>
</SplitView>
</Border>
<!-- Bottom Status Strip -->
<Grid Grid.Row="3" Margin="8" VerticalAlignment="Bottom" ColumnDefinitions="Auto,Auto,*,Auto,Auto">
<Grid.Styles>
<Style Selector="ProgressBar:horizontal">
<Setter Property="MinWidth" Value="100" />
</Style>
</Grid.Styles>
<TextBlock FontSize="14" Grid.Column="0" Text="Upgrading:" VerticalAlignment="Center" IsVisible="{Binding DownloadProgress, Converter={x:Static ObjectConverters.IsNotNull}}" />
<ProgressBar Grid.Column="1" Margin="5,0,10,0" VerticalAlignment="Stretch" Width="100" Value="{Binding DownloadProgress, TargetNullValue=0}" IsVisible="{Binding DownloadProgress, Converter={x:Static ObjectConverters.IsNotNull}}"/>
<TextBlock FontSize="14" Grid.Column="2" Text="{Binding VisibleCountText}" VerticalAlignment="Center" />
<Button FontSize="14" Grid.Column="3" Margin="0,0,16,0" Padding="8,2" VerticalAlignment="Center"
Command="{Binding ShowTrashBinAsync}"
Content="{Binding TrashBinStatusText}"
ToolTip.Tip="{Binding TrashBinStatusToolTip}"
IsVisible="{Binding TrashBinStatusVisible}" />
<TextBlock FontSize="14" Grid.Column="4" Text="{Binding LibraryStats?.StatusString}" VerticalAlignment="Center" />
</Grid>
</Grid>
</Window>