commit
ba4a1c5a51
@ -1,5 +1,4 @@
|
|||||||
using NPOI.XWPF.UserModel;
|
using System;
|
||||||
using System;
|
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace AppScaffolding
|
namespace AppScaffolding
|
||||||
|
|||||||
@ -12,7 +12,6 @@ using DtoImporterService;
|
|||||||
using FileManager;
|
using FileManager;
|
||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using NPOI.OpenXmlFormats.Spreadsheet;
|
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using static DtoImporterService.PerfLogger;
|
using static DtoImporterService.PerfLogger;
|
||||||
|
|
||||||
@ -21,7 +20,7 @@ namespace ApplicationServices
|
|||||||
public static class LibraryCommands
|
public static class LibraryCommands
|
||||||
{
|
{
|
||||||
public static event EventHandler<int> ScanBegin;
|
public static event EventHandler<int> ScanBegin;
|
||||||
public static event EventHandler ScanEnd;
|
public static event EventHandler<int> ScanEnd;
|
||||||
|
|
||||||
public static bool Scanning { get; private set; }
|
public static bool Scanning { get; private set; }
|
||||||
private static object _lock { get; } = new();
|
private static object _lock { get; } = new();
|
||||||
@ -95,7 +94,7 @@ namespace ApplicationServices
|
|||||||
{
|
{
|
||||||
stop();
|
stop();
|
||||||
var putBreakPointHere = logOutput;
|
var putBreakPointHere = logOutput;
|
||||||
ScanEnd?.Invoke(null, null);
|
ScanEnd?.Invoke(null, 0);
|
||||||
GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true, true);
|
GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -108,7 +107,8 @@ namespace ApplicationServices
|
|||||||
if (accounts is null || accounts.Length == 0)
|
if (accounts is null || accounts.Length == 0)
|
||||||
return (0, 0);
|
return (0, 0);
|
||||||
|
|
||||||
try
|
int newCount = 0;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
@ -134,7 +134,7 @@ namespace ApplicationServices
|
|||||||
|
|
||||||
Log.Logger.Information("Begin long-running import");
|
Log.Logger.Information("Begin long-running import");
|
||||||
logTime($"pre {nameof(importIntoDbAsync)}");
|
logTime($"pre {nameof(importIntoDbAsync)}");
|
||||||
var newCount = await importIntoDbAsync(importItems);
|
newCount = await importIntoDbAsync(importItems);
|
||||||
logTime($"post {nameof(importIntoDbAsync)}");
|
logTime($"post {nameof(importIntoDbAsync)}");
|
||||||
Log.Logger.Information($"Import complete. New count {newCount}");
|
Log.Logger.Information($"Import complete. New count {newCount}");
|
||||||
|
|
||||||
@ -167,7 +167,7 @@ namespace ApplicationServices
|
|||||||
{
|
{
|
||||||
stop();
|
stop();
|
||||||
var putBreakPointHere = logOutput;
|
var putBreakPointHere = logOutput;
|
||||||
ScanEnd?.Invoke(null, null);
|
ScanEnd?.Invoke(null, newCount);
|
||||||
GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true, true);
|
GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -283,7 +283,8 @@ namespace ApplicationServices
|
|||||||
catch(ImportValidationException ex)
|
catch(ImportValidationException ex)
|
||||||
{
|
{
|
||||||
await logDtoItemsAsync(ex.Items, ex.InnerExceptions.ToArray());
|
await logDtoItemsAsync(ex.Items, ex.InnerExceptions.ToArray());
|
||||||
throw;
|
//If ImportValidationException is thrown, all Dto items get logged as part of the exception
|
||||||
|
throw new AggregateException(ex.InnerExceptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task logDtoItemsAsync(IEnumerable<AudibleApi.Common.Item> dtoItems, IEnumerable<Exception> exceptions = null)
|
async Task logDtoItemsAsync(IEnumerable<AudibleApi.Common.Item> dtoItems, IEnumerable<Exception> exceptions = null)
|
||||||
|
|||||||
@ -152,13 +152,17 @@ namespace AudibleUtilities
|
|||||||
SetSeries(parent, children);
|
SetSeries(parent, children);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int orphansRemoved = items.RemoveAll(i => (i.IsEpisodes || i.IsSeriesParent) && i.Series is null);
|
||||||
|
if (orphansRemoved > 0)
|
||||||
|
Serilog.Log.Debug("{orphansRemoved} podcast orphans not imported", orphansRemoved);
|
||||||
|
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
totalTime += sw.Elapsed;
|
totalTime += sw.Elapsed;
|
||||||
Serilog.Log.Logger.Information("Completed indexing series episodes after {elappsed_ms} ms.", sw.ElapsedMilliseconds);
|
Serilog.Log.Logger.Information("Completed indexing series episodes after {elappsed_ms} ms.", sw.ElapsedMilliseconds);
|
||||||
Serilog.Log.Logger.Information($"Completed library scan in {totalTime.TotalMilliseconds:F0} ms.");
|
Serilog.Log.Logger.Information($"Completed library scan in {totalTime.TotalMilliseconds:F0} ms.");
|
||||||
|
|
||||||
var allExceptions = IValidator.GetAllValidators().SelectMany(v => v.Validate(items));
|
var allExceptions = IValidator.GetAllValidators().SelectMany(v => v.Validate(items)).ToList();
|
||||||
if (allExceptions?.Any() is true)
|
if (allExceptions?.Count > 0)
|
||||||
throw new ImportValidationException(items, allExceptions);
|
throw new ImportValidationException(items, allExceptions);
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
|
|||||||
@ -41,13 +41,20 @@ namespace DtoImporterService
|
|||||||
//
|
//
|
||||||
// CURRENT SOLUTION: don't re-insert
|
// CURRENT SOLUTION: don't re-insert
|
||||||
|
|
||||||
var existingEntries = DbContext.LibraryBooks.AsEnumerable().Where(l => l.Book is not null).ToDictionary(l => l.Book.AudibleProductId);
|
|
||||||
var hash = ToDictionarySafe(importItems, dto => dto.DtoItem.ProductId, tieBreak);
|
//When Books are upserted during the BookImporter run, they are linked to their LibraryBook in the DbContext
|
||||||
|
//instance. If a LibraryBook has a null book here, that means it's Book was not imported during by BookImporter.
|
||||||
|
//There should never be duplicates, but this is defensive.
|
||||||
|
var existingEntries = DbContext.LibraryBooks.AsEnumerable().Where(l => l.Book is not null).ToDictionarySafe(l => l.Book.AudibleProductId);
|
||||||
|
|
||||||
|
//If importItems are contains duplicates by asin, keep the Item that's "available"
|
||||||
|
var uniqueImportItems = ToDictionarySafe(importItems, dto => dto.DtoItem.ProductId, tieBreak);
|
||||||
|
|
||||||
int qtyNew = 0;
|
int qtyNew = 0;
|
||||||
|
|
||||||
foreach (var item in hash.Values)
|
foreach (var item in uniqueImportItems.Values)
|
||||||
{
|
{
|
||||||
if (existingEntries.TryGetValue(item.DtoItem.ProductId, out LibraryBook existing))
|
if (qtyNew == 0 && existingEntries.TryGetValue(item.DtoItem.ProductId, out LibraryBook existing))
|
||||||
{
|
{
|
||||||
if (existing.Account != item.AccountId)
|
if (existing.Account != item.AccountId)
|
||||||
{
|
{
|
||||||
@ -109,7 +116,7 @@ namespace DtoImporterService
|
|||||||
=> isPlusTitleUnavailable(item1) && !isPlusTitleUnavailable(item2) ? item2 : item1;
|
=> isPlusTitleUnavailable(item1) && !isPlusTitleUnavailable(item2) ? item2 : item1;
|
||||||
|
|
||||||
private static bool isPlusTitleUnavailable(ImportItem item)
|
private static bool isPlusTitleUnavailable(ImportItem item)
|
||||||
=> item.DtoItem.IsAyce is true
|
=> item.DtoItem.ContentType is null
|
||||||
&& item.DtoItem.Plans?.Any(p => p.IsAyce) is not true;
|
|| (item.DtoItem.IsAyce is true && item.DtoItem.Plans?.Any(p => p.IsAyce) is not true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,24 +1,24 @@
|
|||||||
using Avalonia;
|
using ApplicationServices;
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.ApplicationLifetimes;
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
using LibationFileManager;
|
|
||||||
using LibationAvalonia.Views;
|
|
||||||
using System;
|
|
||||||
using Avalonia.Platform;
|
using Avalonia.Platform;
|
||||||
|
using Avalonia.Styling;
|
||||||
using LibationAvalonia.Dialogs;
|
using LibationAvalonia.Dialogs;
|
||||||
using System.Threading.Tasks;
|
using LibationAvalonia.Views;
|
||||||
|
using LibationFileManager;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using ApplicationServices;
|
using System.Threading.Tasks;
|
||||||
using Avalonia.Controls;
|
|
||||||
using Avalonia.Styling;
|
|
||||||
|
|
||||||
namespace LibationAvalonia
|
namespace LibationAvalonia
|
||||||
{
|
{
|
||||||
public class App : Application
|
public class App : Application
|
||||||
{
|
{
|
||||||
public static Window MainWindow { get;private set; }
|
public static Window MainWindow { get; private set; }
|
||||||
public static IBrush ProcessQueueBookFailedBrush { get; private set; }
|
public static IBrush ProcessQueueBookFailedBrush { get; private set; }
|
||||||
public static IBrush ProcessQueueBookCompletedBrush { get; private set; }
|
public static IBrush ProcessQueueBookCompletedBrush { get; private set; }
|
||||||
public static IBrush ProcessQueueBookCancelledBrush { get; private set; }
|
public static IBrush ProcessQueueBookCancelledBrush { get; private set; }
|
||||||
@ -39,18 +39,15 @@ namespace LibationAvalonia
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Task<List<DataLayer.LibraryBook>> LibraryTask;
|
public static Task<List<DataLayer.LibraryBook>> LibraryTask;
|
||||||
public static bool SetupRequired;
|
|
||||||
|
|
||||||
public override void OnFrameworkInitializationCompleted()
|
public override void OnFrameworkInitializationCompleted()
|
||||||
{
|
{
|
||||||
LoadStyles();
|
|
||||||
|
|
||||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
{
|
{
|
||||||
if (SetupRequired)
|
var config = Configuration.Instance;
|
||||||
{
|
|
||||||
var config = Configuration.Instance;
|
|
||||||
|
|
||||||
|
if (!config.LibationSettingsAreValid)
|
||||||
|
{
|
||||||
var defaultLibationFilesDir = Configuration.UserProfile;
|
var defaultLibationFilesDir = Configuration.UserProfile;
|
||||||
|
|
||||||
// check for existing settings in default location
|
// check for existing settings in default location
|
||||||
@ -60,7 +57,7 @@ namespace LibationAvalonia
|
|||||||
|
|
||||||
if (config.LibationSettingsAreValid)
|
if (config.LibationSettingsAreValid)
|
||||||
{
|
{
|
||||||
LibraryTask = Task.Run(() => DbContexts.GetLibrary_Flat_NoTracking(includeParents: true));
|
LibraryTask = Task.Run(() => DbContexts.GetLibrary_Flat_NoTracking(includeParents: true));
|
||||||
ShowMainWindow(desktop);
|
ShowMainWindow(desktop);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -86,11 +83,29 @@ namespace LibationAvalonia
|
|||||||
{
|
{
|
||||||
// all returns should be preceded by either:
|
// all returns should be preceded by either:
|
||||||
// - if config.LibationSettingsAreValid
|
// - if config.LibationSettingsAreValid
|
||||||
// - error message, Exit()
|
// - error message, Exit()
|
||||||
if (setupDialog.IsNewUser)
|
if (setupDialog.IsNewUser)
|
||||||
{
|
{
|
||||||
Configuration.SetLibationFiles(Configuration.UserProfile);
|
Configuration.SetLibationFiles(Configuration.UserProfile);
|
||||||
ShowSettingsWindow(desktop, setupDialog.Config, OnSettingsCompleted);
|
setupDialog.Config.Books = Path.Combine(Configuration.UserProfile, nameof(Configuration.Books));
|
||||||
|
|
||||||
|
if (setupDialog.Config.LibationSettingsAreValid)
|
||||||
|
{
|
||||||
|
var theme
|
||||||
|
= setupDialog.SelectedTheme.Content is nameof(ThemeVariant.Dark)
|
||||||
|
? nameof(ThemeVariant.Dark)
|
||||||
|
: nameof(ThemeVariant.Light);
|
||||||
|
|
||||||
|
setupDialog.Config.SetString(theme, nameof(ThemeVariant));
|
||||||
|
|
||||||
|
|
||||||
|
await RunMigrationsAsync(setupDialog.Config);
|
||||||
|
LibraryTask = Task.Run(() => DbContexts.GetLibrary_Flat_NoTracking(includeParents: true));
|
||||||
|
AudibleUtilities.AudibleApiStorage.EnsureAccountsSettingsFileExists();
|
||||||
|
ShowMainWindow(desktop);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
await CancelInstallation();
|
||||||
}
|
}
|
||||||
else if (setupDialog.IsReturningUser)
|
else if (setupDialog.IsReturningUser)
|
||||||
{
|
{
|
||||||
@ -130,40 +145,6 @@ namespace LibationAvalonia
|
|||||||
AppScaffolding.LibationScaffolding.RunPostMigrationScaffolding(config);
|
AppScaffolding.LibationScaffolding.RunPostMigrationScaffolding(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ShowSettingsWindow(IClassicDesktopStyleApplicationLifetime desktop, Configuration config, Action<IClassicDesktopStyleApplicationLifetime, SettingsDialog, Configuration> OnClose)
|
|
||||||
{
|
|
||||||
config.Books ??= Path.Combine(Configuration.UserProfile, "Books");
|
|
||||||
|
|
||||||
var settingsDialog = new SettingsDialog();
|
|
||||||
desktop.MainWindow = settingsDialog;
|
|
||||||
settingsDialog.RestoreSizeAndLocation(Configuration.Instance);
|
|
||||||
settingsDialog.Show();
|
|
||||||
|
|
||||||
void WindowClosing(object sender, System.ComponentModel.CancelEventArgs e)
|
|
||||||
{
|
|
||||||
settingsDialog.Closing -= WindowClosing;
|
|
||||||
e.Cancel = true;
|
|
||||||
OnClose?.Invoke(desktop, settingsDialog, config);
|
|
||||||
}
|
|
||||||
settingsDialog.Closing += WindowClosing;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void OnSettingsCompleted(IClassicDesktopStyleApplicationLifetime desktop, SettingsDialog settingsDialog, Configuration config)
|
|
||||||
{
|
|
||||||
if (config.LibationSettingsAreValid)
|
|
||||||
{
|
|
||||||
await RunMigrationsAsync(config);
|
|
||||||
LibraryTask = Task.Run(() => DbContexts.GetLibrary_Flat_NoTracking(includeParents: true));
|
|
||||||
AudibleUtilities.AudibleApiStorage.EnsureAccountsSettingsFileExists();
|
|
||||||
ShowMainWindow(desktop);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
await CancelInstallation();
|
|
||||||
|
|
||||||
settingsDialog.Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void ShowLibationFilesDialog(IClassicDesktopStyleApplicationLifetime desktop, Configuration config, Action<IClassicDesktopStyleApplicationLifetime, LibationFilesDialog, Configuration> OnClose)
|
private void ShowLibationFilesDialog(IClassicDesktopStyleApplicationLifetime desktop, Configuration config, Action<IClassicDesktopStyleApplicationLifetime, LibationFilesDialog, Configuration> OnClose)
|
||||||
{
|
{
|
||||||
var libationFilesDialog = new LibationFilesDialog();
|
var libationFilesDialog = new LibationFilesDialog();
|
||||||
@ -200,11 +181,23 @@ namespace LibationAvalonia
|
|||||||
MessageBoxIcon.Question);
|
MessageBoxIcon.Question);
|
||||||
|
|
||||||
if (continueResult == DialogResult.Yes)
|
if (continueResult == DialogResult.Yes)
|
||||||
ShowSettingsWindow(desktop, config, OnSettingsCompleted);
|
{
|
||||||
|
config.Books = Path.Combine(libationFilesDialog.SelectedDirectory, nameof(Configuration.Books));
|
||||||
|
|
||||||
|
if (config.LibationSettingsAreValid)
|
||||||
|
{
|
||||||
|
await RunMigrationsAsync(config);
|
||||||
|
LibraryTask = Task.Run(() => DbContexts.GetLibrary_Flat_NoTracking(includeParents: true));
|
||||||
|
AudibleUtilities.AudibleApiStorage.EnsureAccountsSettingsFileExists();
|
||||||
|
ShowMainWindow(desktop);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
await CancelInstallation();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
await CancelInstallation();
|
await CancelInstallation();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
libationFilesDialog.Close();
|
libationFilesDialog.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,11 +223,11 @@ namespace LibationAvalonia
|
|||||||
|
|
||||||
private static void LoadStyles()
|
private static void LoadStyles()
|
||||||
{
|
{
|
||||||
ProcessQueueBookFailedBrush = AvaloniaUtils.GetBrushFromResources("ProcessQueueBookFailedBrush");
|
ProcessQueueBookFailedBrush = AvaloniaUtils.GetBrushFromResources(nameof(ProcessQueueBookFailedBrush));
|
||||||
ProcessQueueBookCompletedBrush = AvaloniaUtils.GetBrushFromResources("ProcessQueueBookCompletedBrush");
|
ProcessQueueBookCompletedBrush = AvaloniaUtils.GetBrushFromResources(nameof(ProcessQueueBookCompletedBrush));
|
||||||
ProcessQueueBookCancelledBrush = AvaloniaUtils.GetBrushFromResources("ProcessQueueBookCancelledBrush");
|
ProcessQueueBookCancelledBrush = AvaloniaUtils.GetBrushFromResources(nameof(ProcessQueueBookCancelledBrush));
|
||||||
SeriesEntryGridBackgroundBrush = AvaloniaUtils.GetBrushFromResources("SeriesEntryGridBackgroundBrush");
|
SeriesEntryGridBackgroundBrush = AvaloniaUtils.GetBrushFromResources(nameof(SeriesEntryGridBackgroundBrush));
|
||||||
ProcessQueueBookDefaultBrush = AvaloniaUtils.GetBrushFromResources("ProcessQueueBookDefaultBrush");
|
ProcessQueueBookDefaultBrush = AvaloniaUtils.GetBrushFromResources(nameof(ProcessQueueBookDefaultBrush));
|
||||||
HyperlinkVisited = AvaloniaUtils.GetBrushFromResources(nameof(HyperlinkVisited));
|
HyperlinkVisited = AvaloniaUtils.GetBrushFromResources(nameof(HyperlinkVisited));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@ namespace LibationAvalonia
|
|||||||
public static IBrush GetBrushFromResources(string name)
|
public static IBrush GetBrushFromResources(string name)
|
||||||
=> GetBrushFromResources(name, Brushes.Transparent);
|
=> GetBrushFromResources(name, Brushes.Transparent);
|
||||||
public static IBrush GetBrushFromResources(string name, IBrush defaultBrush)
|
public static IBrush GetBrushFromResources(string name, IBrush defaultBrush)
|
||||||
{
|
{
|
||||||
if (App.Current.TryGetResource(name, App.Current.ActualThemeVariant, out var value) && value is IBrush brush)
|
if (App.Current.TryGetResource(name, App.Current.ActualThemeVariant, out var value) && value is IBrush brush)
|
||||||
return brush;
|
return brush;
|
||||||
return defaultBrush;
|
return defaultBrush;
|
||||||
|
|||||||
@ -3,9 +3,6 @@ using Avalonia.Collections;
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using LibationAvalonia.ViewModels;
|
using LibationAvalonia.ViewModels;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace LibationAvalonia.Controls
|
namespace LibationAvalonia.Controls
|
||||||
{
|
{
|
||||||
@ -15,7 +12,7 @@ namespace LibationAvalonia.Controls
|
|||||||
AvaloniaProperty.Register<CheckedListBox, AvaloniaList<CheckBoxViewModel>>(nameof(Items));
|
AvaloniaProperty.Register<CheckedListBox, AvaloniaList<CheckBoxViewModel>>(nameof(Items));
|
||||||
|
|
||||||
public AvaloniaList<CheckBoxViewModel> Items { get => GetValue(ItemsProperty); set => SetValue(ItemsProperty, value); }
|
public AvaloniaList<CheckBoxViewModel> Items { get => GetValue(ItemsProperty); set => SetValue(ItemsProperty, value); }
|
||||||
private CheckedListBoxViewModel _viewModel = new();
|
private CheckedListBoxViewModel _viewModel = new();
|
||||||
|
|
||||||
public CheckedListBox()
|
public CheckedListBox()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -3,7 +3,7 @@ using LibationUiBase.GridView;
|
|||||||
|
|
||||||
namespace LibationAvalonia.Controls
|
namespace LibationAvalonia.Controls
|
||||||
{
|
{
|
||||||
public class DataGridCheckBoxColumnExt : DataGridCheckBoxColumn
|
public class DataGridCheckBoxColumnExt : DataGridCheckBoxColumn
|
||||||
{
|
{
|
||||||
protected override Control GenerateEditingElementDirect(DataGridCell cell, object dataItem)
|
protected override Control GenerateEditingElementDirect(DataGridCell cell, object dataItem)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -15,7 +15,7 @@ namespace LibationAvalonia.Controls
|
|||||||
|
|
||||||
static DataGridContextMenus()
|
static DataGridContextMenus()
|
||||||
{
|
{
|
||||||
ContextMenu.Items = MenuItems;
|
ContextMenu.ItemsSource = MenuItems;
|
||||||
OwningColumnProperty = typeof(DataGridCell).GetProperty("OwningColumn", BindingFlags.Instance | BindingFlags.NonPublic);
|
OwningColumnProperty = typeof(DataGridCell).GetProperty("OwningColumn", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,6 +66,6 @@ namespace LibationAvalonia.Controls
|
|||||||
public IGridEntry GridEntry { get; init; }
|
public IGridEntry GridEntry { get; init; }
|
||||||
public ContextMenu ContextMenu { get; init; }
|
public ContextMenu ContextMenu { get; init; }
|
||||||
public AvaloniaList<Control> ContextMenuItems
|
public AvaloniaList<Control> ContextMenuItems
|
||||||
=> ContextMenu.Items as AvaloniaList<Control>;
|
=> ContextMenu.ItemsSource as AvaloniaList<Control>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,6 @@ using Avalonia.Data;
|
|||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using DataLayer;
|
using DataLayer;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace LibationAvalonia.Controls
|
namespace LibationAvalonia.Controls
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace LibationAvalonia.Controls
|
namespace LibationAvalonia.Controls
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using Dinah.Core;
|
using Dinah.Core;
|
||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
using System.Collections.Generic;
|
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace LibationAvalonia.Controls
|
namespace LibationAvalonia.Controls
|
||||||
@ -56,12 +55,12 @@ namespace LibationAvalonia.Controls
|
|||||||
directorySelectControl.PropertyChanged += DirectorySelectControl_PropertyChanged;
|
directorySelectControl.PropertyChanged += DirectorySelectControl_PropertyChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CustomState: ViewModels.ViewModelBase
|
private class CustomState : ViewModels.ViewModelBase
|
||||||
{
|
{
|
||||||
private string _customDir;
|
private string _customDir;
|
||||||
private bool _knownChecked;
|
private bool _knownChecked;
|
||||||
private bool _customChecked;
|
private bool _customChecked;
|
||||||
public string CustomDir { get=> _customDir; set => this.RaiseAndSetIfChanged(ref _customDir, value); }
|
public string CustomDir { get => _customDir; set => this.RaiseAndSetIfChanged(ref _customDir, value); }
|
||||||
public bool KnownChecked
|
public bool KnownChecked
|
||||||
{
|
{
|
||||||
get => _knownChecked;
|
get => _knownChecked;
|
||||||
@ -141,7 +140,7 @@ namespace LibationAvalonia.Controls
|
|||||||
var known = Configuration.GetKnownDirectory(noSubDir);
|
var known = Configuration.GetKnownDirectory(noSubDir);
|
||||||
|
|
||||||
if (known == Configuration.KnownDirectories.None && noSubDir == Configuration.AppDir_Absolute)
|
if (known == Configuration.KnownDirectories.None && noSubDir == Configuration.AppDir_Absolute)
|
||||||
known = Configuration.KnownDirectories.AppDir;
|
known = Configuration.KnownDirectories.AppDir;
|
||||||
|
|
||||||
if (known is Configuration.KnownDirectories.None)
|
if (known is Configuration.KnownDirectories.None)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,13 +1,12 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Data;
|
||||||
|
using Avalonia.Data.Converters;
|
||||||
using Dinah.Core;
|
using Dinah.Core;
|
||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
using System.Collections.Generic;
|
|
||||||
using Avalonia.Data.Converters;
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using Avalonia.Data;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reactive.Subjects;
|
using System.Reactive.Subjects;
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
|
|
||||||
namespace LibationAvalonia.Controls
|
namespace LibationAvalonia.Controls
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,8 +1,5 @@
|
|||||||
using Avalonia;
|
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using Avalonia.Media;
|
|
||||||
using Avalonia.Styling;
|
using Avalonia.Styling;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
|
|||||||
@ -18,7 +18,7 @@ namespace LibationAvalonia.Controls
|
|||||||
|
|
||||||
public bool IsEditingMode { get; set; }
|
public bool IsEditingMode { get; set; }
|
||||||
public Rating Rating { get => GetValue(RatingProperty); set => SetValue(RatingProperty, value); }
|
public Rating Rating { get => GetValue(RatingProperty); set => SetValue(RatingProperty, value); }
|
||||||
|
|
||||||
public MyRatingCellEditor()
|
public MyRatingCellEditor()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|||||||
@ -1,11 +1,7 @@
|
|||||||
using Avalonia;
|
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using Avalonia.Styling;
|
using Avalonia.Styling;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace LibationAvalonia.Controls
|
namespace LibationAvalonia.Controls
|
||||||
{
|
{
|
||||||
|
|||||||
@ -64,11 +64,14 @@
|
|||||||
<DataGridCheckBoxColumn
|
<DataGridCheckBoxColumn
|
||||||
Binding="{Binding LibraryScan, Mode=TwoWay}"
|
Binding="{Binding LibraryScan, Mode=TwoWay}"
|
||||||
Header="Include in
library scan?"/>
|
Header="Include in
library scan?"/>
|
||||||
|
|
||||||
<DataGridTextColumn
|
<DataGridTemplateColumn Width="2*" Header="Audible
email/login">
|
||||||
Width="2*"
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
Binding="{Binding AccountId, Mode=TwoWay}"
|
<DataTemplate>
|
||||||
Header="Audible
email/login"/>
|
<TextBox Text="{Binding AccountId, Mode=TwoWay}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
|
</DataGridTemplateColumn>
|
||||||
|
|
||||||
<DataGridTemplateColumn Width="Auto" Header="Locale">
|
<DataGridTemplateColumn Width="Auto" Header="Locale">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
@ -93,10 +96,13 @@
|
|||||||
</DataGridTemplateColumn.CellTemplate>
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
</DataGridTemplateColumn>
|
</DataGridTemplateColumn>
|
||||||
|
|
||||||
<DataGridTextColumn
|
<DataGridTemplateColumn Width="3*" Header="Account Nickname
(optional)">
|
||||||
Width="3*"
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
Binding="{Binding AccountName, Mode=TwoWay}"
|
<DataTemplate>
|
||||||
Header="Account Nickname
(optional)"/>
|
<TextBox Text="{Binding AccountName, Mode=TwoWay}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
|
</DataGridTemplateColumn>
|
||||||
|
|
||||||
</DataGrid.Columns>
|
</DataGrid.Columns>
|
||||||
</DataGrid>
|
</DataGrid>
|
||||||
|
|||||||
@ -1,27 +1,24 @@
|
|||||||
|
using AudibleApi;
|
||||||
using AudibleUtilities;
|
using AudibleUtilities;
|
||||||
|
using Avalonia.Collections;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Platform.Storage;
|
||||||
|
using ReactiveUI;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.Specialized;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using ReactiveUI;
|
|
||||||
using AudibleApi;
|
|
||||||
using Avalonia.Platform.Storage;
|
|
||||||
using LibationFileManager;
|
|
||||||
using Avalonia.Platform.Storage.FileIO;
|
|
||||||
|
|
||||||
namespace LibationAvalonia.Dialogs
|
namespace LibationAvalonia.Dialogs
|
||||||
{
|
{
|
||||||
public partial class AccountsDialog : DialogWindow
|
public partial class AccountsDialog : DialogWindow
|
||||||
{
|
{
|
||||||
public ObservableCollection<AccountDto> Accounts { get; } = new();
|
public AvaloniaList<AccountDto> Accounts { get; } = new();
|
||||||
public class AccountDto : ViewModels.ViewModelBase
|
public class AccountDto : ViewModels.ViewModelBase
|
||||||
{
|
{
|
||||||
private string _accountId;
|
private string _accountId;
|
||||||
private Locale _selectedLocale;
|
|
||||||
public IReadOnlyList<Locale> Locales => AccountsDialog.Locales;
|
public IReadOnlyList<Locale> Locales => AccountsDialog.Locales;
|
||||||
public bool LibraryScan { get; set; } = true;
|
public bool LibraryScan { get; set; } = true;
|
||||||
public string AccountId
|
public string AccountId
|
||||||
@ -31,19 +28,21 @@ namespace LibationAvalonia.Dialogs
|
|||||||
{
|
{
|
||||||
this.RaiseAndSetIfChanged(ref _accountId, value);
|
this.RaiseAndSetIfChanged(ref _accountId, value);
|
||||||
this.RaisePropertyChanged(nameof(IsDefault));
|
this.RaisePropertyChanged(nameof(IsDefault));
|
||||||
}
|
|
||||||
}
|
|
||||||
public Locale SelectedLocale
|
|
||||||
{
|
|
||||||
get => _selectedLocale;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
this.RaiseAndSetIfChanged(ref _selectedLocale, value);
|
|
||||||
this.RaisePropertyChanged(nameof(IsDefault));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Locale SelectedLocale { get; set; }
|
||||||
public string AccountName { get; set; }
|
public string AccountName { get; set; }
|
||||||
public bool IsDefault => string.IsNullOrEmpty(AccountId) && SelectedLocale is null;
|
public bool IsDefault => string.IsNullOrEmpty(AccountId);
|
||||||
|
|
||||||
|
public AccountDto() { }
|
||||||
|
public AccountDto(Account account)
|
||||||
|
{
|
||||||
|
LibraryScan = account.LibraryScan;
|
||||||
|
AccountId = account.AccountId;
|
||||||
|
SelectedLocale = Locales.Single(l => l.Name == account.Locale.Name);
|
||||||
|
AccountName = account.AccountName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetAudibleCliAppDataPath()
|
private static string GetAudibleCliAppDataPath()
|
||||||
@ -58,57 +57,40 @@ namespace LibationAvalonia.Dialogs
|
|||||||
// here: copy strings and dispose of persister
|
// here: copy strings and dispose of persister
|
||||||
// only persist in 'save' step
|
// only persist in 'save' step
|
||||||
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
||||||
var accounts = persister.AccountsSettings.Accounts;
|
|
||||||
if (accounts.Any())
|
Accounts.CollectionChanged += Accounts_CollectionChanged;
|
||||||
{
|
Accounts.AddRange(persister.AccountsSettings.Accounts.Select(a => new AccountDto(a)));
|
||||||
foreach (var account in accounts)
|
|
||||||
AddAccountToGrid(account);
|
|
||||||
}
|
|
||||||
|
|
||||||
DataContext = this;
|
DataContext = this;
|
||||||
addBlankAccount();
|
addBlankAccount();
|
||||||
}
|
}
|
||||||
private void addBlankAccount()
|
|
||||||
{
|
|
||||||
|
|
||||||
var newBlank = new AccountDto();
|
private void Accounts_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||||
newBlank.PropertyChanged += AccountDto_PropertyChanged;
|
|
||||||
Accounts.Insert(Accounts.Count, newBlank);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddAccountToGrid(Account account)
|
|
||||||
{
|
{
|
||||||
AccountDto accountDto = new()
|
if (e.Action is NotifyCollectionChangedAction.Add && e.NewItems?.Count > 0)
|
||||||
{
|
{
|
||||||
LibraryScan = account.LibraryScan,
|
foreach (var newItem in e.NewItems.OfType<AccountDto>())
|
||||||
AccountId = account.AccountId,
|
newItem.PropertyChanged += AccountDto_PropertyChanged;
|
||||||
SelectedLocale = Locales.Single(l => l.Name == account.Locale.Name),
|
}
|
||||||
AccountName = account.AccountName,
|
else if (e.Action is NotifyCollectionChangedAction.Remove && e.OldItems?.Count > 0)
|
||||||
};
|
{
|
||||||
accountDto.PropertyChanged += AccountDto_PropertyChanged;
|
foreach (var oldItem in e.OldItems.OfType<AccountDto>())
|
||||||
|
oldItem.PropertyChanged -= AccountDto_PropertyChanged;
|
||||||
//ObservableCollection doesn't fire CollectionChanged on Add, so use Insert instead
|
}
|
||||||
Accounts.Insert(Accounts.Count, accountDto);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addBlankAccount() => Accounts.Insert(Accounts.Count, new AccountDto());
|
||||||
|
|
||||||
private void AccountDto_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
private void AccountDto_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (Accounts.Any(a => a.IsDefault))
|
if (!Accounts.Any(a => a.IsDefault))
|
||||||
return;
|
addBlankAccount();
|
||||||
|
|
||||||
addBlankAccount();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DeleteButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public void DeleteButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.Source is Button expBtn && expBtn.DataContext is AccountDto acc)
|
if (e.Source is Button expBtn && expBtn.DataContext is AccountDto acc)
|
||||||
{
|
|
||||||
var index = Accounts.IndexOf(acc);
|
|
||||||
if (index < 0) return;
|
|
||||||
|
|
||||||
acc.PropertyChanged -= AccountDto_PropertyChanged;
|
|
||||||
Accounts.Remove(acc);
|
Accounts.Remove(acc);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void ImportButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public async void ImportButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
@ -119,11 +101,11 @@ namespace LibationAvalonia.Dialogs
|
|||||||
AllowMultiple = false,
|
AllowMultiple = false,
|
||||||
FileTypeFilter = new FilePickerFileType[]
|
FileTypeFilter = new FilePickerFileType[]
|
||||||
{
|
{
|
||||||
new("JSON files (*.json)")
|
new("JSON files (*.json)")
|
||||||
{
|
{
|
||||||
Patterns = new[] { "*.json" },
|
Patterns = new[] { "*.json" },
|
||||||
AppleUniformTypeIdentifiers = new[] { "public.json" }
|
AppleUniformTypeIdentifiers = new[] { "public.json" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -153,7 +135,7 @@ namespace LibationAvalonia.Dialogs
|
|||||||
|
|
||||||
persister.AccountsSettings.Add(account);
|
persister.AccountsSettings.Add(account);
|
||||||
|
|
||||||
AddAccountToGrid(account);
|
Accounts.Add(new AccountDto(account));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
using ApplicationServices;
|
using ApplicationServices;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using Avalonia.Media.Imaging;
|
using Avalonia.Media.Imaging;
|
||||||
using DataLayer;
|
using DataLayer;
|
||||||
using Dinah.Core;
|
using Dinah.Core;
|
||||||
using LibationFileManager;
|
|
||||||
using LibationAvalonia.ViewModels;
|
using LibationAvalonia.ViewModels;
|
||||||
|
using LibationFileManager;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -42,14 +41,14 @@ namespace LibationAvalonia.Dialogs
|
|||||||
LibraryBook = context.GetLibraryBook_Flat_NoTracking("B017V4IM1G");
|
LibraryBook = context.GetLibraryBook_Flat_NoTracking("B017V4IM1G");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public BookDetailsDialog(LibraryBook libraryBook) :this()
|
public BookDetailsDialog(LibraryBook libraryBook) : this()
|
||||||
{
|
{
|
||||||
LibraryBook = libraryBook;
|
LibraryBook = libraryBook;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void SaveAndClose()
|
protected override void SaveAndClose()
|
||||||
{
|
{
|
||||||
LibraryBook.Book.UpdateUserDefinedItem(NewTags, bookStatus: BookLiberatedStatus, pdfStatus: PdfLiberatedStatus);
|
LibraryBook.Book.UpdateUserDefinedItem(NewTags, bookStatus: BookLiberatedStatus, pdfStatus: PdfLiberatedStatus);
|
||||||
base.SaveAndClose();
|
base.SaveAndClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +60,7 @@ namespace LibationAvalonia.Dialogs
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void SaveButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public void SaveButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
=> SaveAndClose();
|
=> SaveAndClose();
|
||||||
|
|
||||||
private class BookDetailsDialogViewModel : ViewModelBase
|
private class BookDetailsDialogViewModel : ViewModelBase
|
||||||
{
|
{
|
||||||
|
|||||||
@ -79,7 +79,7 @@ namespace LibationAvalonia.Dialogs
|
|||||||
|
|
||||||
public void CheckAll_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public void CheckAll_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
foreach (var record in bookRecordEntries)
|
foreach (var record in bookRecordEntries)
|
||||||
record.IsChecked = true;
|
record.IsChecked = true;
|
||||||
}
|
}
|
||||||
public void UncheckAll_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public void UncheckAll_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace LibationAvalonia.Dialogs
|
namespace LibationAvalonia.Dialogs
|
||||||
@ -12,9 +11,7 @@ namespace LibationAvalonia.Dialogs
|
|||||||
public DescriptionDisplayDialog()
|
public DescriptionDisplayDialog()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
#if DEBUG
|
|
||||||
this.AttachDevTools();
|
|
||||||
#endif
|
|
||||||
DescriptionTextBox = this.FindControl<TextBox>(nameof(DescriptionTextBox));
|
DescriptionTextBox = this.FindControl<TextBox>(nameof(DescriptionTextBox));
|
||||||
this.Activated += DescriptionDisplay_Activated;
|
this.Activated += DescriptionDisplay_Activated;
|
||||||
Opened += DescriptionDisplay_Opened;
|
Opened += DescriptionDisplay_Opened;
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
using Avalonia;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls;
|
|
||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -17,10 +16,6 @@ namespace LibationAvalonia.Dialogs
|
|||||||
this.Initialized += DialogWindow_Initialized;
|
this.Initialized += DialogWindow_Initialized;
|
||||||
this.Opened += DialogWindow_Opened;
|
this.Opened += DialogWindow_Opened;
|
||||||
this.Closing += DialogWindow_Closing;
|
this.Closing += DialogWindow_Closing;
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
this.AttachDevTools();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
public DialogWindow(bool saveAndRestorePosition) : this()
|
public DialogWindow(bool saveAndRestorePosition) : this()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -42,13 +42,16 @@
|
|||||||
|
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
</DataGridTemplateColumn>
|
</DataGridTemplateColumn>
|
||||||
|
|
||||||
|
<DataGridTemplateColumn Width="*" Header="Filter">
|
||||||
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<TextBox Text="{Binding FilterString, Mode=TwoWay}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
|
</DataGridTemplateColumn>
|
||||||
|
|
||||||
<DataGridTextColumn
|
|
||||||
Width="*"
|
|
||||||
IsReadOnly="False"
|
|
||||||
Binding="{Binding FilterString, Mode=TwoWay}"
|
|
||||||
Header="Filter"/>
|
|
||||||
|
|
||||||
<DataGridTemplateColumn Header="Move
Up">
|
<DataGridTemplateColumn Header="Move
Up">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
using AudibleUtilities;
|
using AudibleUtilities;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
|
using ReactiveUI;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using ReactiveUI;
|
|
||||||
|
|
||||||
namespace LibationAvalonia.Dialogs
|
namespace LibationAvalonia.Dialogs
|
||||||
{
|
{
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450"
|
||||||
MinWidth="500" MinHeight="450"
|
MinWidth="500" MinHeight="450"
|
||||||
|
Width="500" Height="450"
|
||||||
x:Class="LibationAvalonia.Dialogs.EditReplacementChars"
|
x:Class="LibationAvalonia.Dialogs.EditReplacementChars"
|
||||||
Title="Illegal Character Replacement"
|
Title="Illegal Character Replacement"
|
||||||
Icon="/Assets/libation.ico">
|
Icon="/Assets/libation.ico">
|
||||||
@ -26,21 +27,31 @@
|
|||||||
Items="{Binding replacements}">
|
Items="{Binding replacements}">
|
||||||
|
|
||||||
<DataGrid.Columns>
|
<DataGrid.Columns>
|
||||||
|
|
||||||
|
|
||||||
|
<DataGridTemplateColumn Header="Char to
Replace">
|
||||||
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<TextBox IsReadOnly="{Binding Mandatory}" Text="{Binding CharacterToReplace, Mode=TwoWay}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
|
</DataGridTemplateColumn>
|
||||||
|
|
||||||
<DataGridTextColumn
|
<DataGridTemplateColumn Header="Replacement
Text">
|
||||||
IsReadOnly="False"
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
Binding="{Binding CharacterToReplace, Mode=TwoWay}"
|
<DataTemplate>
|
||||||
Header="Char to
Replace"/>
|
<TextBox Text="{Binding ReplacementText, Mode=TwoWay}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
|
</DataGridTemplateColumn>
|
||||||
|
|
||||||
<DataGridTextColumn
|
<DataGridTemplateColumn Width="*" Header="Description">
|
||||||
IsReadOnly="False"
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
Binding="{Binding ReplacementText, Mode=TwoWay}"
|
<DataTemplate>
|
||||||
Header="Replacement
Text"/>
|
<TextBox IsReadOnly="{Binding Mandatory}" Text="{Binding Description, Mode=TwoWay}" />
|
||||||
|
</DataTemplate>
|
||||||
<DataGridTextColumn Width="*"
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
IsReadOnly="False"
|
</DataGridTemplateColumn>
|
||||||
Binding="{Binding Description, Mode=TwoWay}"
|
|
||||||
Header="Description"/>
|
|
||||||
|
|
||||||
</DataGrid.Columns>
|
</DataGrid.Columns>
|
||||||
</DataGrid>
|
</DataGrid>
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
|
using Avalonia.Collections;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Data;
|
||||||
using FileManager;
|
using FileManager;
|
||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
using System.Collections.Generic;
|
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Avalonia.Collections;
|
|
||||||
using Avalonia.Data;
|
|
||||||
|
|
||||||
namespace LibationAvalonia.Dialogs
|
namespace LibationAvalonia.Dialogs
|
||||||
{
|
{
|
||||||
@ -37,7 +36,7 @@ namespace LibationAvalonia.Dialogs
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void Defaults_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public void Defaults_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
=> LoadTable(ReplacementCharacters.Default.Replacements);
|
=> LoadTable(ReplacementCharacters.Default.Replacements);
|
||||||
public void LoFiDefaults_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public void LoFiDefaults_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
=> LoadTable(ReplacementCharacters.LoFiDefault.Replacements);
|
=> LoadTable(ReplacementCharacters.LoFiDefault.Replacements);
|
||||||
public void Barebones_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public void Barebones_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
@ -50,7 +49,7 @@ namespace LibationAvalonia.Dialogs
|
|||||||
protected override void SaveAndClose()
|
protected override void SaveAndClose()
|
||||||
{
|
{
|
||||||
var replacements = SOURCE
|
var replacements = SOURCE
|
||||||
.Where(r=> !r.IsDefault)
|
.Where(r => !r.IsDefault)
|
||||||
.Select(r => new Replacement(r.Character, r.ReplacementText, r.Description) { Mandatory = r.Mandatory })
|
.Select(r => new Replacement(r.Character, r.ReplacementText, r.Description) { Mandatory = r.Mandatory })
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
Width="800" Height="450"
|
||||||
x:Class="LibationAvalonia.Dialogs.EditTemplateDialog"
|
x:Class="LibationAvalonia.Dialogs.EditTemplateDialog"
|
||||||
xmlns:dialogs="clr-namespace:LibationAvalonia.Dialogs"
|
xmlns:dialogs="clr-namespace:LibationAvalonia.Dialogs"
|
||||||
Icon="/Assets/libation.ico"
|
Icon="/Assets/libation.ico"
|
||||||
|
|||||||
@ -1,17 +1,15 @@
|
|||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Collections;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Documents;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
using Dinah.Core;
|
using Dinah.Core;
|
||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
|
using ReactiveUI;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using ReactiveUI;
|
|
||||||
using Avalonia.Controls.Documents;
|
|
||||||
using Avalonia.Collections;
|
|
||||||
using Avalonia.Controls;
|
|
||||||
using Avalonia.Markup.Xaml.Templates;
|
|
||||||
|
|
||||||
namespace LibationAvalonia.Dialogs
|
namespace LibationAvalonia.Dialogs
|
||||||
{
|
{
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="500"
|
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="500"
|
||||||
x:Class="LibationAvalonia.Dialogs.ImageDisplayDialog"
|
x:Class="LibationAvalonia.Dialogs.ImageDisplayDialog"
|
||||||
MinWidth="500" MinHeight="500"
|
MinWidth="500" MinHeight="500"
|
||||||
|
Width="500" Height="520"
|
||||||
Title="Cover"
|
Title="Cover"
|
||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner"
|
||||||
Icon="/Assets/libation.ico">
|
Icon="/Assets/libation.ico">
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using Avalonia.Media.Imaging;
|
using Avalonia.Media.Imaging;
|
||||||
|
using Avalonia.Platform.Storage;
|
||||||
|
using ReactiveUI;
|
||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using ReactiveUI;
|
|
||||||
using Avalonia.Platform.Storage;
|
|
||||||
|
|
||||||
namespace LibationAvalonia.Dialogs
|
namespace LibationAvalonia.Dialogs
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,10 +1,6 @@
|
|||||||
using Avalonia;
|
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
using LibationAvalonia.Controls;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace LibationAvalonia.Dialogs
|
namespace LibationAvalonia.Dialogs
|
||||||
{
|
{
|
||||||
@ -18,7 +14,7 @@ namespace LibationAvalonia.Dialogs
|
|||||||
Configuration.KnownDirectories.AppDir,
|
Configuration.KnownDirectories.AppDir,
|
||||||
Configuration.KnownDirectories.MyDocs
|
Configuration.KnownDirectories.MyDocs
|
||||||
};
|
};
|
||||||
|
|
||||||
public string Directory { get; set; } = Configuration.GetKnownDirectoryPath(Configuration.KnownDirectories.UserProfile);
|
public string Directory { get; set; } = Configuration.GetKnownDirectoryPath(Configuration.KnownDirectories.UserProfile);
|
||||||
}
|
}
|
||||||
private DirSelectOptions dirSelectOptions;
|
private DirSelectOptions dirSelectOptions;
|
||||||
@ -28,9 +24,6 @@ namespace LibationAvalonia.Dialogs
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
this.AttachDevTools();
|
|
||||||
#endif
|
|
||||||
DataContext = dirSelectOptions = new();
|
DataContext = dirSelectOptions = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
Title="Liberated status: Whether the book has been downloaded"
|
Title="Liberated status: Whether the book has been downloaded"
|
||||||
MinHeight="100" MaxHeight="165"
|
MinHeight="100" MaxHeight="165"
|
||||||
MinWidth="600" MaxWidth="800"
|
MinWidth="600" MaxWidth="800"
|
||||||
|
Width="600"
|
||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner"
|
||||||
Icon="/Assets/libation.ico">
|
Icon="/Assets/libation.ico">
|
||||||
|
|
||||||
|
|||||||
@ -1,13 +1,11 @@
|
|||||||
using Avalonia.Markup.Xaml;
|
|
||||||
|
|
||||||
namespace LibationAvalonia.Dialogs
|
namespace LibationAvalonia.Dialogs
|
||||||
{
|
{
|
||||||
public partial class LiberatedStatusBatchAutoDialog : DialogWindow
|
public partial class LiberatedStatusBatchAutoDialog : DialogWindow
|
||||||
{
|
{
|
||||||
public bool SetDownloaded { get; set; }
|
public bool SetDownloaded { get; set; }
|
||||||
public bool SetNotDownloaded { get; set; }
|
public bool SetNotDownloaded { get; set; }
|
||||||
|
|
||||||
public LiberatedStatusBatchAutoDialog()
|
public LiberatedStatusBatchAutoDialog()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
DataContext = this;
|
DataContext = this;
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
Title="Liberated status: Whether the book has been downloaded"
|
Title="Liberated status: Whether the book has been downloaded"
|
||||||
MinWidth="400" MinHeight="120"
|
MinWidth="400" MinHeight="120"
|
||||||
MaxWidth="400" MaxHeight="120"
|
MaxWidth="400" MaxHeight="120"
|
||||||
|
Width="400" Height="120"
|
||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner"
|
||||||
Icon="/Assets/libation.ico">
|
Icon="/Assets/libation.ico">
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using DataLayer;
|
using DataLayer;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -34,13 +33,13 @@ namespace LibationAvalonia.Dialogs
|
|||||||
new liberatedComboBoxItem { Status = LiberatedStatus.NotLiberated, Text = "Not Downloaded" },
|
new liberatedComboBoxItem { Status = LiberatedStatus.NotLiberated, Text = "Not Downloaded" },
|
||||||
};
|
};
|
||||||
|
|
||||||
public LiberatedStatusBatchManualDialog(bool isPdf) : this()
|
public LiberatedStatusBatchManualDialog(bool isPdf) : this()
|
||||||
{
|
{
|
||||||
if (isPdf)
|
if (isPdf)
|
||||||
this.Title = this.Title.Replace("book", "PDF");
|
this.Title = this.Title.Replace("book", "PDF");
|
||||||
}
|
}
|
||||||
|
|
||||||
public LiberatedStatusBatchManualDialog()
|
public LiberatedStatusBatchManualDialog()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
SelectedItem = BookStatuses[0] as liberatedComboBoxItem;
|
SelectedItem = BookStatuses[0] as liberatedComboBoxItem;
|
||||||
|
|||||||
@ -2,7 +2,6 @@ using ApplicationServices;
|
|||||||
using Avalonia.Collections;
|
using Avalonia.Collections;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Platform.Storage;
|
using Avalonia.Platform.Storage;
|
||||||
using Avalonia.Platform.Storage.FileIO;
|
|
||||||
using DataLayer;
|
using DataLayer;
|
||||||
using LibationAvalonia.ViewModels;
|
using LibationAvalonia.ViewModels;
|
||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
@ -53,7 +52,7 @@ namespace LibationAvalonia.Dialogs
|
|||||||
|
|
||||||
private void LocateAudiobooks_FileFound(object sender, FilePathCache.CacheEntry e)
|
private void LocateAudiobooks_FileFound(object sender, FilePathCache.CacheEntry e)
|
||||||
{
|
{
|
||||||
var newItem = new Tuple<string,string>($"[{e.Id}]", Path.GetFileName(e.Path));
|
var newItem = new Tuple<string, string>($"[{e.Id}]", Path.GetFileName(e.Path));
|
||||||
_viewModel.FoundFiles.Add(newItem);
|
_viewModel.FoundFiles.Add(newItem);
|
||||||
foundAudiobooksLB.SelectedItem = newItem;
|
foundAudiobooksLB.SelectedItem = newItem;
|
||||||
|
|
||||||
@ -70,7 +69,7 @@ namespace LibationAvalonia.Dialogs
|
|||||||
{
|
{
|
||||||
Title = "Select the folder to search for audiobooks",
|
Title = "Select the folder to search for audiobooks",
|
||||||
AllowMultiple = false,
|
AllowMultiple = false,
|
||||||
SuggestedStartLocation = await StorageProvider.TryGetFolderFromPathAsync(Configuration.Instance.Books.PathWithoutPrefix)
|
SuggestedStartLocation = await StorageProvider.TryGetFolderFromPathAsync(Configuration.Instance.Books.PathWithoutPrefix)
|
||||||
};
|
};
|
||||||
|
|
||||||
var selectedFolder = (await StorageProvider.OpenFolderPickerAsync(folderPicker))?.SingleOrDefault()?.TryGetLocalPath();
|
var selectedFolder = (await StorageProvider.OpenFolderPickerAsync(folderPicker))?.SingleOrDefault()?.TryGetLocalPath();
|
||||||
|
|||||||
@ -1,6 +1,3 @@
|
|||||||
using Avalonia;
|
|
||||||
using Avalonia.Controls;
|
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace LibationAvalonia.Dialogs.Login
|
namespace LibationAvalonia.Dialogs.Login
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
using System;
|
using AudibleApi;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using AudibleApi;
|
|
||||||
using AudibleUtilities;
|
using AudibleUtilities;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace LibationAvalonia.Dialogs.Login
|
namespace LibationAvalonia.Dialogs.Login
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
using System;
|
using AudibleApi;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using AudibleApi;
|
|
||||||
using AudibleUtilities;
|
using AudibleUtilities;
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace LibationAvalonia.Dialogs.Login
|
namespace LibationAvalonia.Dialogs.Login
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using Avalonia.Media.Imaging;
|
using Avalonia.Media.Imaging;
|
||||||
using LibationAvalonia.ViewModels;
|
using LibationAvalonia.ViewModels;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
@ -21,7 +20,7 @@ namespace LibationAvalonia.Dialogs.Login
|
|||||||
captchaBox = this.FindControl<TextBox>(nameof(captchaBox));
|
captchaBox = this.FindControl<TextBox>(nameof(captchaBox));
|
||||||
}
|
}
|
||||||
|
|
||||||
public CaptchaDialog(string password, byte[] captchaImage) :this()
|
public CaptchaDialog(string password, byte[] captchaImage) : this()
|
||||||
{
|
{
|
||||||
//Avalonia doesn't support animated gifs.
|
//Avalonia doesn't support animated gifs.
|
||||||
//Deconstruct gifs into frames and manually switch them.
|
//Deconstruct gifs into frames and manually switch them.
|
||||||
@ -34,7 +33,7 @@ namespace LibationAvalonia.Dialogs.Login
|
|||||||
{
|
{
|
||||||
var frameMetadata = gif.Frames[i].Metadata.GetFormatMetadata(SixLabors.ImageSharp.Formats.Gif.GifFormat.Instance);
|
var frameMetadata = gif.Frames[i].Metadata.GetFormatMetadata(SixLabors.ImageSharp.Formats.Gif.GifFormat.Instance);
|
||||||
|
|
||||||
using var clonedFrame = gif.Frames.CloneFrame(i);
|
using var clonedFrame = gif.Frames.CloneFrame(i);
|
||||||
using var framems = new MemoryStream();
|
using var framems = new MemoryStream();
|
||||||
|
|
||||||
clonedFrame.Save(framems, gifEncoder);
|
clonedFrame.Save(framems, gifEncoder);
|
||||||
@ -66,7 +65,7 @@ namespace LibationAvalonia.Dialogs.Login
|
|||||||
protected override async Task CancelAndCloseAsync()
|
protected override async Task CancelAndCloseAsync()
|
||||||
{
|
{
|
||||||
await _viewModel.StopAsync();
|
await _viewModel.StopAsync();
|
||||||
await base.CancelAndCloseAsync();
|
await base.CancelAndCloseAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void Submit_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public async void Submit_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
@ -106,7 +105,7 @@ namespace LibationAvalonia.Dialogs.Login
|
|||||||
private async Task SwitchFramesAsync(Bitmap[] gifFrames, int[] frameDelayMs)
|
private async Task SwitchFramesAsync(Bitmap[] gifFrames, int[] frameDelayMs)
|
||||||
{
|
{
|
||||||
int index = 0;
|
int index = 0;
|
||||||
while(keepSwitching)
|
while (keepSwitching)
|
||||||
{
|
{
|
||||||
CaptchaImage = gifFrames[index];
|
CaptchaImage = gifFrames[index];
|
||||||
await Task.Delay(frameDelayMs[index++]);
|
await Task.Delay(frameDelayMs[index++]);
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
using AudibleUtilities;
|
using AudibleUtilities;
|
||||||
using Avalonia;
|
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using Dinah.Core;
|
using Dinah.Core;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
using AudibleApi;
|
using AudibleApi;
|
||||||
using AudibleUtilities;
|
using AudibleUtilities;
|
||||||
using Avalonia;
|
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@ -26,7 +24,7 @@ namespace LibationAvalonia.Dialogs.Login
|
|||||||
DataContext = this;
|
DataContext = this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public LoginChoiceEagerDialog(Account account):this()
|
public LoginChoiceEagerDialog(Account account) : this()
|
||||||
{
|
{
|
||||||
Account = account;
|
Account = account;
|
||||||
DataContext = this;
|
DataContext = this;
|
||||||
@ -34,7 +32,7 @@ namespace LibationAvalonia.Dialogs.Login
|
|||||||
|
|
||||||
protected override async Task SaveAndCloseAsync()
|
protected override async Task SaveAndCloseAsync()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(Password))
|
if (LoginMethod is LoginMethod.Api && string.IsNullOrWhiteSpace(Password))
|
||||||
{
|
{
|
||||||
await MessageBox.Show(this, "Please enter your password");
|
await MessageBox.Show(this, "Please enter your password");
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
using AudibleUtilities;
|
using AudibleUtilities;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using Dinah.Core;
|
using Dinah.Core;
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -28,7 +27,7 @@ namespace LibationAvalonia.Dialogs.Login
|
|||||||
DataContext = this;
|
DataContext = this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public LoginExternalDialog(Account account, string loginUrl):this()
|
public LoginExternalDialog(Account account, string loginUrl) : this()
|
||||||
{
|
{
|
||||||
Account = account;
|
Account = account;
|
||||||
ExternalLoginUrl = loginUrl;
|
ExternalLoginUrl = loginUrl;
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Data;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Avalonia.Data;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace LibationAvalonia.Dialogs.Login
|
namespace LibationAvalonia.Dialogs.Login
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace LibationAvalonia.Dialogs.Login
|
namespace LibationAvalonia.Dialogs.Login
|
||||||
@ -12,7 +11,7 @@ namespace LibationAvalonia.Dialogs.Login
|
|||||||
|
|
||||||
public _2faCodeDialog()
|
public _2faCodeDialog()
|
||||||
{
|
{
|
||||||
AvaloniaXamlLoader.Load(this);
|
InitializeComponent();
|
||||||
_2FABox = this.FindControl<TextBox>(nameof(_2FABox));
|
_2FABox = this.FindControl<TextBox>(nameof(_2FABox));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using Dinah.Core;
|
using Dinah.Core;
|
||||||
using FileManager;
|
using FileManager;
|
||||||
using System;
|
using System;
|
||||||
|
|||||||
@ -1,6 +1,3 @@
|
|||||||
using Avalonia;
|
|
||||||
using Avalonia.Controls;
|
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using LibationAvalonia.ViewModels.Dialogs;
|
using LibationAvalonia.ViewModels.Dialogs;
|
||||||
|
|
||||||
namespace LibationAvalonia.Dialogs
|
namespace LibationAvalonia.Dialogs
|
||||||
@ -12,7 +9,7 @@ namespace LibationAvalonia.Dialogs
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
public MessageBoxWindow(bool saveAndRestorePosition):base(saveAndRestorePosition)
|
public MessageBoxWindow(bool saveAndRestorePosition) : base(saveAndRestorePosition)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="185"
|
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="185"
|
||||||
x:Class="LibationAvalonia.Dialogs.ScanAccountsDialog"
|
x:Class="LibationAvalonia.Dialogs.ScanAccountsDialog"
|
||||||
MinWidth="500" MinHeight="160"
|
MinWidth="500" MinHeight="160"
|
||||||
MaxWidth="500" MaxHeight="185"
|
Width="500" Height="200"
|
||||||
Title="Which Accounts?"
|
Title="Which Accounts?"
|
||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner"
|
||||||
Icon="/Assets/libation.ico">
|
Icon="/Assets/libation.ico">
|
||||||
|
|||||||
@ -1,12 +1,9 @@
|
|||||||
using AudibleUtilities;
|
using AudibleUtilities;
|
||||||
using Avalonia;
|
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace LibationAvalonia.Dialogs
|
namespace LibationAvalonia.Dialogs
|
||||||
{
|
{
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
mc:Ignorable="d" d:DesignWidth="950" d:DesignHeight="650"
|
mc:Ignorable="d" d:DesignWidth="950" d:DesignHeight="650"
|
||||||
MinWidth="950" MinHeight="650"
|
MinWidth="950" MinHeight="650"
|
||||||
MaxWidth="950" MaxHeight="650"
|
MaxWidth="950" MaxHeight="650"
|
||||||
|
Width="950" Height="650"
|
||||||
x:Class="LibationAvalonia.Dialogs.SearchSyntaxDialog"
|
x:Class="LibationAvalonia.Dialogs.SearchSyntaxDialog"
|
||||||
Title="Filter Options"
|
Title="Filter Options"
|
||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner"
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
using Avalonia.Markup.Xaml;
|
|
||||||
|
|
||||||
namespace LibationAvalonia.Dialogs
|
namespace LibationAvalonia.Dialogs
|
||||||
{
|
{
|
||||||
public partial class SearchSyntaxDialog : DialogWindow
|
public partial class SearchSyntaxDialog : DialogWindow
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
mc:Ignorable="d" d:DesignWidth="900" d:DesignHeight="750"
|
mc:Ignorable="d" d:DesignWidth="900" d:DesignHeight="750"
|
||||||
MinWidth="900" MinHeight="700"
|
MinWidth="900" MinHeight="700"
|
||||||
|
Width="900" Height="700"
|
||||||
x:Class="LibationAvalonia.Dialogs.SettingsDialog"
|
x:Class="LibationAvalonia.Dialogs.SettingsDialog"
|
||||||
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
||||||
Title="Edit Settings"
|
Title="Edit Settings"
|
||||||
@ -20,7 +21,7 @@
|
|||||||
Content="Save"
|
Content="Save"
|
||||||
Click="SaveButton_Clicked" />
|
Click="SaveButton_Clicked" />
|
||||||
|
|
||||||
<TabControl Grid.Column="0">
|
<TabControl Name="tabControl" Grid.Column="0">
|
||||||
<TabControl.Styles>
|
<TabControl.Styles>
|
||||||
<Style Selector="ItemsPresenter#PART_ItemsPresenter">
|
<Style Selector="ItemsPresenter#PART_ItemsPresenter">
|
||||||
<Setter Property="Height" Value="28"/>
|
<Setter Property="Height" Value="28"/>
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
|
using Avalonia.Collections;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Dinah.Core;
|
||||||
|
using FileManager;
|
||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
|
using LibationUiBase;
|
||||||
|
using ReactiveUI;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using ReactiveUI;
|
|
||||||
using Dinah.Core;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FileManager;
|
using System.Threading.Tasks;
|
||||||
using Avalonia.Collections;
|
|
||||||
using LibationUiBase;
|
|
||||||
|
|
||||||
namespace LibationAvalonia.Dialogs
|
namespace LibationAvalonia.Dialogs
|
||||||
{
|
{
|
||||||
@ -51,7 +51,7 @@ namespace LibationAvalonia.Dialogs
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async void EditFileTemplateButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public async void EditFileTemplateButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var newTemplate = await editTemplate(TemplateEditor<Templates.FileTemplate>.CreateFilenameEditor(config.Books, settingsDisp.DownloadDecryptSettings.FileTemplate));
|
var newTemplate = await editTemplate(TemplateEditor<Templates.FileTemplate>.CreateFilenameEditor(config.Books, settingsDisp.DownloadDecryptSettings.FileTemplate));
|
||||||
if (newTemplate is not null)
|
if (newTemplate is not null)
|
||||||
settingsDisp.DownloadDecryptSettings.FileTemplate = newTemplate;
|
settingsDisp.DownloadDecryptSettings.FileTemplate = newTemplate;
|
||||||
@ -59,7 +59,7 @@ namespace LibationAvalonia.Dialogs
|
|||||||
|
|
||||||
public async void EditChapterFileTemplateButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public async void EditChapterFileTemplateButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
|
|
||||||
var newTemplate = await editTemplate(TemplateEditor<Templates.ChapterFileTemplate>.CreateFilenameEditor(config.Books, settingsDisp.DownloadDecryptSettings.ChapterFileTemplate));
|
var newTemplate = await editTemplate(TemplateEditor<Templates.ChapterFileTemplate>.CreateFilenameEditor(config.Books, settingsDisp.DownloadDecryptSettings.ChapterFileTemplate));
|
||||||
if (newTemplate is not null)
|
if (newTemplate is not null)
|
||||||
settingsDisp.DownloadDecryptSettings.ChapterFileTemplate = newTemplate;
|
settingsDisp.DownloadDecryptSettings.ChapterFileTemplate = newTemplate;
|
||||||
@ -197,7 +197,7 @@ namespace LibationAvalonia.Dialogs
|
|||||||
this.RaiseAndSetIfChanged(ref themeVariant, value);
|
this.RaiseAndSetIfChanged(ref themeVariant, value);
|
||||||
|
|
||||||
SelectionChanged = ThemeVariant != InitialThemeVariant;
|
SelectionChanged = ThemeVariant != InitialThemeVariant;
|
||||||
this.RaisePropertyChanged(nameof(SelectionChanged));
|
this.RaisePropertyChanged(nameof(SelectionChanged));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public string InitialThemeVariant { get; private set; }
|
public string InitialThemeVariant { get; private set; }
|
||||||
@ -242,7 +242,7 @@ namespace LibationAvalonia.Dialogs
|
|||||||
public bool DownloadEpisodes { get; set; }
|
public bool DownloadEpisodes { get; set; }
|
||||||
public bool AutoDownloadEpisodes { get; set; }
|
public bool AutoDownloadEpisodes { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DownloadDecryptSettings : ViewModels.ViewModelBase, ISettingsDisplay
|
public class DownloadDecryptSettings : ViewModels.ViewModelBase, ISettingsDisplay
|
||||||
{
|
{
|
||||||
private bool _badBookAsk;
|
private bool _badBookAsk;
|
||||||
@ -345,7 +345,7 @@ namespace LibationAvalonia.Dialogs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public bool BadBookRetry
|
public bool BadBookRetry
|
||||||
{
|
{
|
||||||
get => _badBookRetry;
|
get => _badBookRetry;
|
||||||
set
|
set
|
||||||
@ -392,7 +392,7 @@ namespace LibationAvalonia.Dialogs
|
|||||||
|
|
||||||
public AvaloniaList<SampleRateSelection> SampleRates { get; }
|
public AvaloniaList<SampleRateSelection> SampleRates { get; }
|
||||||
= new(
|
= new(
|
||||||
new []
|
new[]
|
||||||
{
|
{
|
||||||
AAXClean.SampleRate.Hz_44100,
|
AAXClean.SampleRate.Hz_44100,
|
||||||
AAXClean.SampleRate.Hz_32000,
|
AAXClean.SampleRate.Hz_32000,
|
||||||
|
|||||||
@ -3,14 +3,16 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="350"
|
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="350"
|
||||||
MinWidth="500" MinHeight="350"
|
|
||||||
MaxWidth="500" MaxHeight="350"
|
|
||||||
x:Class="LibationAvalonia.Dialogs.SetupDialog"
|
x:Class="LibationAvalonia.Dialogs.SetupDialog"
|
||||||
WindowStartupLocation="CenterScreen"
|
WindowStartupLocation="CenterScreen"
|
||||||
|
Width="500" Height="350"
|
||||||
Icon="/Assets/libation.ico"
|
Icon="/Assets/libation.ico"
|
||||||
Title="Welcome to Libation">
|
Title="Welcome to Libation">
|
||||||
|
|
||||||
<Grid Margin="10" ColumnDefinitions="*" RowDefinitions="*,Auto,Auto">
|
<Grid
|
||||||
|
Margin="10"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
RowDefinitions="*,Auto,Auto">
|
||||||
|
|
||||||
<TextBlock Grid.Row="0" TextWrapping="Wrap" Text="This appears to be your first time using Libation or a previous setup was incomplete.
|
<TextBlock Grid.Row="0" TextWrapping="Wrap" Text="This appears to be your first time using Libation or a previous setup was incomplete.
|
||||||


|


|
||||||
@ -21,18 +23,48 @@
|
|||||||


|


|
||||||

Download your entire library from the "Liberate" tab or
|

Download your entire library from the "Liberate" tab or
|
||||||

liberate your books one at a time by clicking the stoplight." />
|

liberate your books one at a time by clicking the stoplight." />
|
||||||
|
|
||||||
<Button
|
|
||||||
Grid.Row="1"
|
|
||||||
Width="480"
|
|
||||||
Margin="0,0,0,10"
|
|
||||||
Click="NewUser_Click">
|
|
||||||
|
|
||||||
|
<Grid
|
||||||
|
Grid.Row="1"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
ColumnDefinitions="*,Auto"
|
||||||
|
Margin="0,0,0,10">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
HorizontalContentAlignment="Center"
|
||||||
|
Padding="0,20"
|
||||||
|
Margin="0,0,10,0"
|
||||||
|
Click="NewUser_Click">
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
FontSize="18"
|
||||||
|
TextAlignment="Center"
|
||||||
|
Text="NEW USER"/>
|
||||||
|
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Grid
|
||||||
|
Grid.Column="1"
|
||||||
|
RowDefinitions="*,*">
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
TextAlignment="Center"
|
VerticalAlignment="Top"
|
||||||
Text="NEW USER

Choose Settings"/>
|
Text="Theme: " />
|
||||||
|
|
||||||
</Button>
|
<ComboBox
|
||||||
|
Grid.Row="1"
|
||||||
|
VerticalAlignment="Bottom"
|
||||||
|
SelectedIndex="0"
|
||||||
|
SelectedItem="{Binding SelectedTheme, Mode=OneWayToSource}">
|
||||||
|
<ComboBox.Items>
|
||||||
|
<ComboBoxItem Content="Light" />
|
||||||
|
<ComboBoxItem Content="Dark" />
|
||||||
|
</ComboBox.Items>
|
||||||
|
</ComboBox>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
|
|||||||
@ -1,22 +1,18 @@
|
|||||||
using Avalonia;
|
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
|
|
||||||
namespace LibationAvalonia.Dialogs
|
namespace LibationAvalonia.Dialogs
|
||||||
{
|
{
|
||||||
public partial class SetupDialog : Window
|
public partial class SetupDialog : Window
|
||||||
{
|
{
|
||||||
public bool IsNewUser { get;private set; }
|
public bool IsNewUser { get; private set; }
|
||||||
public bool IsReturningUser { get;private set; }
|
public bool IsReturningUser { get; private set; }
|
||||||
|
public ComboBoxItem SelectedTheme { get; set; }
|
||||||
public Configuration Config { get; init; }
|
public Configuration Config { get; init; }
|
||||||
public SetupDialog()
|
public SetupDialog()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
DataContext = this;
|
||||||
#if DEBUG
|
|
||||||
this.AttachDevTools();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void NewUser_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public void NewUser_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
x:Class="LibationAvalonia.Dialogs.TagsBatchDialog"
|
x:Class="LibationAvalonia.Dialogs.TagsBatchDialog"
|
||||||
MinWidth="630" MinHeight="110"
|
MinWidth="630" MinHeight="110"
|
||||||
MaxWidth="630" MaxHeight="110"
|
MaxWidth="630" MaxHeight="110"
|
||||||
|
Width="630" Height="110"
|
||||||
Title="Replace Tags"
|
Title="Replace Tags"
|
||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner"
|
||||||
Icon="/Assets/libation.ico">
|
Icon="/Assets/libation.ico">
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
using Avalonia;
|
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
|
|
||||||
namespace LibationAvalonia.Dialogs
|
namespace LibationAvalonia.Dialogs
|
||||||
{
|
{
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
x:Class="LibationAvalonia.Dialogs.TrashBinDialog"
|
x:Class="LibationAvalonia.Dialogs.TrashBinDialog"
|
||||||
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
||||||
MinWidth="630" MinHeight="480"
|
MinWidth="630" MinHeight="480"
|
||||||
|
Width="630" Height="480"
|
||||||
Title="Trash Bin"
|
Title="Trash Bin"
|
||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner"
|
||||||
Icon="/Assets/libation.ico">
|
Icon="/Assets/libation.ico">
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
using ApplicationServices;
|
using ApplicationServices;
|
||||||
using Avalonia.Collections;
|
using Avalonia.Collections;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Threading;
|
|
||||||
using DataLayer;
|
using DataLayer;
|
||||||
using LibationAvalonia.Controls;
|
using LibationAvalonia.Controls;
|
||||||
using LibationAvalonia.ViewModels;
|
using LibationAvalonia.ViewModels;
|
||||||
@ -25,7 +24,7 @@ namespace LibationAvalonia.Dialogs
|
|||||||
this.RestoreSizeAndLocation(Configuration.Instance);
|
this.RestoreSizeAndLocation(Configuration.Instance);
|
||||||
DataContext = _viewModel = new();
|
DataContext = _viewModel = new();
|
||||||
|
|
||||||
this.Closing += (_,_) => this.SaveSizeAndLocation(Configuration.Instance);
|
this.Closing += (_, _) => this.SaveSizeAndLocation(Configuration.Instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void EmptyTrash_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public async void EmptyTrash_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
@ -40,7 +39,7 @@ namespace LibationAvalonia.Dialogs
|
|||||||
public string CheckedCountText => $"Checked : {_checkedBooksCount} of {_totalBooksCount}";
|
public string CheckedCountText => $"Checked : {_checkedBooksCount} of {_totalBooksCount}";
|
||||||
|
|
||||||
private bool _controlsEnabled = true;
|
private bool _controlsEnabled = true;
|
||||||
public bool ControlsEnabled { get => _controlsEnabled; set=> this.RaiseAndSetIfChanged(ref _controlsEnabled, value); }
|
public bool ControlsEnabled { get => _controlsEnabled; set => this.RaiseAndSetIfChanged(ref _controlsEnabled, value); }
|
||||||
|
|
||||||
private bool? everythingChecked = false;
|
private bool? everythingChecked = false;
|
||||||
public bool? EverythingChecked
|
public bool? EverythingChecked
|
||||||
@ -96,7 +95,7 @@ namespace LibationAvalonia.Dialogs
|
|||||||
{
|
{
|
||||||
foreach (var item in DeletedBooks)
|
foreach (var item in DeletedBooks)
|
||||||
item.IsChecked = true;
|
item.IsChecked = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UncheckAll()
|
public void UncheckAll()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
using AppScaffolding;
|
using AppScaffolding;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Dinah.Core;
|
using Dinah.Core;
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace LibationAvalonia.Dialogs
|
namespace LibationAvalonia.Dialogs
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
using System;
|
using Avalonia;
|
||||||
using System.Linq;
|
|
||||||
using Avalonia;
|
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.ApplicationLifetimes;
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace LibationAvalonia
|
namespace LibationAvalonia
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,16 +1,15 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.ApplicationLifetimes;
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
|
using Avalonia.Threading;
|
||||||
using DataLayer;
|
using DataLayer;
|
||||||
using Dinah.Core.Logging;
|
using Dinah.Core.Logging;
|
||||||
using LibationAvalonia.ViewModels.Dialogs;
|
|
||||||
using LibationAvalonia.Dialogs;
|
using LibationAvalonia.Dialogs;
|
||||||
|
using LibationAvalonia.ViewModels.Dialogs;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading;
|
|
||||||
using Avalonia.Threading;
|
|
||||||
|
|
||||||
namespace LibationAvalonia
|
namespace LibationAvalonia
|
||||||
{
|
{
|
||||||
@ -64,23 +63,23 @@ namespace LibationAvalonia
|
|||||||
|
|
||||||
public static Task<DialogResult> Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton)
|
public static Task<DialogResult> Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton)
|
||||||
=> ShowCoreAsync(null, text, caption, buttons, icon, defaultButton);
|
=> ShowCoreAsync(null, text, caption, buttons, icon, defaultButton);
|
||||||
public static Task<DialogResult> Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, bool saveAndRestorePosition = true)
|
public static Task<DialogResult> Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, bool saveAndRestorePosition = true)
|
||||||
=> ShowCoreAsync(null, text, caption, buttons, icon, MessageBoxDefaultButton.Button1, saveAndRestorePosition);
|
=> ShowCoreAsync(null, text, caption, buttons, icon, MessageBoxDefaultButton.Button1, saveAndRestorePosition);
|
||||||
public static Task<DialogResult> Show(string text, string caption, MessageBoxButtons buttons)
|
public static Task<DialogResult> Show(string text, string caption, MessageBoxButtons buttons)
|
||||||
=> ShowCoreAsync(null, text, caption, buttons, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
=> ShowCoreAsync(null, text, caption, buttons, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
||||||
public static Task<DialogResult> Show(string text, string caption)
|
public static Task<DialogResult> Show(string text, string caption)
|
||||||
=> ShowCoreAsync(null, text, caption, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
=> ShowCoreAsync(null, text, caption, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
||||||
public static Task<DialogResult> Show(string text)
|
public static Task<DialogResult> Show(string text)
|
||||||
=> ShowCoreAsync(null, text, string.Empty, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
=> ShowCoreAsync(null, text, string.Empty, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
||||||
public static Task<DialogResult> Show(Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton)
|
public static Task<DialogResult> Show(Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton)
|
||||||
=> ShowCoreAsync(owner, text, caption, buttons, icon, defaultButton);
|
=> ShowCoreAsync(owner, text, caption, buttons, icon, defaultButton);
|
||||||
|
|
||||||
public static Task<DialogResult> Show(Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon)
|
public static Task<DialogResult> Show(Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon)
|
||||||
=> ShowCoreAsync(owner, text, caption, buttons, icon, MessageBoxDefaultButton.Button1);
|
=> ShowCoreAsync(owner, text, caption, buttons, icon, MessageBoxDefaultButton.Button1);
|
||||||
public static Task<DialogResult> Show(Window owner, string text, string caption, MessageBoxButtons buttons)
|
public static Task<DialogResult> Show(Window owner, string text, string caption, MessageBoxButtons buttons)
|
||||||
=> ShowCoreAsync(owner, text, caption, buttons, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
=> ShowCoreAsync(owner, text, caption, buttons, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
||||||
public static Task<DialogResult> Show(Window owner, string text, string caption)
|
public static Task<DialogResult> Show(Window owner, string text, string caption)
|
||||||
=> ShowCoreAsync(owner, text, caption, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
=> ShowCoreAsync(owner, text, caption, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
||||||
public static Task<DialogResult> Show(Window owner, string text)
|
public static Task<DialogResult> Show(Window owner, string text)
|
||||||
=> ShowCoreAsync(owner, text, string.Empty, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
=> ShowCoreAsync(owner, text, string.Empty, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
||||||
|
|
||||||
@ -102,10 +101,10 @@ Libation.
|
|||||||
".Trim(), "Verbose logging enabled", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
".Trim(), "Verbose logging enabled", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Note: the format field should use {0} and NOT use the `$` string interpolation. Formatting is done inside this method.
|
/// Note: the format field should use {0} and NOT use the `$` string interpolation. Formatting is done inside this method.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static async Task<DialogResult> ShowConfirmationDialog(Window owner, IEnumerable<LibraryBook> libraryBooks, string format, string title, MessageBoxDefaultButton defaultButton = MessageBoxDefaultButton.Button1)
|
public static async Task<DialogResult> ShowConfirmationDialog(Window owner, IEnumerable<LibraryBook> libraryBooks, string format, string title, MessageBoxDefaultButton defaultButton = MessageBoxDefaultButton.Button1)
|
||||||
{
|
{
|
||||||
if (libraryBooks is null || !libraryBooks.Any())
|
if (libraryBooks is null || !libraryBooks.Any())
|
||||||
return DialogResult.Cancel;
|
return DialogResult.Cancel;
|
||||||
@ -120,7 +119,7 @@ Libation.
|
|||||||
= string.Format(format, $"{thisThese} {count} {bookBooks}")
|
= string.Format(format, $"{thisThese} {count} {bookBooks}")
|
||||||
+ $"\r\n\r\n{titlesAgg}";
|
+ $"\r\n\r\n{titlesAgg}";
|
||||||
|
|
||||||
return await ShowCoreAsync(owner,
|
return await ShowCoreAsync(owner,
|
||||||
message,
|
message,
|
||||||
title,
|
title,
|
||||||
MessageBoxButtons.YesNo,
|
MessageBoxButtons.YesNo,
|
||||||
|
|||||||
@ -43,8 +43,6 @@ namespace LibationAvalonia
|
|||||||
// Migrations which must occur before configuration is loaded for the first time. Usually ones which alter the Configuration
|
// Migrations which must occur before configuration is loaded for the first time. Usually ones which alter the Configuration
|
||||||
var config = LibationScaffolding.RunPreConfigMigrations();
|
var config = LibationScaffolding.RunPreConfigMigrations();
|
||||||
|
|
||||||
App.SetupRequired = !config.LibationSettingsAreValid;
|
|
||||||
|
|
||||||
//Start as much work in parallel as possible.
|
//Start as much work in parallel as possible.
|
||||||
var classicLifetimeTask = Task.Run(() => new ClassicDesktopStyleApplicationLifetime());
|
var classicLifetimeTask = Task.Run(() => new ClassicDesktopStyleApplicationLifetime());
|
||||||
var appBuilderTask = Task.Run(BuildAvaloniaApp);
|
var appBuilderTask = Task.Run(BuildAvaloniaApp);
|
||||||
@ -55,7 +53,7 @@ namespace LibationAvalonia
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
if (!App.SetupRequired)
|
if (config.LibationSettingsAreValid)
|
||||||
{
|
{
|
||||||
if (!RunDbMigrations(config))
|
if (!RunDbMigrations(config))
|
||||||
return;
|
return;
|
||||||
@ -63,7 +61,7 @@ namespace LibationAvalonia
|
|||||||
App.LibraryTask = Task.Run(() => DbContexts.GetLibrary_Flat_NoTracking(includeParents: true));
|
App.LibraryTask = Task.Run(() => DbContexts.GetLibrary_Flat_NoTracking(includeParents: true));
|
||||||
}
|
}
|
||||||
|
|
||||||
(appBuilderTask.GetAwaiter().GetResult()).SetupWithLifetime(classicLifetimeTask.GetAwaiter().GetResult());
|
appBuilderTask.GetAwaiter().GetResult().SetupWithLifetime(classicLifetimeTask.GetAwaiter().GetResult());
|
||||||
|
|
||||||
classicLifetimeTask.Result.Start(null);
|
classicLifetimeTask.Result.Start(null);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,7 @@ using System;
|
|||||||
|
|
||||||
namespace LibationAvalonia.ViewModels
|
namespace LibationAvalonia.ViewModels
|
||||||
{
|
{
|
||||||
public class AvaloniaEntryStatus : EntryStatus, IEntryStatus, IComparable
|
public class AvaloniaEntryStatus : EntryStatus, IEntryStatus, IComparable
|
||||||
{
|
{
|
||||||
public override IBrush BackgroundBrush => IsEpisode ? App.SeriesEntryGridBackgroundBrush : Brushes.Transparent;
|
public override IBrush BackgroundBrush => IsEpisode ? App.SeriesEntryGridBackgroundBrush : Brushes.Transparent;
|
||||||
|
|
||||||
|
|||||||
@ -22,8 +22,8 @@ namespace LibationAvalonia.ViewModels.Dialogs
|
|||||||
public bool HasButton3 => !string.IsNullOrEmpty(Button3Text);
|
public bool HasButton3 => !string.IsNullOrEmpty(Button3Text);
|
||||||
public bool HasButton2 => !string.IsNullOrEmpty(Button2Text);
|
public bool HasButton2 => !string.IsNullOrEmpty(Button2Text);
|
||||||
|
|
||||||
public int WindowHeight { get;private set; }
|
public int WindowHeight { get; private set; }
|
||||||
public int WindowWidth { get;private set; }
|
public int WindowWidth { get; private set; }
|
||||||
|
|
||||||
public string Button1Text => _button switch
|
public string Button1Text => _button switch
|
||||||
{
|
{
|
||||||
@ -47,7 +47,7 @@ namespace LibationAvalonia.ViewModels.Dialogs
|
|||||||
MessageBoxButtons.CancelTryContinue => "Try",
|
MessageBoxButtons.CancelTryContinue => "Try",
|
||||||
_ => string.Empty,
|
_ => string.Empty,
|
||||||
};
|
};
|
||||||
|
|
||||||
public string Button3Text => _button switch
|
public string Button3Text => _button switch
|
||||||
{
|
{
|
||||||
MessageBoxButtons.AbortRetryIgnore => "Ignore",
|
MessageBoxButtons.AbortRetryIgnore => "Ignore",
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
using ApplicationServices;
|
using ApplicationServices;
|
||||||
|
using Avalonia.Collections;
|
||||||
|
using Avalonia.Controls;
|
||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
|
|
||||||
@ -41,24 +43,25 @@ namespace LibationAvalonia.ViewModels
|
|||||||
|
|
||||||
|
|
||||||
/// <summary> Auto scanning accounts is enables </summary>
|
/// <summary> Auto scanning accounts is enables </summary>
|
||||||
public bool AutoScanChecked
|
public bool AutoScanChecked
|
||||||
{
|
{
|
||||||
get => _autoScanChecked;
|
get => _autoScanChecked;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (value != _autoScanChecked)
|
if (value != _autoScanChecked)
|
||||||
Configuration.Instance.AutoScan = value;
|
Configuration.Instance.AutoScan = value;
|
||||||
this.RaiseAndSetIfChanged(ref _autoScanChecked, value);
|
this.RaiseAndSetIfChanged(ref _autoScanChecked, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AvaloniaList<Control> QuickFilterMenuItems { get; } = new();
|
||||||
|
|
||||||
/// <summary> Indicates if the first quick filter is the default filter </summary>
|
/// <summary> Indicates if the first quick filter is the default filter </summary>
|
||||||
public bool FirstFilterIsDefault
|
public bool FirstFilterIsDefault
|
||||||
{
|
{
|
||||||
get => _firstFilterIsDefault;
|
get => _firstFilterIsDefault;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (value != _firstFilterIsDefault)
|
if (value != _firstFilterIsDefault)
|
||||||
QuickFilters.UseDefault = value;
|
QuickFilters.UseDefault = value;
|
||||||
this.RaiseAndSetIfChanged(ref _firstFilterIsDefault, value);
|
this.RaiseAndSetIfChanged(ref _firstFilterIsDefault, value);
|
||||||
|
|||||||
@ -1,8 +1,4 @@
|
|||||||
using System;
|
using ApplicationServices;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using ApplicationServices;
|
|
||||||
using AudibleApi;
|
using AudibleApi;
|
||||||
using AudibleApi.Common;
|
using AudibleApi.Common;
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
@ -15,6 +11,10 @@ using FileLiberator;
|
|||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
using LibationUiBase;
|
using LibationUiBase;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace LibationAvalonia.ViewModels
|
namespace LibationAvalonia.ViewModels
|
||||||
{
|
{
|
||||||
@ -65,7 +65,7 @@ namespace LibationAvalonia.ViewModels
|
|||||||
public string Author { get => _author; set => Dispatcher.UIThread.Post(() => this.RaiseAndSetIfChanged(ref _author, value)); }
|
public string Author { get => _author; set => Dispatcher.UIThread.Post(() => this.RaiseAndSetIfChanged(ref _author, value)); }
|
||||||
public string Title { get => _title; set => Dispatcher.UIThread.Post(() => this.RaiseAndSetIfChanged(ref _title, value)); }
|
public string Title { get => _title; set => Dispatcher.UIThread.Post(() => this.RaiseAndSetIfChanged(ref _title, value)); }
|
||||||
public int Progress { get => _progress; private set => Dispatcher.UIThread.Post(() => this.RaiseAndSetIfChanged(ref _progress, value)); }
|
public int Progress { get => _progress; private set => Dispatcher.UIThread.Post(() => this.RaiseAndSetIfChanged(ref _progress, value)); }
|
||||||
public string ETA { get => _eta; private set => Dispatcher.UIThread.Post(() =>this.RaiseAndSetIfChanged(ref _eta, value)); }
|
public string ETA { get => _eta; private set => Dispatcher.UIThread.Post(() => this.RaiseAndSetIfChanged(ref _eta, value)); }
|
||||||
public Bitmap Cover { get => _cover; private set => Dispatcher.UIThread.Post(() => this.RaiseAndSetIfChanged(ref _cover, value)); }
|
public Bitmap Cover { get => _cover; private set => Dispatcher.UIThread.Post(() => this.RaiseAndSetIfChanged(ref _cover, value)); }
|
||||||
public bool IsFinished => Status is not ProcessBookStatus.Queued and not ProcessBookStatus.Working;
|
public bool IsFinished => Status is not ProcessBookStatus.Queued and not ProcessBookStatus.Working;
|
||||||
public bool IsDownloading => Status is ProcessBookStatus.Working;
|
public bool IsDownloading => Status is ProcessBookStatus.Working;
|
||||||
@ -315,36 +315,36 @@ namespace LibationAvalonia.ViewModels
|
|||||||
Logger.Info($"{((Processable)sender).Name} Step, Completed: {libraryBook.Book}");
|
Logger.Info($"{((Processable)sender).Name} Step, Completed: {libraryBook.Book}");
|
||||||
UnlinkProcessable((Processable)sender);
|
UnlinkProcessable((Processable)sender);
|
||||||
|
|
||||||
if (Processes.Count == 0)
|
if (Processes.Count == 0)
|
||||||
{
|
{
|
||||||
Completed?.Invoke(this, EventArgs.Empty);
|
Completed?.Invoke(this, EventArgs.Empty);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
NextProcessable();
|
NextProcessable();
|
||||||
LinkProcessable(CurrentProcessable);
|
LinkProcessable(CurrentProcessable);
|
||||||
|
|
||||||
StatusHandler result;
|
StatusHandler result;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
result = await CurrentProcessable.ProcessSingleAsync(libraryBook, validate: true);
|
result = await CurrentProcessable.ProcessSingleAsync(libraryBook, validate: true);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Serilog.Log.Logger.Error(ex, $"{nameof(Processable_Completed)} error");
|
Serilog.Log.Logger.Error(ex, $"{nameof(Processable_Completed)} error");
|
||||||
|
|
||||||
result = new StatusHandler();
|
result = new StatusHandler();
|
||||||
result.AddError($"{nameof(Processable_Completed)} error. See log for details. Error summary: {ex.Message}");
|
result.AddError($"{nameof(Processable_Completed)} error. See log for details. Error summary: {ex.Message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.HasErrors)
|
if (result.HasErrors)
|
||||||
{
|
{
|
||||||
foreach (var errorMessage in result.Errors.Where(e => e != "Validation failed"))
|
foreach (var errorMessage in result.Errors.Where(e => e != "Validation failed"))
|
||||||
Logger.Error(errorMessage);
|
Logger.Error(errorMessage);
|
||||||
|
|
||||||
Completed?.Invoke(this, EventArgs.Empty);
|
Completed?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|||||||
@ -33,7 +33,7 @@ namespace LibationAvalonia.ViewModels
|
|||||||
Queue.CompletedCountChanged += Queue_CompletedCountChanged;
|
Queue.CompletedCountChanged += Queue_CompletedCountChanged;
|
||||||
|
|
||||||
if (Design.IsDesignMode)
|
if (Design.IsDesignMode)
|
||||||
_ = Configuration.Instance.LibationFiles;
|
_ = Configuration.Instance.LibationFiles;
|
||||||
|
|
||||||
SpeedLimit = Configuration.Instance.DownloadSpeedLimit / 1024m / 1024;
|
SpeedLimit = Configuration.Instance.DownloadSpeedLimit / 1024m / 1024;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -133,12 +133,12 @@ namespace LibationAvalonia.ViewModels
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void GridEntries_CollectionChanged(object sender = null, EventArgs e = null)
|
private void GridEntries_CollectionChanged(object sender = null, EventArgs e = null)
|
||||||
{
|
{
|
||||||
var count
|
var count
|
||||||
= FilteredInGridEntries?.OfType<ILibraryBookEntry>().Count()
|
= FilteredInGridEntries?.OfType<ILibraryBookEntry>().Count()
|
||||||
?? SOURCE.OfType<ILibraryBookEntry>().Count();
|
?? SOURCE.OfType<ILibraryBookEntry>().Count();
|
||||||
|
|
||||||
VisibleCountChanged?.Invoke(this, count);
|
VisibleCountChanged?.Invoke(this, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -1,8 +1,4 @@
|
|||||||
using Avalonia.Controls;
|
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace LibationAvalonia.ViewModels
|
namespace LibationAvalonia.ViewModels
|
||||||
{
|
{
|
||||||
|
|||||||
@ -35,7 +35,7 @@
|
|||||||
Name="button"
|
Name="button"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
VerticalAlignment="Stretch"
|
VerticalAlignment="Stretch"
|
||||||
IsEnabled="{Binding IsButtonEnabled}" Padding="0" Click="Button_Click" >
|
IsEnabled="{CompiledBinding IsButtonEnabled}" Padding="0" Click="Button_Click" >
|
||||||
<Panel>
|
<Panel>
|
||||||
|
|
||||||
<Panel
|
<Panel
|
||||||
|
|||||||
@ -3,6 +3,7 @@ using Avalonia.Controls;
|
|||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using DataLayer;
|
using DataLayer;
|
||||||
using LibationAvalonia.ViewModels;
|
using LibationAvalonia.ViewModels;
|
||||||
|
using LibationUiBase.GridView;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace LibationAvalonia.Views
|
namespace LibationAvalonia.Views
|
||||||
@ -45,6 +46,16 @@ namespace LibationAvalonia.Views
|
|||||||
PdfStatus = null;
|
PdfStatus = null;
|
||||||
IsSeries = true;
|
IsSeries = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DataContextChanged += LiberateStatusButton_DataContextChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LiberateStatusButton_DataContextChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
//Force book status recheck when an entry is scrolled into view.
|
||||||
|
//This will force a recheck for a paprtially downloaded file.
|
||||||
|
var status = DataContext as ILibraryBookEntry;
|
||||||
|
status?.Liberate.Invalidate(nameof(status.Liberate.BookStatus));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Button_Click(object sender, RoutedEventArgs e) => Click?.Invoke(this, EventArgs.Empty);
|
private void Button_Click(object sender, RoutedEventArgs e) => Click?.Invoke(this, EventArgs.Empty);
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
using ApplicationServices;
|
using ApplicationServices;
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@ -19,7 +17,7 @@ namespace LibationAvalonia.Views
|
|||||||
private void setBackupCounts(object _, object __)
|
private void setBackupCounts(object _, object __)
|
||||||
{
|
{
|
||||||
if (updateCountsTask?.IsCompleted is not false)
|
if (updateCountsTask?.IsCompleted is not false)
|
||||||
updateCountsTask = Dispatcher.UIThread.InvokeAsync(() => _viewModel.LibraryStats = LibraryCommands.GetCounts());
|
updateCountsTask = Dispatcher.UIThread.InvokeAsync(() => _viewModel.LibraryStats = LibraryCommands.GetCounts());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,11 @@
|
|||||||
using ApplicationServices;
|
using ApplicationServices;
|
||||||
using Avalonia.Controls;
|
|
||||||
using Avalonia.Platform.Storage;
|
using Avalonia.Platform.Storage;
|
||||||
using FileManager;
|
using FileManager;
|
||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace LibationAvalonia.Views
|
namespace LibationAvalonia.Views
|
||||||
{
|
{
|
||||||
//DONE
|
|
||||||
public partial class MainWindow
|
public partial class MainWindow
|
||||||
{
|
{
|
||||||
private void Configure_Export() { }
|
private void Configure_Export() { }
|
||||||
|
|||||||
@ -1,18 +1,16 @@
|
|||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace LibationAvalonia.Views
|
namespace LibationAvalonia.Views
|
||||||
{
|
{
|
||||||
//DONE
|
|
||||||
public partial class MainWindow
|
public partial class MainWindow
|
||||||
{
|
{
|
||||||
protected void Configure_Filter() { }
|
protected void Configure_Filter() { }
|
||||||
|
|
||||||
public async void filterHelpBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public async void filterHelpBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
=> await (new LibationAvalonia.Dialogs.SearchSyntaxDialog()).ShowDialog(this);
|
=> await (new LibationAvalonia.Dialogs.SearchSyntaxDialog()).ShowDialog(this);
|
||||||
|
|
||||||
public async void filterSearchTb_KeyPress(object sender, KeyEventArgs e)
|
public async void filterSearchTb_KeyPress(object sender, KeyEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.Key == Key.Return)
|
if (e.Key == Key.Return)
|
||||||
|
|||||||
@ -5,7 +5,6 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace LibationAvalonia.Views
|
namespace LibationAvalonia.Views
|
||||||
{
|
{
|
||||||
//DONE
|
|
||||||
public partial class MainWindow
|
public partial class MainWindow
|
||||||
{
|
{
|
||||||
private void Configure_Liberate() { }
|
private void Configure_Liberate() { }
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
using LibationUiBase;
|
using LibationUiBase;
|
||||||
using System;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace LibationAvalonia.Views
|
namespace LibationAvalonia.Views
|
||||||
{
|
{
|
||||||
@ -13,11 +11,11 @@ namespace LibationAvalonia.Views
|
|||||||
using var ms1 = new MemoryStream();
|
using var ms1 = new MemoryStream();
|
||||||
App.OpenAsset("img-coverart-prod-unavailable_80x80.jpg").CopyTo(ms1);
|
App.OpenAsset("img-coverart-prod-unavailable_80x80.jpg").CopyTo(ms1);
|
||||||
PictureStorage.SetDefaultImage(PictureSize._80x80, ms1.ToArray());
|
PictureStorage.SetDefaultImage(PictureSize._80x80, ms1.ToArray());
|
||||||
|
|
||||||
using var ms2 = new MemoryStream();
|
using var ms2 = new MemoryStream();
|
||||||
App.OpenAsset("img-coverart-prod-unavailable_300x300.jpg").CopyTo(ms2);
|
App.OpenAsset("img-coverart-prod-unavailable_300x300.jpg").CopyTo(ms2);
|
||||||
PictureStorage.SetDefaultImage(PictureSize._300x300, ms2.ToArray());
|
PictureStorage.SetDefaultImage(PictureSize._300x300, ms2.ToArray());
|
||||||
|
|
||||||
using var ms3 = new MemoryStream();
|
using var ms3 = new MemoryStream();
|
||||||
App.OpenAsset("img-coverart-prod-unavailable_500x500.jpg").CopyTo(ms3);
|
App.OpenAsset("img-coverart-prod-unavailable_500x500.jpg").CopyTo(ms3);
|
||||||
PictureStorage.SetDefaultImage(PictureSize._500x500, ms3.ToArray());
|
PictureStorage.SetDefaultImage(PictureSize._500x500, ms3.ToArray());
|
||||||
|
|||||||
@ -3,7 +3,6 @@ using Dinah.Core;
|
|||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
using LibationUiBase.GridView;
|
using LibationUiBase.GridView;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace LibationAvalonia.Views
|
namespace LibationAvalonia.Views
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
using System;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Avalonia.Data;
|
||||||
|
|
||||||
namespace LibationAvalonia.Views
|
namespace LibationAvalonia.Views
|
||||||
{
|
{
|
||||||
//DONE
|
|
||||||
public partial class MainWindow
|
public partial class MainWindow
|
||||||
{
|
{
|
||||||
private void Configure_QuickFilters()
|
private void Configure_QuickFilters()
|
||||||
@ -13,24 +13,54 @@ namespace LibationAvalonia.Views
|
|||||||
_viewModel.FirstFilterIsDefault = QuickFilters.UseDefault;
|
_viewModel.FirstFilterIsDefault = QuickFilters.UseDefault;
|
||||||
Load += updateFiltersMenu;
|
Load += updateFiltersMenu;
|
||||||
QuickFilters.Updated += updateFiltersMenu;
|
QuickFilters.Updated += updateFiltersMenu;
|
||||||
|
|
||||||
|
//We need to be able to dynamically add and remove menu items from the Quick Filters menu.
|
||||||
|
//To do that, we need quick filter's menu items source to be writable, which we can only
|
||||||
|
//achieve by creating the list ourselves (instead of allowing Avalonia to create it from the xaml)
|
||||||
|
|
||||||
|
var startWithFilterMenuItem = new MenuItem
|
||||||
|
{
|
||||||
|
Header = "Start Libation with 1st filter _Default",
|
||||||
|
Icon = new CheckBox
|
||||||
|
{
|
||||||
|
BorderThickness = new Thickness(0),
|
||||||
|
IsHitTestVisible = false,
|
||||||
|
[!CheckBox.IsCheckedProperty] = new Binding(nameof(_viewModel.FirstFilterIsDefault))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var editFiltersMenuItem = new MenuItem { Header = "_Edit quick filters..." };
|
||||||
|
|
||||||
|
startWithFilterMenuItem.Click += firstFilterIsDefaultToolStripMenuItem_Click;
|
||||||
|
editFiltersMenuItem.Click += editQuickFiltersToolStripMenuItem_Click;
|
||||||
|
|
||||||
|
_viewModel.QuickFilterMenuItems.Add(startWithFilterMenuItem);
|
||||||
|
_viewModel.QuickFilterMenuItems.Add(editFiltersMenuItem);
|
||||||
|
_viewModel.QuickFilterMenuItems.Add(new Separator());
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void QuickFiltersMenuItem_KeyDown(object sender, Avalonia.Input.KeyEventArgs e)
|
||||||
|
{
|
||||||
|
int keyNum = (int)e.Key - 34;
|
||||||
|
|
||||||
|
if (keyNum <=9 && keyNum >= 1)
|
||||||
|
{
|
||||||
|
var menuItem = _viewModel.QuickFilterMenuItems
|
||||||
|
.OfType<MenuItem>()
|
||||||
|
.FirstOrDefault(i => i.Header is string h && h.StartsWith($"_{keyNum}"));
|
||||||
|
|
||||||
|
if (menuItem is not null)
|
||||||
|
{
|
||||||
|
await performFilter(menuItem.Tag as string);
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private object quickFilterTag { get; } = new();
|
|
||||||
private void updateFiltersMenu(object _ = null, object __ = null)
|
private void updateFiltersMenu(object _ = null, object __ = null)
|
||||||
{
|
{
|
||||||
var allItems = quickFiltersToolStripMenuItem
|
//Clear all filters
|
||||||
.Items
|
_viewModel.QuickFilterMenuItems.RemoveAll(_viewModel.QuickFilterMenuItems.Where(i => i.Tag is string).ToList());
|
||||||
.Cast<Control>()
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
var toRemove = allItems
|
|
||||||
.OfType<MenuItem>()
|
|
||||||
.Where(mi => mi.Tag == quickFilterTag)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
allItems = allItems
|
|
||||||
.Except(toRemove)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
// re-populate
|
// re-populate
|
||||||
var index = 0;
|
var index = 0;
|
||||||
@ -38,27 +68,23 @@ namespace LibationAvalonia.Views
|
|||||||
{
|
{
|
||||||
var quickFilterMenuItem = new MenuItem
|
var quickFilterMenuItem = new MenuItem
|
||||||
{
|
{
|
||||||
Tag = quickFilterTag,
|
Tag = filter,
|
||||||
Header = $"_{++index}: {filter}"
|
Header = $"_{++index}: {filter}"
|
||||||
};
|
};
|
||||||
quickFilterMenuItem.Click += async (_, __) => await performFilter(filter);
|
quickFilterMenuItem.Click += async (_, __) => await performFilter(filter);
|
||||||
allItems.Add(quickFilterMenuItem);
|
_viewModel.QuickFilterMenuItems.Add(quickFilterMenuItem);
|
||||||
}
|
}
|
||||||
quickFiltersToolStripMenuItem.Items = allItems;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void firstFilterIsDefaultToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
private void firstFilterIsDefaultToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (sender is MenuItem mi && mi.Icon is CheckBox checkBox)
|
_viewModel.FirstFilterIsDefault = !_viewModel.FirstFilterIsDefault;
|
||||||
{
|
|
||||||
checkBox.IsChecked = !(checkBox.IsChecked ?? false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addQuickFilterBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
private void addQuickFilterBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
=> QuickFilters.Add(_viewModel.FilterString);
|
=> QuickFilters.Add(_viewModel.FilterString);
|
||||||
|
|
||||||
public async void editQuickFiltersToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
private async void editQuickFiltersToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
=> await new Dialogs.EditQuickFilters().ShowDialog(this);
|
=> await new Dialogs.EditQuickFilters().ShowDialog(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,12 @@
|
|||||||
using AudibleUtilities;
|
using AudibleUtilities;
|
||||||
using LibationAvalonia.Dialogs;
|
using LibationAvalonia.Dialogs;
|
||||||
using System;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace LibationAvalonia.Views
|
namespace LibationAvalonia.Views
|
||||||
{
|
{
|
||||||
//DONE
|
|
||||||
public partial class MainWindow
|
public partial class MainWindow
|
||||||
{
|
{
|
||||||
private void Configure_RemoveBooks()
|
private void Configure_RemoveBooks()
|
||||||
{
|
{
|
||||||
if (Avalonia.Controls.Design.IsDesignMode)
|
if (Avalonia.Controls.Design.IsDesignMode)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -43,7 +43,7 @@ namespace LibationAvalonia.Views
|
|||||||
};
|
};
|
||||||
|
|
||||||
_viewModel.AutoScanChecked = Configuration.Instance.AutoScan;
|
_viewModel.AutoScanChecked = Configuration.Instance.AutoScan;
|
||||||
|
|
||||||
// if enabled: begin on load
|
// if enabled: begin on load
|
||||||
Opened += startAutoScan;
|
Opened += startAutoScan;
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
using ApplicationServices;
|
using ApplicationServices;
|
||||||
using AudibleUtilities;
|
using AudibleUtilities;
|
||||||
using Avalonia.Controls;
|
|
||||||
using LibationAvalonia.Dialogs;
|
using LibationAvalonia.Dialogs;
|
||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
using System;
|
using System;
|
||||||
@ -10,7 +9,6 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace LibationAvalonia.Views
|
namespace LibationAvalonia.Views
|
||||||
{
|
{
|
||||||
//DONE
|
|
||||||
public partial class MainWindow
|
public partial class MainWindow
|
||||||
{
|
{
|
||||||
private void Configure_ScanManual()
|
private void Configure_ScanManual()
|
||||||
@ -69,7 +67,7 @@ namespace LibationAvalonia.Views
|
|||||||
if (Configuration.Instance.ShowImportedStats && newAdded > 0)
|
if (Configuration.Instance.ShowImportedStats && newAdded > 0)
|
||||||
await MessageBox.Show($"Total processed: {totalProcessed}\r\nNew: {newAdded}");
|
await MessageBox.Show($"Total processed: {totalProcessed}\r\nNew: {newAdded}");
|
||||||
}
|
}
|
||||||
catch(OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
Serilog.Log.Information("Audible login attempt cancelled by user");
|
Serilog.Log.Information("Audible login attempt cancelled by user");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,8 @@
|
|||||||
using ApplicationServices;
|
using ApplicationServices;
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace LibationAvalonia.Views
|
namespace LibationAvalonia.Views
|
||||||
{
|
{
|
||||||
//DONE
|
|
||||||
public partial class MainWindow
|
public partial class MainWindow
|
||||||
{
|
{
|
||||||
private void Configure_ScanNotification()
|
private void Configure_ScanNotification()
|
||||||
@ -18,7 +16,7 @@ namespace LibationAvalonia.Views
|
|||||||
_viewModel.NumAccountsScanning = accountsLength;
|
_viewModel.NumAccountsScanning = accountsLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LibraryCommands_ScanEnd(object sender, EventArgs e)
|
private void LibraryCommands_ScanEnd(object sender, int newCount)
|
||||||
{
|
{
|
||||||
_viewModel.NumAccountsScanning = 0;
|
_viewModel.NumAccountsScanning = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,6 @@ using System;
|
|||||||
|
|
||||||
namespace LibationAvalonia.Views
|
namespace LibationAvalonia.Views
|
||||||
{
|
{
|
||||||
//DONE
|
|
||||||
public partial class MainWindow
|
public partial class MainWindow
|
||||||
{
|
{
|
||||||
private void Configure_Settings() { }
|
private void Configure_Settings() { }
|
||||||
@ -17,13 +16,16 @@ namespace LibationAvalonia.Views
|
|||||||
public async void aboutToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public async void aboutToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
=> await MessageBox.Show($"Libation {AppScaffolding.LibationScaffolding.Variety}{Environment.NewLine}Version {AppScaffolding.LibationScaffolding.BuildVersion}", $"Libation v{AppScaffolding.LibationScaffolding.BuildVersion}");
|
=> await MessageBox.Show($"Libation {AppScaffolding.LibationScaffolding.Variety}{Environment.NewLine}Version {AppScaffolding.LibationScaffolding.BuildVersion}", $"Libation v{AppScaffolding.LibationScaffolding.BuildVersion}");
|
||||||
|
|
||||||
|
public async void tourToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
=> await new Walkthrough(this).RunAsync();
|
||||||
|
|
||||||
public void launchHangoverToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public void launchHangoverToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
System.Diagnostics.Process.Start("Hangover" + (Configuration.IsWindows ? ".exe" : ""));
|
System.Diagnostics.Process.Start("Hangover" + (Configuration.IsWindows ? ".exe" : ""));
|
||||||
}
|
}
|
||||||
catch(Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Serilog.Log.Logger.Error(ex, "Failed to launch Hangover");
|
Serilog.Log.Logger.Error(ex, "Failed to launch Hangover");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,8 +5,8 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace LibationAvalonia.Views
|
namespace LibationAvalonia.Views
|
||||||
{
|
{
|
||||||
public partial class MainWindow
|
public partial class MainWindow
|
||||||
{
|
{
|
||||||
private void Configure_Upgrade()
|
private void Configure_Upgrade()
|
||||||
{
|
{
|
||||||
setProgressVisible(false);
|
setProgressVisible(false);
|
||||||
|
|||||||
@ -1,15 +1,11 @@
|
|||||||
using ApplicationServices;
|
using ApplicationServices;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using DataLayer;
|
using DataLayer;
|
||||||
using Dinah.Core;
|
|
||||||
using LibationFileManager;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace LibationAvalonia.Views
|
namespace LibationAvalonia.Views
|
||||||
{
|
{
|
||||||
//DONE
|
|
||||||
public partial class MainWindow
|
public partial class MainWindow
|
||||||
{
|
{
|
||||||
private void Configure_VisibleBooks()
|
private void Configure_VisibleBooks()
|
||||||
@ -52,15 +48,15 @@ namespace LibationAvalonia.Views
|
|||||||
var confirmationResult = await MessageBox.ShowConfirmationDialog(
|
var confirmationResult = await MessageBox.ShowConfirmationDialog(
|
||||||
this,
|
this,
|
||||||
visibleLibraryBooks,
|
visibleLibraryBooks,
|
||||||
// do not use `$` string interpolation. See impl.
|
// do not use `$` string interpolation. See impl.
|
||||||
"Are you sure you want to replace tags in {0}?",
|
"Are you sure you want to replace tags in {0}?",
|
||||||
"Replace tags?");
|
"Replace tags?");
|
||||||
|
|
||||||
if (confirmationResult != DialogResult.Yes)
|
if (confirmationResult != DialogResult.Yes)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
visibleLibraryBooks.UpdateTags(dialog.NewTags);
|
visibleLibraryBooks.UpdateTags(dialog.NewTags);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void setBookDownloadedManualToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs args)
|
public async void setBookDownloadedManualToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs args)
|
||||||
{
|
{
|
||||||
@ -74,73 +70,73 @@ namespace LibationAvalonia.Views
|
|||||||
var confirmationResult = await MessageBox.ShowConfirmationDialog(
|
var confirmationResult = await MessageBox.ShowConfirmationDialog(
|
||||||
this,
|
this,
|
||||||
visibleLibraryBooks,
|
visibleLibraryBooks,
|
||||||
// do not use `$` string interpolation. See impl.
|
// do not use `$` string interpolation. See impl.
|
||||||
"Are you sure you want to replace book downloaded status in {0}?",
|
"Are you sure you want to replace book downloaded status in {0}?",
|
||||||
"Replace downloaded status?");
|
"Replace downloaded status?");
|
||||||
|
|
||||||
if (confirmationResult != DialogResult.Yes)
|
if (confirmationResult != DialogResult.Yes)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
visibleLibraryBooks.UpdateBookStatus(dialog.BookLiberatedStatus);
|
visibleLibraryBooks.UpdateBookStatus(dialog.BookLiberatedStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void setPdfDownloadedManualToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs args)
|
public async void setPdfDownloadedManualToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs args)
|
||||||
{
|
{
|
||||||
var dialog = new Dialogs.LiberatedStatusBatchManualDialog(isPdf: true);
|
var dialog = new Dialogs.LiberatedStatusBatchManualDialog(isPdf: true);
|
||||||
var result = await dialog.ShowDialog<DialogResult>(this);
|
var result = await dialog.ShowDialog<DialogResult>(this);
|
||||||
if (result != DialogResult.OK)
|
if (result != DialogResult.OK)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var visibleLibraryBooks = _viewModel.ProductsDisplay.GetVisibleBookEntries();
|
var visibleLibraryBooks = _viewModel.ProductsDisplay.GetVisibleBookEntries();
|
||||||
|
|
||||||
var confirmationResult = await MessageBox.ShowConfirmationDialog(
|
var confirmationResult = await MessageBox.ShowConfirmationDialog(
|
||||||
this,
|
this,
|
||||||
visibleLibraryBooks,
|
visibleLibraryBooks,
|
||||||
// do not use `$` string interpolation. See impl.
|
// do not use `$` string interpolation. See impl.
|
||||||
"Are you sure you want to replace PDF downloaded status in {0}?",
|
"Are you sure you want to replace PDF downloaded status in {0}?",
|
||||||
"Replace downloaded status?");
|
"Replace downloaded status?");
|
||||||
|
|
||||||
if (confirmationResult != DialogResult.Yes)
|
if (confirmationResult != DialogResult.Yes)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
visibleLibraryBooks.UpdatePdfStatus(dialog.BookLiberatedStatus);
|
visibleLibraryBooks.UpdatePdfStatus(dialog.BookLiberatedStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void setDownloadedAutoToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs args)
|
public async void setDownloadedAutoToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs args)
|
||||||
{
|
{
|
||||||
var dialog = new Dialogs.LiberatedStatusBatchAutoDialog();
|
var dialog = new Dialogs.LiberatedStatusBatchAutoDialog();
|
||||||
var result = await dialog.ShowDialog<DialogResult>(this);
|
var result = await dialog.ShowDialog<DialogResult>(this);
|
||||||
if (result != DialogResult.OK)
|
if (result != DialogResult.OK)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var bulkSetStatus = new BulkSetDownloadStatus(_viewModel.ProductsDisplay.GetVisibleBookEntries(), dialog.SetDownloaded, dialog.SetNotDownloaded);
|
var bulkSetStatus = new BulkSetDownloadStatus(_viewModel.ProductsDisplay.GetVisibleBookEntries(), dialog.SetDownloaded, dialog.SetNotDownloaded);
|
||||||
var count = await Task.Run(bulkSetStatus.Discover);
|
var count = await Task.Run(bulkSetStatus.Discover);
|
||||||
|
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var confirmationResult = await MessageBox.Show(
|
var confirmationResult = await MessageBox.Show(
|
||||||
bulkSetStatus.AggregateMessage,
|
bulkSetStatus.AggregateMessage,
|
||||||
"Replace downloaded status?",
|
"Replace downloaded status?",
|
||||||
MessageBoxButtons.YesNo,
|
MessageBoxButtons.YesNo,
|
||||||
MessageBoxIcon.Question,
|
MessageBoxIcon.Question,
|
||||||
MessageBoxDefaultButton.Button1);
|
MessageBoxDefaultButton.Button1);
|
||||||
|
|
||||||
if (confirmationResult != DialogResult.Yes)
|
if (confirmationResult != DialogResult.Yes)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bulkSetStatus.Execute();
|
bulkSetStatus.Execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void removeToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs args)
|
public async void removeToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs args)
|
||||||
{
|
{
|
||||||
var visibleLibraryBooks = _viewModel.ProductsDisplay.GetVisibleBookEntries();
|
var visibleLibraryBooks = _viewModel.ProductsDisplay.GetVisibleBookEntries();
|
||||||
|
|
||||||
var confirmationResult = await MessageBox.ShowConfirmationDialog(
|
var confirmationResult = await MessageBox.ShowConfirmationDialog(
|
||||||
this,
|
this,
|
||||||
visibleLibraryBooks,
|
visibleLibraryBooks,
|
||||||
// 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?",
|
||||||
MessageBoxDefaultButton.Button2);
|
MessageBoxDefaultButton.Button2);
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
||||||
mc:Ignorable="d" d:DesignWidth="1850" d:DesignHeight="700"
|
mc:Ignorable="d" d:DesignWidth="1850" d:DesignHeight="700"
|
||||||
x:Class="LibationAvalonia.Views.MainWindow"
|
x:Class="LibationAvalonia.Views.MainWindow"
|
||||||
Title="Libation"
|
Title="Libation: Liberate your Library"
|
||||||
Name="Form1"
|
Name="Form1"
|
||||||
Icon="/Assets/libation.ico">
|
Icon="/Assets/libation.ico">
|
||||||
|
|
||||||
@ -19,6 +19,7 @@
|
|||||||
<!-- Menu Strip -->
|
<!-- Menu Strip -->
|
||||||
<Menu Grid.Column="0" VerticalAlignment="Top">
|
<Menu Grid.Column="0" VerticalAlignment="Top">
|
||||||
<!-- Decrease height of menu strop -->
|
<!-- Decrease height of menu strop -->
|
||||||
|
|
||||||
<Menu.Styles>
|
<Menu.Styles>
|
||||||
<Style Selector="ItemsPresenter#PART_ItemsPresenter">
|
<Style Selector="ItemsPresenter#PART_ItemsPresenter">
|
||||||
<Setter Property="Height" Value="25"/>
|
<Setter Property="Height" Value="25"/>
|
||||||
@ -27,7 +28,7 @@
|
|||||||
|
|
||||||
<!-- Import Menu -->
|
<!-- Import Menu -->
|
||||||
|
|
||||||
<MenuItem Header="_Import">
|
<MenuItem Name="importToolStripMenuItem" Header="_Import">
|
||||||
<!-- Remove height style property for menu item -->
|
<!-- Remove height style property for menu item -->
|
||||||
<MenuItem.Styles>
|
<MenuItem.Styles>
|
||||||
<Style Selector="ItemsPresenter#PART_ItemsPresenter">
|
<Style Selector="ItemsPresenter#PART_ItemsPresenter">
|
||||||
@ -42,8 +43,8 @@
|
|||||||
<MenuItem IsVisible="{Binding !AnyAccounts}" Click="noAccountsYetAddAccountToolStripMenuItem_Click" Header="No accounts yet. A_dd Account..." />
|
<MenuItem IsVisible="{Binding !AnyAccounts}" Click="noAccountsYetAddAccountToolStripMenuItem_Click" Header="No accounts yet. A_dd Account..." />
|
||||||
|
|
||||||
<!-- Scan Library -->
|
<!-- Scan Library -->
|
||||||
<MenuItem IsVisible="{Binding OneAccount}" IsEnabled="{Binding !ActivelyScanning}" Click="scanLibraryToolStripMenuItem_Click" Header="Scan _Library" />
|
<MenuItem IsVisible="{Binding OneAccount}" IsEnabled="{Binding !ActivelyScanning}" Name="scanLibraryToolStripMenuItem" Click="scanLibraryToolStripMenuItem_Click" Header="Scan _Library" />
|
||||||
<MenuItem IsVisible="{Binding MultipleAccounts}" IsEnabled="{Binding !ActivelyScanning}" Click="scanLibraryOfAllAccountsToolStripMenuItem_Click" Header="Scan Library of _All Accounts" />
|
<MenuItem IsVisible="{Binding MultipleAccounts}" IsEnabled="{Binding !ActivelyScanning}" Name="scanLibraryOfAllAccountsToolStripMenuItem" Click="scanLibraryOfAllAccountsToolStripMenuItem_Click" Header="Scan Library of _All Accounts" />
|
||||||
<MenuItem IsVisible="{Binding MultipleAccounts}" IsEnabled="{Binding !ActivelyScanning}" Click="scanLibraryOfSomeAccountsToolStripMenuItem_Click" Header="Scan Library of _Some Accounts" />
|
<MenuItem IsVisible="{Binding MultipleAccounts}" IsEnabled="{Binding !ActivelyScanning}" Click="scanLibraryOfSomeAccountsToolStripMenuItem_Click" Header="Scan Library of _Some Accounts" />
|
||||||
<Separator />
|
<Separator />
|
||||||
|
|
||||||
@ -86,20 +87,13 @@
|
|||||||
|
|
||||||
<!-- Quick Filters Menu -->
|
<!-- Quick Filters Menu -->
|
||||||
|
|
||||||
<MenuItem Name="quickFiltersToolStripMenuItem" Header="Quick _Filters">
|
<MenuItem Name="quickFiltersToolStripMenuItem" Header="Quick _Filters" ItemsSource="{Binding QuickFilterMenuItems}" KeyDown="QuickFiltersMenuItem_KeyDown">
|
||||||
<!-- Remove height style property for menu item -->
|
<!-- Remove height style property for menu item -->
|
||||||
<MenuItem.Styles>
|
<MenuItem.Styles>
|
||||||
<Style Selector="ItemsPresenter#PART_ItemsPresenter">
|
<Style Selector="ItemsPresenter#PART_ItemsPresenter">
|
||||||
<Setter Property="Height" Value="NaN"/>
|
<Setter Property="Height" Value="NaN"/>
|
||||||
</Style>
|
</Style>
|
||||||
</MenuItem.Styles>
|
</MenuItem.Styles>
|
||||||
<MenuItem Click="firstFilterIsDefaultToolStripMenuItem_Click" Header="Start Libation with 1st filter _Default">
|
|
||||||
<MenuItem.Icon>
|
|
||||||
<CheckBox BorderThickness="0" IsChecked="{Binding FirstFilterIsDefault, Mode=TwoWay}" IsHitTestVisible="False" />
|
|
||||||
</MenuItem.Icon>
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem Click="editQuickFiltersToolStripMenuItem_Click" Header="_Edit quick filters..." />
|
|
||||||
<Separator />
|
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|
||||||
<!-- Visible Books Menu -->
|
<!-- Visible Books Menu -->
|
||||||
@ -121,19 +115,20 @@
|
|||||||
|
|
||||||
<!-- Settings Menu -->
|
<!-- Settings Menu -->
|
||||||
|
|
||||||
<MenuItem Header="_Settings">
|
<MenuItem Header="_Settings" Name="settingsToolStripMenuItem">
|
||||||
<!-- Remove height style property for menu item -->
|
<!-- Remove height style property for menu item -->
|
||||||
<MenuItem.Styles>
|
<MenuItem.Styles>
|
||||||
<Style Selector="ItemsPresenter#PART_ItemsPresenter">
|
<Style Selector="ItemsPresenter#PART_ItemsPresenter">
|
||||||
<Setter Property="Height" Value="NaN"/>
|
<Setter Property="Height" Value="NaN"/>
|
||||||
</Style>
|
</Style>
|
||||||
</MenuItem.Styles>
|
</MenuItem.Styles>
|
||||||
<MenuItem Click="accountsToolStripMenuItem_Click" Header="_Accounts..." />
|
<MenuItem Name="accountsToolStripMenuItem" Click="accountsToolStripMenuItem_Click" Header="_Accounts..." />
|
||||||
<MenuItem Click="basicSettingsToolStripMenuItem_Click" Header="_Settings..." />
|
<MenuItem Name="basicSettingsToolStripMenuItem" Click="basicSettingsToolStripMenuItem_Click" Header="_Settings..." />
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem Click="openTrashBinToolStripMenuItem_Click" Header="Trash Bin" />
|
<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="tourToolStripMenuItem_Click" Header="Take a Guided _Tour of Libation" />
|
||||||
<MenuItem Click="aboutToolStripMenuItem_Click" Header="A_bout..." />
|
<MenuItem Click="aboutToolStripMenuItem_Click" Header="A_bout..." />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</Menu>
|
</Menu>
|
||||||
@ -160,8 +155,8 @@
|
|||||||
</Grid.Styles>
|
</Grid.Styles>
|
||||||
|
|
||||||
<StackPanel Grid.Column="0" Orientation="Horizontal">
|
<StackPanel Grid.Column="0" Orientation="Horizontal">
|
||||||
<Button Margin="0" Click="filterHelpBtn_Click" Content="?"/>
|
<Button Name="filterHelpBtn" Margin="0" Click="filterHelpBtn_Click" Content="?"/>
|
||||||
<Button Click="addQuickFilterBtn_Click" Content="Add To Quick Filters"/>
|
<Button Name="addQuickFilterBtn" Click="addQuickFilterBtn_Click" Content="Add To Quick Filters"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<StackPanel Grid.Column="1" Orientation="Horizontal">
|
<StackPanel Grid.Column="1" Orientation="Horizontal">
|
||||||
@ -169,10 +164,10 @@
|
|||||||
<Button IsVisible="{Binding RemoveButtonsVisible}" Click="doneRemovingBtn_Click" Content="Done Removing Books"/>
|
<Button IsVisible="{Binding RemoveButtonsVisible}" Click="doneRemovingBtn_Click" Content="Done Removing Books"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<TextBox Grid.Column="1" Margin="10,0,0,0" IsVisible="{Binding !RemoveButtonsVisible}" Text="{Binding FilterString, Mode=TwoWay}" KeyDown="filterSearchTb_KeyPress" />
|
<TextBox Grid.Column="1" Margin="10,0,0,0" Name="filterSearchTb" IsVisible="{Binding !RemoveButtonsVisible}" Text="{Binding FilterString, Mode=TwoWay}" KeyDown="filterSearchTb_KeyPress" />
|
||||||
|
|
||||||
<StackPanel Grid.Column="2" Height="30" Orientation="Horizontal">
|
<StackPanel Grid.Column="2" Height="30" Orientation="Horizontal">
|
||||||
<Button Click="filterBtn_Click" VerticalAlignment="Stretch" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Content="Filter"/>
|
<Button Name="filterBtn" Click="filterBtn_Click" VerticalAlignment="Stretch" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Content="Filter"/>
|
||||||
<Button Padding="2,6,2,6" VerticalAlignment="Stretch" Click="ToggleQueueHideBtn_Click">
|
<Button Padding="2,6,2,6" VerticalAlignment="Stretch" Click="ToggleQueueHideBtn_Click">
|
||||||
<Path Stretch="Uniform" Fill="{DynamicResource IconFill}" Data="{StaticResource LeftArrows}">
|
<Path Stretch="Uniform" Fill="{DynamicResource IconFill}" Data="{StaticResource LeftArrows}">
|
||||||
<Path.RenderTransform>
|
<Path.RenderTransform>
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using ApplicationServices;
|
using ApplicationServices;
|
||||||
using Avalonia.ReactiveUI;
|
using Avalonia.ReactiveUI;
|
||||||
using DataLayer;
|
using DataLayer;
|
||||||
using LibationAvalonia.ViewModels;
|
using LibationAvalonia.ViewModels;
|
||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace LibationAvalonia.Views
|
namespace LibationAvalonia.Views
|
||||||
{
|
{
|
||||||
@ -48,6 +48,23 @@ namespace LibationAvalonia.Views
|
|||||||
Closing += (_, _) => this.SaveSizeAndLocation(Configuration.Instance);
|
Closing += (_, _) => this.SaveSizeAndLocation(Configuration.Instance);
|
||||||
}
|
}
|
||||||
Closing += MainWindow_Closing;
|
Closing += MainWindow_Closing;
|
||||||
|
|
||||||
|
Opened += MainWindow_Opened;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void MainWindow_Opened(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (Configuration.Instance.FirstLaunch)
|
||||||
|
{
|
||||||
|
var result = await MessageBox.Show(this, "Would you like a guided tour to get started?", "Libation Walkthrough", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1);
|
||||||
|
|
||||||
|
if (result is DialogResult.Yes)
|
||||||
|
{
|
||||||
|
await new Walkthrough(this).RunAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
Configuration.Instance.FirstLaunch = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
|
private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
|
||||||
|
|||||||
@ -2,20 +2,22 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:vm="clr-namespace:LibationAvalonia.ViewModels"
|
||||||
|
x:DataType="vm:ProcessBookViewModel"
|
||||||
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="87" MaxHeight="87" MinHeight="87" MinWidth="300"
|
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="87" MaxHeight="87" MinHeight="87" MinWidth="300"
|
||||||
x:Class="LibationAvalonia.Views.ProcessBookControl" Background="{Binding BackgroundColor}">
|
x:Class="LibationAvalonia.Views.ProcessBookControl" Background="{CompiledBinding BackgroundColor}">
|
||||||
|
|
||||||
<Border BorderBrush="{DynamicResource SystemControlForegroundBaseMediumBrush}" BorderThickness="0,0,0,1">
|
<Border BorderBrush="{DynamicResource SystemControlForegroundBaseMediumBrush}" BorderThickness="0,0,0,1">
|
||||||
<Grid ColumnDefinitions="Auto,*,Auto">
|
<Grid ColumnDefinitions="Auto,*,Auto">
|
||||||
|
|
||||||
<Panel Grid.Column="0" Margin="3" Width="80" Height="80" HorizontalAlignment="Left">
|
<Panel Grid.Column="0" Margin="3" Width="80" Height="80" HorizontalAlignment="Left">
|
||||||
<Image Width="80" Height="80" Source="{Binding Cover}" Stretch="Uniform" />
|
<Image Width="80" Height="80" Source="{CompiledBinding Cover}" Stretch="Uniform" />
|
||||||
</Panel>
|
</Panel>
|
||||||
<Grid Margin="0,3,0,3" Grid.Column="1" ColumnDefinitions="*" RowDefinitions="*,16">
|
<Grid Margin="0,3,0,3" Grid.Column="1" ColumnDefinitions="*" RowDefinitions="*,16">
|
||||||
<StackPanel Grid.Column="0" Grid.Row="0" Orientation="Vertical">
|
<StackPanel Grid.Column="0" Grid.Row="0" Orientation="Vertical">
|
||||||
<TextBlock ClipToBounds="True" TextWrapping="Wrap" FontSize="11" Text="{Binding Title}" />
|
<TextBlock ClipToBounds="True" TextWrapping="Wrap" FontSize="11" Text="{CompiledBinding Title}" />
|
||||||
<TextBlock FontSize="10" TextWrapping="NoWrap" Text="{Binding Author}" />
|
<TextBlock FontSize="10" TextWrapping="NoWrap" Text="{CompiledBinding Author}" />
|
||||||
<TextBlock FontSize="10" TextWrapping="NoWrap" Text="{Binding Narrator}" />
|
<TextBlock FontSize="10" TextWrapping="NoWrap" Text="{CompiledBinding Narrator}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<Panel Grid.Column="0" Grid.Row="1">
|
<Panel Grid.Column="0" Grid.Row="1">
|
||||||
<Panel.Styles>
|
<Panel.Styles>
|
||||||
@ -23,8 +25,8 @@
|
|||||||
<Setter Property="MinWidth" Value="20" />
|
<Setter Property="MinWidth" Value="20" />
|
||||||
</Style>
|
</Style>
|
||||||
</Panel.Styles>
|
</Panel.Styles>
|
||||||
<ProgressBar IsVisible="{Binding IsDownloading}" Value="{Binding Progress}" ShowProgressText="True" FontSize="12" />
|
<ProgressBar IsVisible="{CompiledBinding IsDownloading}" Value="{CompiledBinding Progress}" ShowProgressText="True" FontSize="12" />
|
||||||
<TextBlock IsVisible="{Binding !IsDownloading}" Text="{Binding StatusText}"/>
|
<TextBlock IsVisible="{CompiledBinding !IsDownloading}" Text="{CompiledBinding StatusText}"/>
|
||||||
</Panel>
|
</Panel>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid Margin="3" Grid.Column="2" HorizontalAlignment="Right" ColumnDefinitions="Auto,Auto">
|
<Grid Margin="3" Grid.Column="2" HorizontalAlignment="Right" ColumnDefinitions="Auto,Auto">
|
||||||
@ -39,7 +41,7 @@
|
|||||||
</Style>
|
</Style>
|
||||||
</Style>
|
</Style>
|
||||||
</Grid.Styles>
|
</Grid.Styles>
|
||||||
<StackPanel IsVisible="{Binding Queued}" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Orientation="Vertical">
|
<StackPanel IsVisible="{CompiledBinding Queued}" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Orientation="Vertical">
|
||||||
|
|
||||||
<Button Click="MoveFirst_Click">
|
<Button Click="MoveFirst_Click">
|
||||||
<Path VerticalAlignment="Top" Data="{StaticResource FirstButtonIcon}" />
|
<Path VerticalAlignment="Top" Data="{StaticResource FirstButtonIcon}" />
|
||||||
@ -55,13 +57,13 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<Panel Margin="3,0,0,0" Grid.Column="1" VerticalAlignment="Top">
|
<Panel Margin="3,0,0,0" Grid.Column="1" VerticalAlignment="Top">
|
||||||
<Button Height="32" Background="{DynamicResource CancelRed}" Width="22" IsVisible="{Binding !IsFinished}" CornerRadius="11" Click="Cancel_Click">
|
<Button Height="32" Background="{DynamicResource CancelRed}" Width="22" IsVisible="{CompiledBinding !IsFinished}" CornerRadius="11" Click="Cancel_Click">
|
||||||
<Path Fill="{DynamicResource ProcessQueueBookDefaultBrush}" VerticalAlignment="Center" Data="{StaticResource CancelButtonIcon}" RenderTransform="{StaticResource Rotate45Transform}" />
|
<Path Fill="{DynamicResource ProcessQueueBookDefaultBrush}" VerticalAlignment="Center" Data="{StaticResource CancelButtonIcon}" RenderTransform="{StaticResource Rotate45Transform}" />
|
||||||
</Button>
|
</Button>
|
||||||
</Panel>
|
</Panel>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Panel Margin="3" Width="50" Grid.Column="2">
|
<Panel Margin="3" Width="50" Grid.Column="2">
|
||||||
<TextPresenter FontSize="9" VerticalAlignment="Bottom" HorizontalAlignment="Right" IsVisible="{Binding IsDownloading}" Text="{Binding ETA}" />
|
<TextPresenter FontSize="9" VerticalAlignment="Bottom" HorizontalAlignment="Right" IsVisible="{CompiledBinding IsDownloading}" Text="{CompiledBinding ETA}" />
|
||||||
</Panel>
|
</Panel>
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@ -1,9 +1,7 @@
|
|||||||
using System;
|
|
||||||
using Avalonia.Controls;
|
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using LibationAvalonia.ViewModels;
|
|
||||||
using ApplicationServices;
|
using ApplicationServices;
|
||||||
|
using Avalonia.Controls;
|
||||||
using DataLayer;
|
using DataLayer;
|
||||||
|
using LibationAvalonia.ViewModels;
|
||||||
using LibationUiBase;
|
using LibationUiBase;
|
||||||
|
|
||||||
namespace LibationAvalonia.Views
|
namespace LibationAvalonia.Views
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Data.Converters;
|
using Avalonia.Data.Converters;
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using DataLayer;
|
using DataLayer;
|
||||||
using LibationAvalonia.ViewModels;
|
using LibationAvalonia.ViewModels;
|
||||||
using LibationUiBase;
|
using LibationUiBase;
|
||||||
@ -14,7 +13,7 @@ using System.Linq;
|
|||||||
namespace LibationAvalonia.Views
|
namespace LibationAvalonia.Views
|
||||||
{
|
{
|
||||||
public partial class ProcessQueueControl : UserControl
|
public partial class ProcessQueueControl : UserControl
|
||||||
{
|
{
|
||||||
private TrackedQueue<ProcessBookViewModel> Queue => _viewModel.Queue;
|
private TrackedQueue<ProcessBookViewModel> Queue => _viewModel.Queue;
|
||||||
private ProcessQueueViewModel _viewModel => DataContext as ProcessQueueViewModel;
|
private ProcessQueueViewModel _viewModel => DataContext as ProcessQueueViewModel;
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:views="clr-namespace:LibationAvalonia.Views"
|
xmlns:views="clr-namespace:LibationAvalonia.Views"
|
||||||
|
xmlns:uibase="clr-namespace:LibationUiBase.GridView;assembly=LibationUiBase"
|
||||||
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
||||||
mc:Ignorable="d" d:DesignWidth="1560" d:DesignHeight="400"
|
mc:Ignorable="d" d:DesignWidth="1560" d:DesignHeight="400"
|
||||||
x:Class="LibationAvalonia.Views.ProductsDisplay">
|
x:Class="LibationAvalonia.Views.ProductsDisplay">
|
||||||
@ -37,6 +38,14 @@
|
|||||||
<Setter Property="VerticalAlignment" Value="Center" />
|
<Setter Property="VerticalAlignment" Value="Center" />
|
||||||
<Setter Property="Fill" Value="{DynamicResource IconFill}" />
|
<Setter Property="Fill" Value="{DynamicResource IconFill}" />
|
||||||
</Style>
|
</Style>
|
||||||
|
<Style Selector="DataGridColumnHeader ContextMenu MenuItem">
|
||||||
|
<Setter Property="Padding" Value="0" />
|
||||||
|
<Style Selector="^:icon /template/ Viewbox#PART_IconPresenter">
|
||||||
|
<Setter Property="Height" Value="32" />
|
||||||
|
<Setter Property="Width" Value="32" />
|
||||||
|
<Setter Property="Margin" Value="6,0" />
|
||||||
|
</Style>
|
||||||
|
</Style>
|
||||||
</DataGrid.Styles>
|
</DataGrid.Styles>
|
||||||
|
|
||||||
<DataGrid.Columns>
|
<DataGrid.Columns>
|
||||||
@ -51,25 +60,25 @@
|
|||||||
Width="75">
|
Width="75">
|
||||||
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate x:DataType="uibase:IGridEntry">
|
||||||
<CheckBox
|
<CheckBox
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
IsThreeState="True"
|
IsThreeState="True"
|
||||||
IsChecked="{Binding Remove, Mode=TwoWay}" />
|
IsChecked="{CompiledBinding Remove, Mode=TwoWay}" />
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
</DataGridTemplateColumn>
|
</DataGridTemplateColumn>
|
||||||
|
|
||||||
<controls:DataGridTemplateColumnExt CanUserSort="True" Width="75" Header="Liberate" SortMemberPath="Liberate" ClipboardContentBinding="{Binding Liberate.ToolTip}">
|
<controls:DataGridTemplateColumnExt CanUserSort="True" Width="75" Header="Liberate" SortMemberPath="Liberate" ClipboardContentBinding="{Binding Liberate.ToolTip}">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate x:DataType="uibase:IGridEntry">
|
||||||
<views:LiberateStatusButton
|
<views:LiberateStatusButton
|
||||||
ToolTip.Tip="{Binding Liberate.ToolTip}"
|
ToolTip.Tip="{CompiledBinding Liberate.ToolTip}"
|
||||||
BookStatus="{Binding Liberate.BookStatus}"
|
BookStatus="{CompiledBinding Liberate.BookStatus}"
|
||||||
PdfStatus="{Binding Liberate.PdfStatus}"
|
PdfStatus="{CompiledBinding Liberate.PdfStatus}"
|
||||||
IsUnavailable="{Binding Liberate.IsUnavailable}"
|
IsUnavailable="{CompiledBinding Liberate.IsUnavailable}"
|
||||||
IsSeries="{Binding Liberate.IsSeries}"
|
IsSeries="{CompiledBinding Liberate.IsSeries}"
|
||||||
Expanded="{Binding Liberate.Expanded}"
|
Expanded="{CompiledBinding Liberate.Expanded}"
|
||||||
Click="LiberateButton_Click" />
|
Click="LiberateButton_Click" />
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
@ -77,17 +86,17 @@
|
|||||||
|
|
||||||
<DataGridTemplateColumn CanUserSort="False" Width="80" Header="Cover" SortMemberPath="Cover" ClipboardContentBinding="{Binding LibraryBook.Book.PictureLarge}">
|
<DataGridTemplateColumn CanUserSort="False" Width="80" Header="Cover" SortMemberPath="Cover" ClipboardContentBinding="{Binding LibraryBook.Book.PictureLarge}">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate x:DataType="uibase:IGridEntry">
|
||||||
<Image Opacity="{Binding Liberate.Opacity}" Tapped="Cover_Click" Height="80" Source="{Binding Cover}" ToolTip.Tip="Click to see full size" />
|
<Image Opacity="{CompiledBinding Liberate.Opacity}" Tapped="Cover_Click" Height="80" Source="{CompiledBinding Cover}" ToolTip.Tip="Click to see full size" />
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
</DataGridTemplateColumn>
|
</DataGridTemplateColumn>
|
||||||
|
|
||||||
<controls:DataGridTemplateColumnExt MinWidth="150" Width="2*" Header="Title" CanUserSort="True" SortMemberPath="Title" ClipboardContentBinding="{Binding Title}">
|
<controls:DataGridTemplateColumnExt MinWidth="150" Width="2*" Header="Title" CanUserSort="True" SortMemberPath="Title" ClipboardContentBinding="{Binding Title}">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate x:DataType="uibase:IGridEntry">
|
||||||
<Panel Opacity="{Binding Liberate.Opacity}" Background="{Binding Liberate.BackgroundBrush}">
|
<Panel Opacity="{CompiledBinding Liberate.Opacity}" Background="{CompiledBinding Liberate.BackgroundBrush}">
|
||||||
<TextBlock FontSize="14" Text="{Binding Title}" />
|
<TextBlock FontSize="14" Text="{CompiledBinding Title}" />
|
||||||
</Panel>
|
</Panel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
@ -95,9 +104,9 @@
|
|||||||
|
|
||||||
<controls:DataGridTemplateColumnExt MinWidth="80" Width="1*" Header="Authors" CanUserSort="True" SortMemberPath="Authors" ClipboardContentBinding="{Binding Authors}">
|
<controls:DataGridTemplateColumnExt MinWidth="80" Width="1*" Header="Authors" CanUserSort="True" SortMemberPath="Authors" ClipboardContentBinding="{Binding Authors}">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate x:DataType="uibase:IGridEntry">
|
||||||
<Panel Opacity="{Binding Liberate.Opacity}" Background="{Binding Liberate.BackgroundBrush}">
|
<Panel Opacity="{CompiledBinding Liberate.Opacity}" Background="{CompiledBinding Liberate.BackgroundBrush}">
|
||||||
<TextBlock Text="{Binding Authors}" />
|
<TextBlock Text="{CompiledBinding Authors}" />
|
||||||
</Panel>
|
</Panel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
@ -105,9 +114,9 @@
|
|||||||
|
|
||||||
<controls:DataGridTemplateColumnExt MinWidth="80" Width="1*" Header="Narrators" CanUserSort="True" SortMemberPath="Narrators" ClipboardContentBinding="{Binding Narrators}">
|
<controls:DataGridTemplateColumnExt MinWidth="80" Width="1*" Header="Narrators" CanUserSort="True" SortMemberPath="Narrators" ClipboardContentBinding="{Binding Narrators}">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate x:DataType="uibase:IGridEntry">
|
||||||
<Panel Opacity="{Binding Liberate.Opacity}" Background="{Binding Liberate.BackgroundBrush}">
|
<Panel Opacity="{CompiledBinding Liberate.Opacity}" Background="{CompiledBinding Liberate.BackgroundBrush}">
|
||||||
<TextBlock Text="{Binding Narrators}" />
|
<TextBlock Text="{CompiledBinding Narrators}" />
|
||||||
</Panel>
|
</Panel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
@ -115,9 +124,9 @@
|
|||||||
|
|
||||||
<controls:DataGridTemplateColumnExt Width="90" Header="Length" CanUserSort="True" SortMemberPath="Length" ClipboardContentBinding="{Binding Length}">
|
<controls:DataGridTemplateColumnExt Width="90" Header="Length" CanUserSort="True" SortMemberPath="Length" ClipboardContentBinding="{Binding Length}">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate x:DataType="uibase:IGridEntry">
|
||||||
<Panel Opacity="{Binding Liberate.Opacity}" Background="{Binding Liberate.BackgroundBrush}">
|
<Panel Opacity="{CompiledBinding Liberate.Opacity}" Background="{CompiledBinding Liberate.BackgroundBrush}">
|
||||||
<TextBlock Text="{Binding Length}" />
|
<TextBlock Text="{CompiledBinding Length}" />
|
||||||
</Panel>
|
</Panel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
@ -125,9 +134,19 @@
|
|||||||
|
|
||||||
<controls:DataGridTemplateColumnExt MinWidth="80" Width="1*" Header="Series" CanUserSort="True" SortMemberPath="Series" ClipboardContentBinding="{Binding Series}">
|
<controls:DataGridTemplateColumnExt MinWidth="80" Width="1*" Header="Series" CanUserSort="True" SortMemberPath="Series" ClipboardContentBinding="{Binding Series}">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate x:DataType="uibase:IGridEntry">
|
||||||
<Panel Opacity="{Binding Liberate.Opacity}" Background="{Binding Liberate.BackgroundBrush}">
|
<Panel Opacity="{CompiledBinding Liberate.Opacity}" Background="{CompiledBinding Liberate.BackgroundBrush}">
|
||||||
<TextBlock Text="{Binding Series}" />
|
<TextBlock Text="{CompiledBinding Series}" />
|
||||||
|
</Panel>
|
||||||
|
</DataTemplate>
|
||||||
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
|
</controls:DataGridTemplateColumnExt>
|
||||||
|
|
||||||
|
<controls:DataGridTemplateColumnExt Width="Auto" Header="Series
Order" CanUserSort="True" SortMemberPath="SeriesOrder" ClipboardContentBinding="{Binding Series}">
|
||||||
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
|
<DataTemplate x:DataType="uibase:IGridEntry">
|
||||||
|
<Panel Opacity="{CompiledBinding Liberate.Opacity}" Background="{CompiledBinding Liberate.BackgroundBrush}">
|
||||||
|
<TextBlock Text="{CompiledBinding SeriesOrder}" HorizontalAlignment="Center" />
|
||||||
</Panel>
|
</Panel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
@ -135,9 +154,9 @@
|
|||||||
|
|
||||||
<controls:DataGridTemplateColumnExt MinWidth="100" Width="1*" Header="Description" CanUserSort="True" SortMemberPath="Description" ClipboardContentBinding="{Binding LongDescription}">
|
<controls:DataGridTemplateColumnExt MinWidth="100" Width="1*" Header="Description" CanUserSort="True" SortMemberPath="Description" ClipboardContentBinding="{Binding LongDescription}">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate x:DataType="uibase:IGridEntry">
|
||||||
<Panel Opacity="{Binding Liberate.Opacity}" Background="{Binding Liberate.BackgroundBrush}" Tapped="Description_Click" ToolTip.Tip="Click to see full description" >
|
<Panel Opacity="{CompiledBinding Liberate.Opacity}" Background="{CompiledBinding Liberate.BackgroundBrush}" Tapped="Description_Click" ToolTip.Tip="Click to see full description" >
|
||||||
<TextBlock Text="{Binding Description}" FontSize="11" VerticalAlignment="Top" />
|
<TextBlock Text="{CompiledBinding Description}" FontSize="11" VerticalAlignment="Top" />
|
||||||
</Panel>
|
</Panel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
@ -145,49 +164,51 @@
|
|||||||
|
|
||||||
<controls:DataGridTemplateColumnExt Width="100" Header="Category" CanUserSort="True" SortMemberPath="Category" ClipboardContentBinding="{Binding Category}">
|
<controls:DataGridTemplateColumnExt Width="100" Header="Category" CanUserSort="True" SortMemberPath="Category" ClipboardContentBinding="{Binding Category}">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate x:DataType="uibase:IGridEntry">
|
||||||
<Panel Opacity="{Binding Liberate.Opacity}" Background="{Binding Liberate.BackgroundBrush}">
|
<Panel Opacity="{CompiledBinding Liberate.Opacity}" Background="{CompiledBinding Liberate.BackgroundBrush}">
|
||||||
<TextBlock Text="{Binding Category}" />
|
<TextBlock Text="{CompiledBinding Category}" />
|
||||||
</Panel>
|
</Panel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
</controls:DataGridTemplateColumnExt>
|
</controls:DataGridTemplateColumnExt>
|
||||||
|
|
||||||
<controls:DataGridMyRatingColumn
|
<controls:DataGridMyRatingColumn
|
||||||
|
x:DataType="uibase:IGridEntry"
|
||||||
Header="Product
Rating"
|
Header="Product
Rating"
|
||||||
IsReadOnly="true"
|
IsReadOnly="true"
|
||||||
Width="115"
|
Width="115"
|
||||||
SortMemberPath="ProductRating" CanUserSort="True"
|
SortMemberPath="ProductRating" CanUserSort="True"
|
||||||
OpacityBinding="{Binding Liberate.Opacity}"
|
OpacityBinding="{CompiledBinding Liberate.Opacity}"
|
||||||
BackgroundBinding="{Binding Liberate.BackgroundBrush}"
|
BackgroundBinding="{CompiledBinding Liberate.BackgroundBrush}"
|
||||||
ClipboardContentBinding="{Binding ProductRating}"
|
ClipboardContentBinding="{CompiledBinding ProductRating}"
|
||||||
Binding="{Binding ProductRating}" />
|
Binding="{CompiledBinding ProductRating}" />
|
||||||
|
|
||||||
<controls:DataGridTemplateColumnExt Width="90" Header="Purchase
Date" CanUserSort="True" SortMemberPath="PurchaseDate" ClipboardContentBinding="{Binding PurchaseDate}">
|
<controls:DataGridTemplateColumnExt Width="90" Header="Purchase
Date" CanUserSort="True" SortMemberPath="PurchaseDate" ClipboardContentBinding="{Binding PurchaseDate}">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate x:DataType="uibase:IGridEntry">
|
||||||
<Panel Opacity="{Binding Liberate.Opacity}" Background="{Binding Liberate.BackgroundBrush}">
|
<Panel Opacity="{CompiledBinding Liberate.Opacity}" Background="{CompiledBinding Liberate.BackgroundBrush}">
|
||||||
<TextBlock Text="{Binding PurchaseDate}" />
|
<TextBlock Text="{CompiledBinding PurchaseDate}" />
|
||||||
</Panel>
|
</Panel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
</controls:DataGridTemplateColumnExt>
|
</controls:DataGridTemplateColumnExt>
|
||||||
|
|
||||||
<controls:DataGridMyRatingColumn
|
<controls:DataGridMyRatingColumn
|
||||||
|
x:DataType="uibase:IGridEntry"
|
||||||
Header="My Rating"
|
Header="My Rating"
|
||||||
IsReadOnly="false"
|
IsReadOnly="false"
|
||||||
Width="115"
|
Width="115"
|
||||||
SortMemberPath="MyRating" CanUserSort="True"
|
SortMemberPath="MyRating" CanUserSort="True"
|
||||||
OpacityBinding="{Binding Liberate.Opacity}"
|
OpacityBinding="{CompiledBinding Liberate.Opacity}"
|
||||||
BackgroundBinding="{Binding Liberate.BackgroundBrush}"
|
BackgroundBinding="{CompiledBinding Liberate.BackgroundBrush}"
|
||||||
ClipboardContentBinding="{Binding MyRating}"
|
ClipboardContentBinding="{CompiledBinding MyRating}"
|
||||||
Binding="{Binding MyRating, Mode=TwoWay}" />
|
Binding="{CompiledBinding MyRating, Mode=TwoWay}" />
|
||||||
|
|
||||||
<controls:DataGridTemplateColumnExt Width="135" Header="Misc" CanUserSort="True" SortMemberPath="Misc" ClipboardContentBinding="{Binding Misc}">
|
<controls:DataGridTemplateColumnExt Width="135" Header="Misc" CanUserSort="True" SortMemberPath="Misc" ClipboardContentBinding="{Binding Misc}">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate x:DataType="uibase:IGridEntry">
|
||||||
<Panel Opacity="{Binding Liberate.Opacity}" Background="{Binding Liberate.BackgroundBrush}">
|
<Panel Opacity="{CompiledBinding Liberate.Opacity}" Background="{CompiledBinding Liberate.BackgroundBrush}">
|
||||||
<TextBlock Text="{Binding Misc}" TextWrapping="WrapWithOverflow" FontSize="10" />
|
<TextBlock Text="{CompiledBinding Misc}" TextWrapping="WrapWithOverflow" FontSize="10" />
|
||||||
</Panel>
|
</Panel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
@ -195,9 +216,9 @@
|
|||||||
|
|
||||||
<controls:DataGridTemplateColumnExt Width="102" Header="Last
Download" CanUserSort="True" SortMemberPath="LastDownload" ClipboardContentBinding="{Binding LastDownload}">
|
<controls:DataGridTemplateColumnExt Width="102" Header="Last
Download" CanUserSort="True" SortMemberPath="LastDownload" ClipboardContentBinding="{Binding LastDownload}">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate x:DataType="uibase:IGridEntry">
|
||||||
<Panel Opacity="{Binding Liberate.Opacity}" Background="{Binding Liberate.BackgroundBrush}" ToolTip.Tip="{Binding LastDownload.ToolTipText}" DoubleTapped="Version_DoubleClick">
|
<Panel Opacity="{CompiledBinding Liberate.Opacity}" Background="{CompiledBinding Liberate.BackgroundBrush}" ToolTip.Tip="{CompiledBinding LastDownload.ToolTipText}" DoubleTapped="Version_DoubleClick">
|
||||||
<TextBlock Text="{Binding LastDownload}" TextWrapping="WrapWithOverflow" FontSize="10" />
|
<TextBlock Text="{CompiledBinding LastDownload}" TextWrapping="WrapWithOverflow" FontSize="10" />
|
||||||
</Panel>
|
</Panel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
@ -205,13 +226,13 @@
|
|||||||
|
|
||||||
<controls:DataGridTemplateColumnExt CanUserSort="True" Width="100" Header="Tags" SortMemberPath="BookTags" ClipboardContentBinding="{Binding BookTags}">
|
<controls:DataGridTemplateColumnExt CanUserSort="True" Width="100" Header="Tags" SortMemberPath="BookTags" ClipboardContentBinding="{Binding BookTags}">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate x:DataType="uibase:IGridEntry">
|
||||||
<Button IsVisible="{Binding !Liberate.IsSeries}" Width="100" Height="80" Click="OnTagsButtonClick" ToolTip.Tip="Click to edit tags" >
|
<Button IsVisible="{CompiledBinding !Liberate.IsSeries}" Width="100" Height="80" Click="OnTagsButtonClick" ToolTip.Tip="Click to edit tags" >
|
||||||
<Panel Opacity="{Binding Liberate.Opacity}">
|
<Panel Opacity="{CompiledBinding Liberate.Opacity}">
|
||||||
<Panel Width="24" Height="24" IsVisible="{Binding BookTags, Converter={x:Static StringConverters.IsNullOrEmpty}}">
|
<Panel Width="24" Height="24" IsVisible="{CompiledBinding BookTags, Converter={x:Static StringConverters.IsNullOrEmpty}}">
|
||||||
<Path Stretch="Uniform" Fill="{DynamicResource IconFill}" Data="{StaticResource EditTagsIcon}" />
|
<Path Stretch="Uniform" Fill="{DynamicResource IconFill}" Data="{StaticResource EditTagsIcon}" />
|
||||||
</Panel>
|
</Panel>
|
||||||
<TextBlock IsVisible="{Binding BookTags, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" FontSize="12" TextWrapping="WrapWithOverflow" HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding BookTags}"/>
|
<TextBlock IsVisible="{CompiledBinding BookTags, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" FontSize="12" TextWrapping="WrapWithOverflow" HorizontalAlignment="Center" VerticalAlignment="Center" Text="{CompiledBinding BookTags}"/>
|
||||||
</Panel>
|
</Panel>
|
||||||
</Button>
|
</Button>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
using ApplicationServices;
|
using ApplicationServices;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using Avalonia.Platform.Storage;
|
using Avalonia.Platform.Storage;
|
||||||
using DataLayer;
|
using DataLayer;
|
||||||
using FileLiberator;
|
using FileLiberator;
|
||||||
@ -256,7 +255,7 @@ namespace LibationAvalonia.Views
|
|||||||
contextMenu.MenuClosed += ContextMenu_MenuClosed;
|
contextMenu.MenuClosed += ContextMenu_MenuClosed;
|
||||||
contextMenu.ContextMenuOpening += ContextMenu_ContextMenuOpening;
|
contextMenu.ContextMenuOpening += ContextMenu_ContextMenuOpening;
|
||||||
List<Control> menuItems = new();
|
List<Control> menuItems = new();
|
||||||
contextMenu.Items = menuItems;
|
contextMenu.ItemsSource = menuItems;
|
||||||
|
|
||||||
menuItems.Add(new MenuItem { Header = "Show / Hide Columns" });
|
menuItems.Add(new MenuItem { Header = "Show / Hide Columns" });
|
||||||
menuItems.Add(new MenuItem { Header = "-" });
|
menuItems.Add(new MenuItem { Header = "-" });
|
||||||
@ -274,13 +273,9 @@ namespace LibationAvalonia.Views
|
|||||||
(
|
(
|
||||||
new MenuItem
|
new MenuItem
|
||||||
{
|
{
|
||||||
Header = ((string)column.Header).Replace((char)0xa, ' '),
|
Header = ((string)column.Header).Replace('\n', ' '),
|
||||||
Tag = column,
|
Tag = column,
|
||||||
Margin = new Thickness(6, 0),
|
Icon = new CheckBox(),
|
||||||
Icon = new CheckBox
|
|
||||||
{
|
|
||||||
Width = 50,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -419,7 +414,9 @@ namespace LibationAvalonia.Views
|
|||||||
if (!isDefault)
|
if (!isDefault)
|
||||||
PictureStorage.PictureCached -= PictureCached;
|
PictureStorage.PictureCached -= PictureCached;
|
||||||
|
|
||||||
if (!imageDisplayDialog.IsVisible)
|
if (imageDisplayDialog.IsVisible)
|
||||||
|
imageDisplayDialog.Activate();
|
||||||
|
else
|
||||||
imageDisplayDialog.Show();
|
imageDisplayDialog.Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,16 +1,11 @@
|
|||||||
using AudibleApi.Common;
|
using Avalonia.Collections;
|
||||||
using AudibleApi;
|
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Media;
|
||||||
using DataLayer;
|
using DataLayer;
|
||||||
using Dinah.Core;
|
using Dinah.Core;
|
||||||
using FileLiberator;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Avalonia.Collections;
|
|
||||||
using LibationAvalonia.Dialogs;
|
using LibationAvalonia.Dialogs;
|
||||||
using LibationUiBase.SeriesView;
|
using LibationUiBase.SeriesView;
|
||||||
using System;
|
using System;
|
||||||
using Avalonia.Media;
|
|
||||||
|
|
||||||
namespace LibationAvalonia.Views
|
namespace LibationAvalonia.Views
|
||||||
{
|
{
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
||||||
|
xmlns:uibase="clr-namespace:LibationUiBase.SeriesView;assembly=LibationUiBase"
|
||||||
x:Class="LibationAvalonia.Views.SeriesViewGrid">
|
x:Class="LibationAvalonia.Views.SeriesViewGrid">
|
||||||
|
|
||||||
<DataGrid
|
<DataGrid
|
||||||
@ -34,11 +35,11 @@
|
|||||||
|
|
||||||
<DataGridTemplateColumn Width="80" Header="Cover" CanUserSort="False">
|
<DataGridTemplateColumn Width="80" Header="Cover" CanUserSort="False">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate x:DataType="uibase:SeriesItem">
|
||||||
<Image
|
<Image
|
||||||
Tapped="Cover_Click"
|
Tapped="Cover_Click"
|
||||||
Height="80"
|
Height="80"
|
||||||
Source="{Binding Cover}"
|
Source="{CompiledBinding Cover}"
|
||||||
ToolTip.Tip="Click to see full size" />
|
ToolTip.Tip="Click to see full size" />
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
@ -46,10 +47,10 @@
|
|||||||
|
|
||||||
<DataGridTemplateColumn Width="Auto" Header="Series
Order" CanUserSort="True" SortMemberPath="Order">
|
<DataGridTemplateColumn Width="Auto" Header="Series
Order" CanUserSort="True" SortMemberPath="Order">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate x:DataType="uibase:SeriesItem">
|
||||||
<Panel>
|
<Panel>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Text="{Binding Order}"
|
Text="{CompiledBinding Order}"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
VerticalAlignment="Center" />
|
VerticalAlignment="Center" />
|
||||||
</Panel>
|
</Panel>
|
||||||
@ -59,7 +60,7 @@
|
|||||||
|
|
||||||
<DataGridTemplateColumn Width="Auto" Header="Availability" CanUserSort="True" SortMemberPath="Button">
|
<DataGridTemplateColumn Width="Auto" Header="Availability" CanUserSort="True" SortMemberPath="Button">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate x:DataType="uibase:SeriesItem">
|
||||||
<Panel>
|
<Panel>
|
||||||
<Panel.Styles>
|
<Panel.Styles>
|
||||||
<Style Selector="TextBlock">
|
<Style Selector="TextBlock">
|
||||||
@ -71,17 +72,17 @@
|
|||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
VerticalAlignment="Stretch"
|
VerticalAlignment="Stretch"
|
||||||
Click="Availability_Click"
|
Click="Availability_Click"
|
||||||
IsVisible="{Binding Button.HasButtonAction}"
|
IsVisible="{CompiledBinding Button.HasButtonAction}"
|
||||||
IsEnabled="{Binding Button.Enabled}">
|
IsEnabled="{CompiledBinding Button.Enabled}">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Text="{Binding Button.DisplayText}"
|
Text="{CompiledBinding Button.DisplayText}"
|
||||||
TextAlignment="Center"
|
TextAlignment="Center"
|
||||||
VerticalAlignment="Center" />
|
VerticalAlignment="Center" />
|
||||||
</Button>
|
</Button>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
IsVisible="{Binding !Button.HasButtonAction}"
|
IsVisible="{CompiledBinding !Button.HasButtonAction}"
|
||||||
Text="{Binding Button.DisplayText}" />
|
Text="{CompiledBinding Button.DisplayText}" />
|
||||||
</Panel>
|
</Panel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
@ -89,11 +90,11 @@
|
|||||||
|
|
||||||
<DataGridTemplateColumn MinWidth="150" Width="*" Header="Title" CanUserSort="True" SortMemberPath="Title">
|
<DataGridTemplateColumn MinWidth="150" Width="*" Header="Title" CanUserSort="True" SortMemberPath="Title">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate x:DataType="uibase:SeriesItem">
|
||||||
<Panel ToolTip.Tip="Open Audible product page">
|
<Panel ToolTip.Tip="Open Audible product page">
|
||||||
<controls:LinkLabel
|
<controls:LinkLabel
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="{Binding Title}"
|
Text="{CompiledBinding Title}"
|
||||||
Tapped="Title_Click" />
|
Tapped="Title_Click" />
|
||||||
</Panel>
|
</Panel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
using ApplicationServices;
|
|
||||||
using AudibleApi.Common;
|
using AudibleApi.Common;
|
||||||
using AudibleUtilities;
|
|
||||||
using Avalonia.Collections;
|
using Avalonia.Collections;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using DataLayer;
|
using DataLayer;
|
||||||
@ -83,7 +81,9 @@ namespace LibationAvalonia.Views
|
|||||||
if (!isDefault)
|
if (!isDefault)
|
||||||
PictureStorage.PictureCached -= PictureCached;
|
PictureStorage.PictureCached -= PictureCached;
|
||||||
|
|
||||||
if (!imageDisplayDialog.IsVisible)
|
if (imageDisplayDialog.IsVisible)
|
||||||
|
imageDisplayDialog.Activate();
|
||||||
|
else
|
||||||
imageDisplayDialog.Show();
|
imageDisplayDialog.Show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
256
Source/LibationAvalonia/Walkthrough.cs
Normal file
256
Source/LibationAvalonia/Walkthrough.cs
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
using ApplicationServices;
|
||||||
|
using AudibleUtilities;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Primitives;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
|
using Avalonia.Media;
|
||||||
|
using Avalonia.Threading;
|
||||||
|
using Dinah.Core.StepRunner;
|
||||||
|
using LibationAvalonia.Dialogs;
|
||||||
|
using LibationAvalonia.Views;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace LibationAvalonia
|
||||||
|
{
|
||||||
|
internal class Walkthrough
|
||||||
|
{
|
||||||
|
private static Dictionary<string, string> settingTabMessages = new()
|
||||||
|
{
|
||||||
|
{ "Important Settings", "Change where liberated books are stored."},
|
||||||
|
{ "Import Library", "Change how your library is scanned, imported, and liberated."},
|
||||||
|
{ "Download/Decrypt", "Control how liberated files and folders are named and stored."},
|
||||||
|
{ "Audio File Settings", "Control how audio files are decrypted, including audio format and metadata handling."},
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly MainWindow MainForm;
|
||||||
|
private readonly AsyncStepSequence sequence = new();
|
||||||
|
public Walkthrough(MainWindow mainForm)
|
||||||
|
{
|
||||||
|
MainForm = mainForm;
|
||||||
|
sequence[nameof(ShowAccountDialog)] = ShowAccountDialog;
|
||||||
|
sequence[nameof(ShowSettingsDialog)] = ShowSettingsDialog;
|
||||||
|
sequence[nameof(ShowAccountScanning)] = ShowAccountScanning;
|
||||||
|
sequence[nameof(ShowSearching)] = ShowSearching;
|
||||||
|
sequence[nameof(ShowQuickFilters)] = ShowQuickFilters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task RunAsync() => await sequence.RunAsync();
|
||||||
|
|
||||||
|
private async Task<bool> ShowAccountDialog()
|
||||||
|
{
|
||||||
|
if (await OkCancelMessageBox("First, add your Audible account(s).", "Add Accounts") is not DialogResult.OK) return false;
|
||||||
|
|
||||||
|
await Task.Delay(750);
|
||||||
|
await flashControlAsync(MainForm.settingsToolStripMenuItem);
|
||||||
|
await InvokeAsync(MainForm.settingsToolStripMenuItem.Open);
|
||||||
|
await Task.Delay(500);
|
||||||
|
|
||||||
|
await flashControlAsync(MainForm.accountsToolStripMenuItem);
|
||||||
|
await InvokeAsync(() => MainForm.accountsToolStripMenuItem.IsSelected = true);
|
||||||
|
await Task.Delay(500);
|
||||||
|
|
||||||
|
var accountSettings = await InvokeAsync(() => new AccountsDialog());
|
||||||
|
accountSettings.Opened += async (_, _) => await MessageBox.Show(accountSettings, "Add your Audible account(s), then save.", "Add an Account");
|
||||||
|
await InvokeAsync(() => accountSettings.ShowDialog(MainForm));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> ShowSettingsDialog()
|
||||||
|
{
|
||||||
|
if (await OkCancelMessageBox("Next, adjust Libation's settings", "Change Settings") is not DialogResult.OK) return false;
|
||||||
|
|
||||||
|
await Task.Delay(750);
|
||||||
|
await flashControlAsync(MainForm.settingsToolStripMenuItem);
|
||||||
|
await InvokeAsync(MainForm.settingsToolStripMenuItem.Open);
|
||||||
|
await Task.Delay(500);
|
||||||
|
|
||||||
|
await flashControlAsync(MainForm.basicSettingsToolStripMenuItem);
|
||||||
|
await InvokeAsync(() => MainForm.basicSettingsToolStripMenuItem.IsSelected = true);
|
||||||
|
await Task.Delay(500);
|
||||||
|
|
||||||
|
var settingsDialog = await InvokeAsync(() => new SettingsDialog());
|
||||||
|
|
||||||
|
var tabsToVisit = settingsDialog.tabControl.Items.OfType<TabItem>().ToList();
|
||||||
|
|
||||||
|
foreach (var tab in tabsToVisit)
|
||||||
|
tab.PropertyChanged += TabControl_PropertyChanged;
|
||||||
|
|
||||||
|
settingsDialog.Closing += SettingsDialog_FormClosing;
|
||||||
|
|
||||||
|
await InvokeAsync(() => settingsDialog.ShowDialog(MainForm));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
async void TabControl_PropertyChanged(object sender, Avalonia.AvaloniaPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Property == TabItem.IsSelectedProperty)
|
||||||
|
{
|
||||||
|
var selectedTab = sender as TabItem;
|
||||||
|
|
||||||
|
tabsToVisit.Remove(selectedTab);
|
||||||
|
|
||||||
|
if (!selectedTab.IsVisible || !(selectedTab.Header is TextBlock header && settingTabMessages.ContainsKey(header.Text))) return;
|
||||||
|
|
||||||
|
await MessageBox.Show(settingsDialog, settingTabMessages[header.Text], header.Text + " Tab", MessageBoxButtons.OK);
|
||||||
|
|
||||||
|
settingTabMessages.Remove(header.Text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsDialog_FormClosing(object sender, WindowClosingEventArgs e)
|
||||||
|
{
|
||||||
|
if (tabsToVisit.Count > 0)
|
||||||
|
{
|
||||||
|
var nextTab = tabsToVisit[0];
|
||||||
|
tabsToVisit.RemoveAt(0);
|
||||||
|
|
||||||
|
settingsDialog.tabControl.SelectedItem = nextTab;
|
||||||
|
e.Cancel = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> ShowAccountScanning()
|
||||||
|
{
|
||||||
|
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
||||||
|
var count = persister.AccountsSettings.Accounts.Count;
|
||||||
|
|
||||||
|
if (count < 1)
|
||||||
|
{
|
||||||
|
await InvokeAsync(() => MessageBox.Show(MainForm, "Add an Audible account, then sync your library through the \"Import\" menu", "Add an Audible Account", MessageBoxButtons.OK, MessageBoxIcon.Information));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var accounts = count > 1 ? "accounts" : "account";
|
||||||
|
var library = count > 1 ? "libraries" : "library";
|
||||||
|
if (await OkCancelMessageBox($"Finally, scan your Audible {accounts} to sync your {library} with Libation", $"Scan {accounts}") is not DialogResult.OK) return false;
|
||||||
|
|
||||||
|
var scanItem = count > 1 ? MainForm.scanLibraryOfAllAccountsToolStripMenuItem : MainForm.scanLibraryToolStripMenuItem;
|
||||||
|
|
||||||
|
await Task.Delay(750);
|
||||||
|
await flashControlAsync(MainForm.importToolStripMenuItem);
|
||||||
|
await InvokeAsync(MainForm.importToolStripMenuItem.Open);
|
||||||
|
await Task.Delay(500);
|
||||||
|
|
||||||
|
await flashControlAsync(scanItem);
|
||||||
|
await InvokeAsync(() => scanItem.IsSelected = true);
|
||||||
|
await Task.Delay(500);
|
||||||
|
|
||||||
|
await InvokeAsync(() => scanItem.RaiseEvent(new RoutedEventArgs(MenuItem.ClickEvent)));
|
||||||
|
await InvokeAsync(MainForm.importToolStripMenuItem.Close);
|
||||||
|
|
||||||
|
var tcs = new TaskCompletionSource();
|
||||||
|
LibraryCommands.ScanEnd += LibraryCommands_ScanEnd;
|
||||||
|
await tcs.Task;
|
||||||
|
LibraryCommands.ScanEnd -= LibraryCommands_ScanEnd;
|
||||||
|
MainForm.ViewModel.ProductsDisplay.VisibleCountChanged -= productsDisplay_VisibleCountChanged;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
void LibraryCommands_ScanEnd(object sender, int newCount)
|
||||||
|
{
|
||||||
|
//if we imported new books, wait for the grid to update before proceeding.
|
||||||
|
if (newCount > 0)
|
||||||
|
MainForm.ViewModel.ProductsDisplay.VisibleCountChanged += productsDisplay_VisibleCountChanged;
|
||||||
|
else
|
||||||
|
tcs.SetResult();
|
||||||
|
}
|
||||||
|
void productsDisplay_VisibleCountChanged(object sender, int e) => tcs.SetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> ShowSearching()
|
||||||
|
{
|
||||||
|
var books = DbContexts.GetLibrary_Flat_NoTracking();
|
||||||
|
if (books.Count == 0) return true;
|
||||||
|
|
||||||
|
var firstAuthor = getFirstAuthor();
|
||||||
|
if (firstAuthor == null) return true;
|
||||||
|
|
||||||
|
if (await OkCancelMessageBox("You can filter the grid entries by searching", "Searching") is not DialogResult.OK) return false;
|
||||||
|
|
||||||
|
await flashControlAsync(MainForm.filterSearchTb);
|
||||||
|
|
||||||
|
await InvokeAsync(() => MainForm.filterSearchTb.Text = string.Empty);
|
||||||
|
foreach (var c in firstAuthor)
|
||||||
|
{
|
||||||
|
await InvokeAsync(() => MainForm.filterSearchTb.Text += c);
|
||||||
|
await Task.Delay(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
await flashControlAsync(MainForm.filterBtn);
|
||||||
|
await InvokeAsync(MainForm.filterBtn.Focus);
|
||||||
|
await Task.Delay(500);
|
||||||
|
|
||||||
|
await InvokeAsync(() => MainForm.filterBtn.RaiseEvent(new RoutedEventArgs(Button.ClickEvent)));
|
||||||
|
await Task.Delay(1000);
|
||||||
|
|
||||||
|
await MessageBox.Show(MainForm, "Libation provides a built-in cheat sheet for its query language", "Search Cheat Sheet");
|
||||||
|
|
||||||
|
await flashControlAsync(MainForm.filterHelpBtn);
|
||||||
|
var filterHelp = await InvokeAsync(() => new SearchSyntaxDialog());
|
||||||
|
await InvokeAsync(() => filterHelp.ShowDialog(MainForm));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
private async Task<bool> ShowQuickFilters()
|
||||||
|
{
|
||||||
|
var firstAuthor = getFirstAuthor();
|
||||||
|
|
||||||
|
if (firstAuthor == null) return true;
|
||||||
|
|
||||||
|
if (await OkCancelMessageBox("Queries that you perform regularly can be added to 'Quick Filters'", "Quick Filters") is not DialogResult.OK) return false;
|
||||||
|
|
||||||
|
await InvokeAsync(() => MainForm.filterSearchTb.Text = firstAuthor);
|
||||||
|
|
||||||
|
await Task.Delay(750);
|
||||||
|
await flashControlAsync(MainForm.addQuickFilterBtn);
|
||||||
|
await InvokeAsync(() => MainForm.addQuickFilterBtn.RaiseEvent(new RoutedEventArgs(Button.ClickEvent)));
|
||||||
|
await Task.Delay(750);
|
||||||
|
|
||||||
|
await flashControlAsync(MainForm.quickFiltersToolStripMenuItem);
|
||||||
|
await InvokeAsync(MainForm.quickFiltersToolStripMenuItem.Open);
|
||||||
|
await Task.Delay(500);
|
||||||
|
|
||||||
|
var editQuickFiltersToolStripMenuItem = MainForm.quickFiltersToolStripMenuItem.ItemsSource.OfType<MenuItem>().ElementAt(1);
|
||||||
|
|
||||||
|
await flashControlAsync(editQuickFiltersToolStripMenuItem);
|
||||||
|
await InvokeAsync(() => editQuickFiltersToolStripMenuItem.IsSelected = true);
|
||||||
|
await Task.Delay(500);
|
||||||
|
|
||||||
|
var editQuickFilters = await InvokeAsync(() => new EditQuickFilters());
|
||||||
|
editQuickFilters.Opened += async (_, _) => await MessageBox.Show(editQuickFilters, "From here you can edit, delete, and change the order of Quick Filters", "Editing Quick Filters");
|
||||||
|
await InvokeAsync(() => editQuickFilters.ShowDialog(MainForm));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string getFirstAuthor()
|
||||||
|
{
|
||||||
|
var books = DbContexts.GetLibrary_Flat_NoTracking();
|
||||||
|
return books.SelectMany(lb => lb.Book.Authors).FirstOrDefault(a => !string.IsNullOrWhiteSpace(a.Name))?.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task flashControlAsync(TemplatedControl control, int flashCount = 3)
|
||||||
|
{
|
||||||
|
var backColor = await InvokeAsync(() => control.Background);
|
||||||
|
for (int i = 0; i < flashCount; i++)
|
||||||
|
{
|
||||||
|
await InvokeAsync(() => control.Background = Brushes.Firebrick);
|
||||||
|
await Task.Delay(200);
|
||||||
|
await InvokeAsync(() => control.Background = backColor);
|
||||||
|
await Task.Delay(200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task<T> InvokeAsync<T>(Func<T> func) => Dispatcher.UIThread.InvokeAsync(func);
|
||||||
|
private Task<T> InvokeAsync<T>(Func<Task<T>> func) => Dispatcher.UIThread.InvokeAsync(func);
|
||||||
|
private Task InvokeAsync(Func<Task> action) => Dispatcher.UIThread.InvokeAsync(action);
|
||||||
|
private Task InvokeAsync(Action action) => Dispatcher.UIThread.InvokeAsync(action);
|
||||||
|
|
||||||
|
private Task<DialogResult> OkCancelMessageBox(string message, string caption)
|
||||||
|
=> InvokeAsync(() => MessageBox.Show(MainForm, message, caption, MessageBoxButtons.OKCancel));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -142,8 +142,15 @@ namespace LibationFileManager
|
|||||||
[Description("Lame target VBR quality [10,100]")]
|
[Description("Lame target VBR quality [10,100]")]
|
||||||
public int LameVBRQuality { get => GetNonString(defaultValue: 2); set => SetNonString(value); }
|
public int LameVBRQuality { get => GetNonString(defaultValue: 2); set => SetNonString(value); }
|
||||||
|
|
||||||
|
private static readonly EquatableDictionary<string, bool> DefaultColumns = new(
|
||||||
|
new KeyValuePair<string, bool>[]
|
||||||
|
{
|
||||||
|
new ("SeriesOrder", false),
|
||||||
|
new ("LastDownload", false)
|
||||||
|
});
|
||||||
|
|
||||||
[Description("A Dictionary of GridView data property names and bool indicating its column's visibility in ProductsGrid")]
|
[Description("A Dictionary of GridView data property names and bool indicating its column's visibility in ProductsGrid")]
|
||||||
public Dictionary<string, bool> GridColumnsVisibilities { get => GetNonString(defaultValue: new EquatableDictionary<string, bool>()).Clone(); set => SetNonString(value); }
|
public Dictionary<string, bool> GridColumnsVisibilities { get => GetNonString(defaultValue: DefaultColumns).Clone(); set => SetNonString(value); }
|
||||||
|
|
||||||
[Description("A Dictionary of GridView data property names and int indicating its column's display index in ProductsGrid")]
|
[Description("A Dictionary of GridView data property names and int indicating its column's display index in ProductsGrid")]
|
||||||
public Dictionary<string, int> GridColumnsDisplayIndices { get => GetNonString(defaultValue: new EquatableDictionary<string, int>()).Clone(); set => SetNonString(value); }
|
public Dictionary<string, int> GridColumnsDisplayIndices { get => GetNonString(defaultValue: new EquatableDictionary<string, int>()).Clone(); set => SetNonString(value); }
|
||||||
@ -184,6 +191,9 @@ namespace LibationFileManager
|
|||||||
Ignore = 3
|
Ignore = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Description("Indicates that this is the first time Libation has been run")]
|
||||||
|
public bool FirstLaunch { get => GetNonString(defaultValue: true); set => SetNonString(value); }
|
||||||
|
|
||||||
[Description("When liberating books and there is an error, Libation should:")]
|
[Description("When liberating books and there is an error, Libation should:")]
|
||||||
public BadBookAction BadBook { get => GetNonString(defaultValue: BadBookAction.Ask); set => SetNonString(value); }
|
public BadBookAction BadBook { get => GetNonString(defaultValue: BadBookAction.Ask); set => SetNonString(value); }
|
||||||
|
|
||||||
|
|||||||
@ -40,6 +40,7 @@ namespace LibationUiBase.GridView
|
|||||||
private LastDownloadStatus _lastDownload;
|
private LastDownloadStatus _lastDownload;
|
||||||
private object _cover;
|
private object _cover;
|
||||||
private string _series;
|
private string _series;
|
||||||
|
private SeriesOrder _seriesOrder;
|
||||||
private string _title;
|
private string _title;
|
||||||
private string _authors;
|
private string _authors;
|
||||||
private string _narrators;
|
private string _narrators;
|
||||||
@ -57,6 +58,7 @@ namespace LibationUiBase.GridView
|
|||||||
public LastDownloadStatus LastDownload { get => _lastDownload; protected set => RaiseAndSetIfChanged(ref _lastDownload, value); }
|
public LastDownloadStatus LastDownload { get => _lastDownload; protected set => RaiseAndSetIfChanged(ref _lastDownload, value); }
|
||||||
public object Cover { get => _cover; private set => RaiseAndSetIfChanged(ref _cover, value); }
|
public object Cover { get => _cover; private set => RaiseAndSetIfChanged(ref _cover, value); }
|
||||||
public string Series { get => _series; private set => RaiseAndSetIfChanged(ref _series, value); }
|
public string Series { get => _series; private set => RaiseAndSetIfChanged(ref _series, value); }
|
||||||
|
public SeriesOrder SeriesOrder { get => _seriesOrder; private set => RaiseAndSetIfChanged(ref _seriesOrder, value); }
|
||||||
public string Title { get => _title; private set => RaiseAndSetIfChanged(ref _title, value); }
|
public string Title { get => _title; private set => RaiseAndSetIfChanged(ref _title, value); }
|
||||||
public string Authors { get => _authors; private set => RaiseAndSetIfChanged(ref _authors, value); }
|
public string Authors { get => _authors; private set => RaiseAndSetIfChanged(ref _authors, value); }
|
||||||
public string Narrators { get => _narrators; private set => RaiseAndSetIfChanged(ref _narrators, value); }
|
public string Narrators { get => _narrators; private set => RaiseAndSetIfChanged(ref _narrators, value); }
|
||||||
@ -105,6 +107,7 @@ namespace LibationUiBase.GridView
|
|||||||
|
|
||||||
Title = Book.Title;
|
Title = Book.Title;
|
||||||
Series = Book.SeriesNames(includeIndex: true);
|
Series = Book.SeriesNames(includeIndex: true);
|
||||||
|
SeriesOrder = new SeriesOrder(Book.SeriesLink);
|
||||||
Length = GetBookLengthString();
|
Length = GetBookLengthString();
|
||||||
//Ratings are changed using Update(), which is a problem for Avalonia data bindings because
|
//Ratings are changed using Update(), which is a problem for Avalonia data bindings because
|
||||||
//the reference doesn't change. Clone the rating so that it updates within Avalonia properly.
|
//the reference doesn't change. Clone the rating so that it updates within Avalonia properly.
|
||||||
@ -200,6 +203,7 @@ namespace LibationUiBase.GridView
|
|||||||
{ nameof(Remove), () => Remove.HasValue ? Remove.Value ? RemoveStatus.Removed : RemoveStatus.NotRemoved : RemoveStatus.SomeRemoved },
|
{ nameof(Remove), () => Remove.HasValue ? Remove.Value ? RemoveStatus.Removed : RemoveStatus.NotRemoved : RemoveStatus.SomeRemoved },
|
||||||
{ nameof(Title), () => Book.TitleSortable() },
|
{ nameof(Title), () => Book.TitleSortable() },
|
||||||
{ nameof(Series), () => Book.SeriesSortable() },
|
{ nameof(Series), () => Book.SeriesSortable() },
|
||||||
|
{ nameof(SeriesOrder), () => SeriesOrder },
|
||||||
{ nameof(Length), () => GetLengthInMinutes() },
|
{ nameof(Length), () => GetLengthInMinutes() },
|
||||||
{ nameof(MyRating), () => Book.UserDefinedItem.Rating },
|
{ nameof(MyRating), () => Book.UserDefinedItem.Rating },
|
||||||
{ nameof(PurchaseDate), () => GetPurchaseDate() },
|
{ nameof(PurchaseDate), () => GetPurchaseDate() },
|
||||||
@ -233,6 +237,7 @@ namespace LibationUiBase.GridView
|
|||||||
{ typeof(Rating), new ObjectComparer<Rating>() },
|
{ typeof(Rating), new ObjectComparer<Rating>() },
|
||||||
{ typeof(DateTime), new ObjectComparer<DateTime>() },
|
{ typeof(DateTime), new ObjectComparer<DateTime>() },
|
||||||
{ typeof(EntryStatus), new ObjectComparer<EntryStatus>() },
|
{ typeof(EntryStatus), new ObjectComparer<EntryStatus>() },
|
||||||
|
{ typeof(SeriesOrder), new ObjectComparer<SeriesOrder>() },
|
||||||
{ typeof(LastDownloadStatus), new ObjectComparer<LastDownloadStatus>() },
|
{ typeof(LastDownloadStatus), new ObjectComparer<LastDownloadStatus>() },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user