diff --git a/Source/AppScaffolding/UpgradeProperties.cs b/Source/AppScaffolding/UpgradeProperties.cs index aa042b52..4db8765c 100644 --- a/Source/AppScaffolding/UpgradeProperties.cs +++ b/Source/AppScaffolding/UpgradeProperties.cs @@ -1,5 +1,4 @@ -using NPOI.XWPF.UserModel; -using System; +using System; using System.Text.RegularExpressions; namespace AppScaffolding diff --git a/Source/ApplicationServices/LibraryCommands.cs b/Source/ApplicationServices/LibraryCommands.cs index b18e05c9..a690458d 100644 --- a/Source/ApplicationServices/LibraryCommands.cs +++ b/Source/ApplicationServices/LibraryCommands.cs @@ -12,7 +12,6 @@ using DtoImporterService; using FileManager; using LibationFileManager; using Newtonsoft.Json.Linq; -using NPOI.OpenXmlFormats.Spreadsheet; using Serilog; using static DtoImporterService.PerfLogger; @@ -21,7 +20,7 @@ namespace ApplicationServices public static class LibraryCommands { public static event EventHandler ScanBegin; - public static event EventHandler ScanEnd; + public static event EventHandler ScanEnd; public static bool Scanning { get; private set; } private static object _lock { get; } = new(); @@ -95,7 +94,7 @@ namespace ApplicationServices { stop(); var putBreakPointHere = logOutput; - ScanEnd?.Invoke(null, null); + ScanEnd?.Invoke(null, 0); GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true, true); } } @@ -108,7 +107,8 @@ namespace ApplicationServices if (accounts is null || accounts.Length == 0) return (0, 0); - try + int newCount = 0; + try { lock (_lock) { @@ -134,7 +134,7 @@ namespace ApplicationServices Log.Logger.Information("Begin long-running import"); logTime($"pre {nameof(importIntoDbAsync)}"); - var newCount = await importIntoDbAsync(importItems); + newCount = await importIntoDbAsync(importItems); logTime($"post {nameof(importIntoDbAsync)}"); Log.Logger.Information($"Import complete. New count {newCount}"); @@ -167,7 +167,7 @@ namespace ApplicationServices { stop(); var putBreakPointHere = logOutput; - ScanEnd?.Invoke(null, null); + ScanEnd?.Invoke(null, newCount); GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true, true); } } @@ -283,7 +283,8 @@ namespace ApplicationServices catch(ImportValidationException ex) { 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 dtoItems, IEnumerable exceptions = null) diff --git a/Source/AudibleUtilities/ApiExtended.cs b/Source/AudibleUtilities/ApiExtended.cs index 97caa4aa..c9c202ae 100644 --- a/Source/AudibleUtilities/ApiExtended.cs +++ b/Source/AudibleUtilities/ApiExtended.cs @@ -152,13 +152,17 @@ namespace AudibleUtilities 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(); totalTime += sw.Elapsed; 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."); - var allExceptions = IValidator.GetAllValidators().SelectMany(v => v.Validate(items)); - if (allExceptions?.Any() is true) + var allExceptions = IValidator.GetAllValidators().SelectMany(v => v.Validate(items)).ToList(); + if (allExceptions?.Count > 0) throw new ImportValidationException(items, allExceptions); return items; diff --git a/Source/DtoImporterService/LibraryBookImporter.cs b/Source/DtoImporterService/LibraryBookImporter.cs index 58583be2..e08a29a1 100644 --- a/Source/DtoImporterService/LibraryBookImporter.cs +++ b/Source/DtoImporterService/LibraryBookImporter.cs @@ -41,13 +41,20 @@ namespace DtoImporterService // // 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; - 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) { @@ -109,7 +116,7 @@ namespace DtoImporterService => isPlusTitleUnavailable(item1) && !isPlusTitleUnavailable(item2) ? item2 : item1; private static bool isPlusTitleUnavailable(ImportItem item) - => item.DtoItem.IsAyce is true - && item.DtoItem.Plans?.Any(p => p.IsAyce) is not true; + => item.DtoItem.ContentType is null + || (item.DtoItem.IsAyce is true && item.DtoItem.Plans?.Any(p => p.IsAyce) is not true); } } diff --git a/Source/LibationAvalonia/App.axaml.cs b/Source/LibationAvalonia/App.axaml.cs index 13fa7356..e31714b4 100644 --- a/Source/LibationAvalonia/App.axaml.cs +++ b/Source/LibationAvalonia/App.axaml.cs @@ -1,24 +1,24 @@ -using Avalonia; +using ApplicationServices; +using Avalonia; +using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; using Avalonia.Media; -using LibationFileManager; -using LibationAvalonia.Views; -using System; using Avalonia.Platform; +using Avalonia.Styling; using LibationAvalonia.Dialogs; -using System.Threading.Tasks; +using LibationAvalonia.Views; +using LibationFileManager; +using System; using System.Collections.Generic; using System.IO; -using ApplicationServices; -using Avalonia.Controls; -using Avalonia.Styling; +using System.Threading.Tasks; namespace LibationAvalonia { 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 ProcessQueueBookCompletedBrush { get; private set; } public static IBrush ProcessQueueBookCancelledBrush { get; private set; } @@ -39,18 +39,15 @@ namespace LibationAvalonia } public static Task> LibraryTask; - public static bool SetupRequired; public override void OnFrameworkInitializationCompleted() { - LoadStyles(); - if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { - if (SetupRequired) - { - var config = Configuration.Instance; + var config = Configuration.Instance; + if (!config.LibationSettingsAreValid) + { var defaultLibationFilesDir = Configuration.UserProfile; // check for existing settings in default location @@ -60,7 +57,7 @@ namespace LibationAvalonia if (config.LibationSettingsAreValid) { - LibraryTask = Task.Run(() => DbContexts.GetLibrary_Flat_NoTracking(includeParents: true)); + LibraryTask = Task.Run(() => DbContexts.GetLibrary_Flat_NoTracking(includeParents: true)); ShowMainWindow(desktop); } else @@ -86,11 +83,29 @@ namespace LibationAvalonia { // all returns should be preceded by either: // - if config.LibationSettingsAreValid - // - error message, Exit() + // - error message, Exit() if (setupDialog.IsNewUser) { 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) { @@ -130,40 +145,6 @@ namespace LibationAvalonia AppScaffolding.LibationScaffolding.RunPostMigrationScaffolding(config); } - private void ShowSettingsWindow(IClassicDesktopStyleApplicationLifetime desktop, Configuration config, Action 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 OnClose) { var libationFilesDialog = new LibationFilesDialog(); @@ -200,11 +181,23 @@ namespace LibationAvalonia MessageBoxIcon.Question); 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 await CancelInstallation(); - } + libationFilesDialog.Close(); } @@ -230,11 +223,11 @@ namespace LibationAvalonia private static void LoadStyles() { - ProcessQueueBookFailedBrush = AvaloniaUtils.GetBrushFromResources("ProcessQueueBookFailedBrush"); - ProcessQueueBookCompletedBrush = AvaloniaUtils.GetBrushFromResources("ProcessQueueBookCompletedBrush"); - ProcessQueueBookCancelledBrush = AvaloniaUtils.GetBrushFromResources("ProcessQueueBookCancelledBrush"); - SeriesEntryGridBackgroundBrush = AvaloniaUtils.GetBrushFromResources("SeriesEntryGridBackgroundBrush"); - ProcessQueueBookDefaultBrush = AvaloniaUtils.GetBrushFromResources("ProcessQueueBookDefaultBrush"); + ProcessQueueBookFailedBrush = AvaloniaUtils.GetBrushFromResources(nameof(ProcessQueueBookFailedBrush)); + ProcessQueueBookCompletedBrush = AvaloniaUtils.GetBrushFromResources(nameof(ProcessQueueBookCompletedBrush)); + ProcessQueueBookCancelledBrush = AvaloniaUtils.GetBrushFromResources(nameof(ProcessQueueBookCancelledBrush)); + SeriesEntryGridBackgroundBrush = AvaloniaUtils.GetBrushFromResources(nameof(SeriesEntryGridBackgroundBrush)); + ProcessQueueBookDefaultBrush = AvaloniaUtils.GetBrushFromResources(nameof(ProcessQueueBookDefaultBrush)); HyperlinkVisited = AvaloniaUtils.GetBrushFromResources(nameof(HyperlinkVisited)); } } diff --git a/Source/LibationAvalonia/AvaloniaUtils.cs b/Source/LibationAvalonia/AvaloniaUtils.cs index f92e1c4d..a825eace 100644 --- a/Source/LibationAvalonia/AvaloniaUtils.cs +++ b/Source/LibationAvalonia/AvaloniaUtils.cs @@ -13,7 +13,7 @@ namespace LibationAvalonia public static IBrush GetBrushFromResources(string name) => GetBrushFromResources(name, Brushes.Transparent); public static IBrush GetBrushFromResources(string name, IBrush defaultBrush) - { + { if (App.Current.TryGetResource(name, App.Current.ActualThemeVariant, out var value) && value is IBrush brush) return brush; return defaultBrush; diff --git a/Source/LibationAvalonia/Controls/CheckedListBox.axaml.cs b/Source/LibationAvalonia/Controls/CheckedListBox.axaml.cs index f0ab1e61..7f3d07a5 100644 --- a/Source/LibationAvalonia/Controls/CheckedListBox.axaml.cs +++ b/Source/LibationAvalonia/Controls/CheckedListBox.axaml.cs @@ -3,9 +3,6 @@ using Avalonia.Collections; using Avalonia.Controls; using LibationAvalonia.ViewModels; using ReactiveUI; -using System; -using System.Collections.Generic; -using System.Linq; namespace LibationAvalonia.Controls { @@ -15,7 +12,7 @@ namespace LibationAvalonia.Controls AvaloniaProperty.Register>(nameof(Items)); public AvaloniaList Items { get => GetValue(ItemsProperty); set => SetValue(ItemsProperty, value); } - private CheckedListBoxViewModel _viewModel = new(); + private CheckedListBoxViewModel _viewModel = new(); public CheckedListBox() { diff --git a/Source/LibationAvalonia/Controls/DataGridCheckBoxColumnExt.cs b/Source/LibationAvalonia/Controls/DataGridCheckBoxColumnExt.cs index eca89804..f99d9bda 100644 --- a/Source/LibationAvalonia/Controls/DataGridCheckBoxColumnExt.cs +++ b/Source/LibationAvalonia/Controls/DataGridCheckBoxColumnExt.cs @@ -3,7 +3,7 @@ using LibationUiBase.GridView; namespace LibationAvalonia.Controls { - public class DataGridCheckBoxColumnExt : DataGridCheckBoxColumn + public class DataGridCheckBoxColumnExt : DataGridCheckBoxColumn { protected override Control GenerateEditingElementDirect(DataGridCell cell, object dataItem) { diff --git a/Source/LibationAvalonia/Controls/DataGridContextMenus.cs b/Source/LibationAvalonia/Controls/DataGridContextMenus.cs index 277e68ee..60e939cf 100644 --- a/Source/LibationAvalonia/Controls/DataGridContextMenus.cs +++ b/Source/LibationAvalonia/Controls/DataGridContextMenus.cs @@ -15,7 +15,7 @@ namespace LibationAvalonia.Controls static DataGridContextMenus() { - ContextMenu.Items = MenuItems; + ContextMenu.ItemsSource = MenuItems; OwningColumnProperty = typeof(DataGridCell).GetProperty("OwningColumn", BindingFlags.Instance | BindingFlags.NonPublic); } @@ -66,6 +66,6 @@ namespace LibationAvalonia.Controls public IGridEntry GridEntry { get; init; } public ContextMenu ContextMenu { get; init; } public AvaloniaList ContextMenuItems - => ContextMenu.Items as AvaloniaList; + => ContextMenu.ItemsSource as AvaloniaList; } } diff --git a/Source/LibationAvalonia/Controls/DataGridMyRatingColumn.cs b/Source/LibationAvalonia/Controls/DataGridMyRatingColumn.cs index 41b2be6f..871d7330 100644 --- a/Source/LibationAvalonia/Controls/DataGridMyRatingColumn.cs +++ b/Source/LibationAvalonia/Controls/DataGridMyRatingColumn.cs @@ -4,7 +4,6 @@ using Avalonia.Data; using Avalonia.Interactivity; using DataLayer; using ReactiveUI; -using System; namespace LibationAvalonia.Controls { diff --git a/Source/LibationAvalonia/Controls/DataGridTemplateColumnExt.cs b/Source/LibationAvalonia/Controls/DataGridTemplateColumnExt.cs index b1ed35c7..c65358aa 100644 --- a/Source/LibationAvalonia/Controls/DataGridTemplateColumnExt.cs +++ b/Source/LibationAvalonia/Controls/DataGridTemplateColumnExt.cs @@ -1,6 +1,4 @@ using Avalonia.Controls; -using System; -using System.Linq; namespace LibationAvalonia.Controls { diff --git a/Source/LibationAvalonia/Controls/DirectoryOrCustomSelectControl.axaml.cs b/Source/LibationAvalonia/Controls/DirectoryOrCustomSelectControl.axaml.cs index 338e48e7..a62280a0 100644 --- a/Source/LibationAvalonia/Controls/DirectoryOrCustomSelectControl.axaml.cs +++ b/Source/LibationAvalonia/Controls/DirectoryOrCustomSelectControl.axaml.cs @@ -1,10 +1,9 @@ using Avalonia; using Avalonia.Controls; -using Avalonia.Markup.Xaml; using Dinah.Core; using LibationFileManager; -using System.Collections.Generic; using ReactiveUI; +using System.Collections.Generic; using System.Linq; namespace LibationAvalonia.Controls @@ -56,12 +55,12 @@ namespace LibationAvalonia.Controls directorySelectControl.PropertyChanged += DirectorySelectControl_PropertyChanged; } - private class CustomState: ViewModels.ViewModelBase + private class CustomState : ViewModels.ViewModelBase { private string _customDir; private bool _knownChecked; 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 { get => _knownChecked; @@ -141,7 +140,7 @@ namespace LibationAvalonia.Controls var known = Configuration.GetKnownDirectory(noSubDir); if (known == Configuration.KnownDirectories.None && noSubDir == Configuration.AppDir_Absolute) - known = Configuration.KnownDirectories.AppDir; + known = Configuration.KnownDirectories.AppDir; if (known is Configuration.KnownDirectories.None) { diff --git a/Source/LibationAvalonia/Controls/DirectorySelectControl.axaml.cs b/Source/LibationAvalonia/Controls/DirectorySelectControl.axaml.cs index 5cb84725..ebd4717d 100644 --- a/Source/LibationAvalonia/Controls/DirectorySelectControl.axaml.cs +++ b/Source/LibationAvalonia/Controls/DirectorySelectControl.axaml.cs @@ -1,13 +1,12 @@ using Avalonia; using Avalonia.Controls; -using Avalonia.Markup.Xaml; +using Avalonia.Data; +using Avalonia.Data.Converters; using Dinah.Core; using LibationFileManager; -using System.Collections.Generic; -using Avalonia.Data.Converters; using System; +using System.Collections.Generic; using System.Globalization; -using Avalonia.Data; using System.IO; using System.Reactive.Subjects; diff --git a/Source/LibationAvalonia/Controls/GroupBox.axaml.cs b/Source/LibationAvalonia/Controls/GroupBox.axaml.cs index bf9a72b8..dd9a148f 100644 --- a/Source/LibationAvalonia/Controls/GroupBox.axaml.cs +++ b/Source/LibationAvalonia/Controls/GroupBox.axaml.cs @@ -1,6 +1,5 @@ using Avalonia; using Avalonia.Controls; -using Avalonia.Markup.Xaml; namespace LibationAvalonia.Controls { diff --git a/Source/LibationAvalonia/Controls/LinkLabel.axaml.cs b/Source/LibationAvalonia/Controls/LinkLabel.axaml.cs index 4d7ee7ac..978a1e7a 100644 --- a/Source/LibationAvalonia/Controls/LinkLabel.axaml.cs +++ b/Source/LibationAvalonia/Controls/LinkLabel.axaml.cs @@ -1,8 +1,5 @@ -using Avalonia; using Avalonia.Controls; using Avalonia.Input; -using Avalonia.Markup.Xaml; -using Avalonia.Media; using Avalonia.Styling; using System; diff --git a/Source/LibationAvalonia/Controls/MyRatingCellEditor.axaml.cs b/Source/LibationAvalonia/Controls/MyRatingCellEditor.axaml.cs index c5100f14..a23b960f 100644 --- a/Source/LibationAvalonia/Controls/MyRatingCellEditor.axaml.cs +++ b/Source/LibationAvalonia/Controls/MyRatingCellEditor.axaml.cs @@ -18,7 +18,7 @@ namespace LibationAvalonia.Controls public bool IsEditingMode { get; set; } public Rating Rating { get => GetValue(RatingProperty); set => SetValue(RatingProperty, value); } - + public MyRatingCellEditor() { InitializeComponent(); diff --git a/Source/LibationAvalonia/Controls/WheelComboBox.axaml.cs b/Source/LibationAvalonia/Controls/WheelComboBox.axaml.cs index 9658ec3e..cf6a150f 100644 --- a/Source/LibationAvalonia/Controls/WheelComboBox.axaml.cs +++ b/Source/LibationAvalonia/Controls/WheelComboBox.axaml.cs @@ -1,11 +1,7 @@ -using Avalonia; using Avalonia.Controls; using Avalonia.Input; -using Avalonia.Markup.Xaml; using Avalonia.Styling; using System; -using System.Collections; -using System.Linq; namespace LibationAvalonia.Controls { diff --git a/Source/LibationAvalonia/Dialogs/AccountsDialog.axaml b/Source/LibationAvalonia/Dialogs/AccountsDialog.axaml index 07647a43..cb88ce89 100644 --- a/Source/LibationAvalonia/Dialogs/AccountsDialog.axaml +++ b/Source/LibationAvalonia/Dialogs/AccountsDialog.axaml @@ -64,11 +64,14 @@ - - + + + + + + + + @@ -93,10 +96,13 @@ - + + + + + + + diff --git a/Source/LibationAvalonia/Dialogs/AccountsDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/AccountsDialog.axaml.cs index e2da5d21..cbbee5dc 100644 --- a/Source/LibationAvalonia/Dialogs/AccountsDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/AccountsDialog.axaml.cs @@ -1,27 +1,24 @@ +using AudibleApi; using AudibleUtilities; +using Avalonia.Collections; using Avalonia.Controls; -using Avalonia.Markup.Xaml; +using Avalonia.Platform.Storage; +using ReactiveUI; using System; using System.Collections.Generic; -using System.Collections.ObjectModel; +using System.Collections.Specialized; using System.IO; using System.Linq; using System.Threading.Tasks; -using ReactiveUI; -using AudibleApi; -using Avalonia.Platform.Storage; -using LibationFileManager; -using Avalonia.Platform.Storage.FileIO; namespace LibationAvalonia.Dialogs { public partial class AccountsDialog : DialogWindow { - public ObservableCollection Accounts { get; } = new(); + public AvaloniaList Accounts { get; } = new(); public class AccountDto : ViewModels.ViewModelBase { private string _accountId; - private Locale _selectedLocale; public IReadOnlyList Locales => AccountsDialog.Locales; public bool LibraryScan { get; set; } = true; public string AccountId @@ -31,19 +28,21 @@ namespace LibationAvalonia.Dialogs { this.RaiseAndSetIfChanged(ref _accountId, value); 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 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() @@ -58,57 +57,40 @@ namespace LibationAvalonia.Dialogs // here: copy strings and dispose of persister // only persist in 'save' step using var persister = AudibleApiStorage.GetAccountsSettingsPersister(); - var accounts = persister.AccountsSettings.Accounts; - if (accounts.Any()) - { - foreach (var account in accounts) - AddAccountToGrid(account); - } + + Accounts.CollectionChanged += Accounts_CollectionChanged; + Accounts.AddRange(persister.AccountsSettings.Accounts.Select(a => new AccountDto(a))); DataContext = this; addBlankAccount(); } - private void addBlankAccount() - { - var newBlank = new AccountDto(); - newBlank.PropertyChanged += AccountDto_PropertyChanged; - Accounts.Insert(Accounts.Count, newBlank); - } - - private void AddAccountToGrid(Account account) + private void Accounts_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { - AccountDto accountDto = new() + if (e.Action is NotifyCollectionChangedAction.Add && e.NewItems?.Count > 0) { - LibraryScan = account.LibraryScan, - AccountId = account.AccountId, - SelectedLocale = Locales.Single(l => l.Name == account.Locale.Name), - AccountName = account.AccountName, - }; - accountDto.PropertyChanged += AccountDto_PropertyChanged; - - //ObservableCollection doesn't fire CollectionChanged on Add, so use Insert instead - Accounts.Insert(Accounts.Count, accountDto); + foreach (var newItem in e.NewItems.OfType()) + newItem.PropertyChanged += AccountDto_PropertyChanged; + } + else if (e.Action is NotifyCollectionChangedAction.Remove && e.OldItems?.Count > 0) + { + foreach (var oldItem in e.OldItems.OfType()) + oldItem.PropertyChanged -= AccountDto_PropertyChanged; + } } + private void addBlankAccount() => Accounts.Insert(Accounts.Count, new AccountDto()); + private void AccountDto_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { - if (Accounts.Any(a => a.IsDefault)) - return; - - addBlankAccount(); + if (!Accounts.Any(a => a.IsDefault)) + addBlankAccount(); } public void DeleteButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e) { 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); - } } public async void ImportButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e) @@ -119,11 +101,11 @@ namespace LibationAvalonia.Dialogs AllowMultiple = false, FileTypeFilter = new FilePickerFileType[] { - new("JSON files (*.json)") - { - Patterns = new[] { "*.json" }, - AppleUniformTypeIdentifiers = new[] { "public.json" } - } + new("JSON files (*.json)") + { + Patterns = new[] { "*.json" }, + AppleUniformTypeIdentifiers = new[] { "public.json" } + } } }; @@ -153,7 +135,7 @@ namespace LibationAvalonia.Dialogs persister.AccountsSettings.Add(account); - AddAccountToGrid(account); + Accounts.Add(new AccountDto(account)); } catch (Exception ex) { diff --git a/Source/LibationAvalonia/Dialogs/BookDetailsDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/BookDetailsDialog.axaml.cs index 5f825f44..2cb4218b 100644 --- a/Source/LibationAvalonia/Dialogs/BookDetailsDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/BookDetailsDialog.axaml.cs @@ -1,11 +1,10 @@ using ApplicationServices; using Avalonia.Controls; -using Avalonia.Markup.Xaml; using Avalonia.Media.Imaging; using DataLayer; using Dinah.Core; -using LibationFileManager; using LibationAvalonia.ViewModels; +using LibationFileManager; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; @@ -42,14 +41,14 @@ namespace LibationAvalonia.Dialogs LibraryBook = context.GetLibraryBook_Flat_NoTracking("B017V4IM1G"); } } - public BookDetailsDialog(LibraryBook libraryBook) :this() + public BookDetailsDialog(LibraryBook libraryBook) : this() { LibraryBook = libraryBook; } protected override void SaveAndClose() - { - LibraryBook.Book.UpdateUserDefinedItem(NewTags, bookStatus: BookLiberatedStatus, pdfStatus: PdfLiberatedStatus); + { + LibraryBook.Book.UpdateUserDefinedItem(NewTags, bookStatus: BookLiberatedStatus, pdfStatus: PdfLiberatedStatus); base.SaveAndClose(); } @@ -61,7 +60,7 @@ namespace LibationAvalonia.Dialogs } public void SaveButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e) - => SaveAndClose(); + => SaveAndClose(); private class BookDetailsDialogViewModel : ViewModelBase { diff --git a/Source/LibationAvalonia/Dialogs/BookRecordsDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/BookRecordsDialog.axaml.cs index 1389e0d7..9261394c 100644 --- a/Source/LibationAvalonia/Dialogs/BookRecordsDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/BookRecordsDialog.axaml.cs @@ -79,7 +79,7 @@ namespace LibationAvalonia.Dialogs public void CheckAll_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) { - foreach (var record in bookRecordEntries) + foreach (var record in bookRecordEntries) record.IsChecked = true; } public void UncheckAll_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) diff --git a/Source/LibationAvalonia/Dialogs/DescriptionDisplayDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/DescriptionDisplayDialog.axaml.cs index 1a934173..8a33783b 100644 --- a/Source/LibationAvalonia/Dialogs/DescriptionDisplayDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/DescriptionDisplayDialog.axaml.cs @@ -1,6 +1,5 @@ using Avalonia; using Avalonia.Controls; -using Avalonia.Markup.Xaml; using System; namespace LibationAvalonia.Dialogs @@ -12,9 +11,7 @@ namespace LibationAvalonia.Dialogs public DescriptionDisplayDialog() { InitializeComponent(); -#if DEBUG - this.AttachDevTools(); -#endif + DescriptionTextBox = this.FindControl(nameof(DescriptionTextBox)); this.Activated += DescriptionDisplay_Activated; Opened += DescriptionDisplay_Opened; diff --git a/Source/LibationAvalonia/Dialogs/DialogWindow.cs b/Source/LibationAvalonia/Dialogs/DialogWindow.cs index f30e9c47..a46aea77 100644 --- a/Source/LibationAvalonia/Dialogs/DialogWindow.cs +++ b/Source/LibationAvalonia/Dialogs/DialogWindow.cs @@ -1,5 +1,4 @@ -using Avalonia; -using Avalonia.Controls; +using Avalonia.Controls; using LibationFileManager; using System; using System.Threading.Tasks; @@ -17,10 +16,6 @@ namespace LibationAvalonia.Dialogs this.Initialized += DialogWindow_Initialized; this.Opened += DialogWindow_Opened; this.Closing += DialogWindow_Closing; - -#if DEBUG - this.AttachDevTools(); -#endif } public DialogWindow(bool saveAndRestorePosition) : this() { diff --git a/Source/LibationAvalonia/Dialogs/EditQuickFilters.axaml b/Source/LibationAvalonia/Dialogs/EditQuickFilters.axaml index d10beafc..1890818a 100644 --- a/Source/LibationAvalonia/Dialogs/EditQuickFilters.axaml +++ b/Source/LibationAvalonia/Dialogs/EditQuickFilters.axaml @@ -42,13 +42,16 @@ - + + + + + + + + + - diff --git a/Source/LibationAvalonia/Dialogs/EditQuickFilters.axaml.cs b/Source/LibationAvalonia/Dialogs/EditQuickFilters.axaml.cs index 52b65df1..70d99c48 100644 --- a/Source/LibationAvalonia/Dialogs/EditQuickFilters.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/EditQuickFilters.axaml.cs @@ -1,9 +1,9 @@ using AudibleUtilities; using Avalonia.Controls; using LibationFileManager; +using ReactiveUI; using System.Collections.ObjectModel; using System.Linq; -using ReactiveUI; namespace LibationAvalonia.Dialogs { diff --git a/Source/LibationAvalonia/Dialogs/EditReplacementChars.axaml b/Source/LibationAvalonia/Dialogs/EditReplacementChars.axaml index e23fb97d..202a2a7a 100644 --- a/Source/LibationAvalonia/Dialogs/EditReplacementChars.axaml +++ b/Source/LibationAvalonia/Dialogs/EditReplacementChars.axaml @@ -4,6 +4,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450" MinWidth="500" MinHeight="450" + Width="500" Height="450" x:Class="LibationAvalonia.Dialogs.EditReplacementChars" Title="Illegal Character Replacement" Icon="/Assets/libation.ico"> @@ -26,21 +27,31 @@ Items="{Binding replacements}"> + + + + + + + + + - + + + + + + + - - - + + + + + + + diff --git a/Source/LibationAvalonia/Dialogs/EditReplacementChars.axaml.cs b/Source/LibationAvalonia/Dialogs/EditReplacementChars.axaml.cs index 91b8f039..813b4e80 100644 --- a/Source/LibationAvalonia/Dialogs/EditReplacementChars.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/EditReplacementChars.axaml.cs @@ -1,12 +1,11 @@ +using Avalonia.Collections; using Avalonia.Controls; -using Avalonia.Markup.Xaml; +using Avalonia.Data; using FileManager; using LibationFileManager; -using System.Collections.Generic; using ReactiveUI; +using System.Collections.Generic; using System.Linq; -using Avalonia.Collections; -using Avalonia.Data; namespace LibationAvalonia.Dialogs { @@ -37,7 +36,7 @@ namespace LibationAvalonia.Dialogs } 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) => LoadTable(ReplacementCharacters.LoFiDefault.Replacements); public void Barebones_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) @@ -50,7 +49,7 @@ namespace LibationAvalonia.Dialogs protected override void SaveAndClose() { var replacements = SOURCE - .Where(r=> !r.IsDefault) + .Where(r => !r.IsDefault) .Select(r => new Replacement(r.Character, r.ReplacementText, r.Description) { Mandatory = r.Mandatory }) .ToList(); diff --git a/Source/LibationAvalonia/Dialogs/EditTemplateDialog.axaml b/Source/LibationAvalonia/Dialogs/EditTemplateDialog.axaml index 4723e4d0..db5dfe22 100644 --- a/Source/LibationAvalonia/Dialogs/EditTemplateDialog.axaml +++ b/Source/LibationAvalonia/Dialogs/EditTemplateDialog.axaml @@ -3,6 +3,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" + Width="800" Height="450" x:Class="LibationAvalonia.Dialogs.EditTemplateDialog" xmlns:dialogs="clr-namespace:LibationAvalonia.Dialogs" Icon="/Assets/libation.ico" diff --git a/Source/LibationAvalonia/Dialogs/EditTemplateDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/EditTemplateDialog.axaml.cs index 32674059..fe03866c 100644 --- a/Source/LibationAvalonia/Dialogs/EditTemplateDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/EditTemplateDialog.axaml.cs @@ -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 Dinah.Core; using LibationFileManager; +using ReactiveUI; using System; -using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; -using ReactiveUI; -using Avalonia.Controls.Documents; -using Avalonia.Collections; -using Avalonia.Controls; -using Avalonia.Markup.Xaml.Templates; namespace LibationAvalonia.Dialogs { diff --git a/Source/LibationAvalonia/Dialogs/ImageDisplayDialog.axaml b/Source/LibationAvalonia/Dialogs/ImageDisplayDialog.axaml index a6f0cfd7..46654fb7 100644 --- a/Source/LibationAvalonia/Dialogs/ImageDisplayDialog.axaml +++ b/Source/LibationAvalonia/Dialogs/ImageDisplayDialog.axaml @@ -5,6 +5,7 @@ mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="500" x:Class="LibationAvalonia.Dialogs.ImageDisplayDialog" MinWidth="500" MinHeight="500" + Width="500" Height="520" Title="Cover" WindowStartupLocation="CenterOwner" Icon="/Assets/libation.ico"> diff --git a/Source/LibationAvalonia/Dialogs/ImageDisplayDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/ImageDisplayDialog.axaml.cs index d866c247..051d78e1 100644 --- a/Source/LibationAvalonia/Dialogs/ImageDisplayDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/ImageDisplayDialog.axaml.cs @@ -1,9 +1,8 @@ -using Avalonia.Markup.Xaml; using Avalonia.Media.Imaging; +using Avalonia.Platform.Storage; +using ReactiveUI; using System; using System.ComponentModel; -using ReactiveUI; -using Avalonia.Platform.Storage; namespace LibationAvalonia.Dialogs { diff --git a/Source/LibationAvalonia/Dialogs/LibationFilesDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/LibationFilesDialog.axaml.cs index e2e0b912..a21e44a3 100644 --- a/Source/LibationAvalonia/Dialogs/LibationFilesDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/LibationFilesDialog.axaml.cs @@ -1,10 +1,6 @@ -using Avalonia; using Avalonia.Controls; -using Avalonia.Markup.Xaml; using LibationFileManager; -using LibationAvalonia.Controls; using System.Collections.Generic; -using System.Threading.Tasks; namespace LibationAvalonia.Dialogs { @@ -18,7 +14,7 @@ namespace LibationAvalonia.Dialogs Configuration.KnownDirectories.AppDir, Configuration.KnownDirectories.MyDocs }; - + public string Directory { get; set; } = Configuration.GetKnownDirectoryPath(Configuration.KnownDirectories.UserProfile); } private DirSelectOptions dirSelectOptions; @@ -28,9 +24,6 @@ namespace LibationAvalonia.Dialogs { InitializeComponent(); -#if DEBUG - this.AttachDevTools(); -#endif DataContext = dirSelectOptions = new(); } diff --git a/Source/LibationAvalonia/Dialogs/LiberatedStatusBatchAutoDialog.axaml b/Source/LibationAvalonia/Dialogs/LiberatedStatusBatchAutoDialog.axaml index 8430a7c5..05ffd5d0 100644 --- a/Source/LibationAvalonia/Dialogs/LiberatedStatusBatchAutoDialog.axaml +++ b/Source/LibationAvalonia/Dialogs/LiberatedStatusBatchAutoDialog.axaml @@ -8,6 +8,7 @@ Title="Liberated status: Whether the book has been downloaded" MinHeight="100" MaxHeight="165" MinWidth="600" MaxWidth="800" + Width="600" WindowStartupLocation="CenterOwner" Icon="/Assets/libation.ico"> diff --git a/Source/LibationAvalonia/Dialogs/LiberatedStatusBatchAutoDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/LiberatedStatusBatchAutoDialog.axaml.cs index 47c0e7b8..761a1224 100644 --- a/Source/LibationAvalonia/Dialogs/LiberatedStatusBatchAutoDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/LiberatedStatusBatchAutoDialog.axaml.cs @@ -1,13 +1,11 @@ -using Avalonia.Markup.Xaml; - namespace LibationAvalonia.Dialogs { public partial class LiberatedStatusBatchAutoDialog : DialogWindow - { - public bool SetDownloaded { get; set; } - public bool SetNotDownloaded { get; set; } + { + public bool SetDownloaded { get; set; } + public bool SetNotDownloaded { get; set; } - public LiberatedStatusBatchAutoDialog() + public LiberatedStatusBatchAutoDialog() { InitializeComponent(); DataContext = this; diff --git a/Source/LibationAvalonia/Dialogs/LiberatedStatusBatchManualDialog.axaml b/Source/LibationAvalonia/Dialogs/LiberatedStatusBatchManualDialog.axaml index bdb39d0c..a0a294ab 100644 --- a/Source/LibationAvalonia/Dialogs/LiberatedStatusBatchManualDialog.axaml +++ b/Source/LibationAvalonia/Dialogs/LiberatedStatusBatchManualDialog.axaml @@ -8,6 +8,7 @@ Title="Liberated status: Whether the book has been downloaded" MinWidth="400" MinHeight="120" MaxWidth="400" MaxHeight="120" + Width="400" Height="120" WindowStartupLocation="CenterOwner" Icon="/Assets/libation.ico"> diff --git a/Source/LibationAvalonia/Dialogs/LiberatedStatusBatchManualDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/LiberatedStatusBatchManualDialog.axaml.cs index 189eb279..acaa90af 100644 --- a/Source/LibationAvalonia/Dialogs/LiberatedStatusBatchManualDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/LiberatedStatusBatchManualDialog.axaml.cs @@ -1,4 +1,3 @@ -using Avalonia.Markup.Xaml; using DataLayer; using System.Collections; using System.Collections.Generic; @@ -34,13 +33,13 @@ namespace LibationAvalonia.Dialogs new liberatedComboBoxItem { Status = LiberatedStatus.NotLiberated, Text = "Not Downloaded" }, }; - public LiberatedStatusBatchManualDialog(bool isPdf) : this() - { - if (isPdf) - this.Title = this.Title.Replace("book", "PDF"); - } + public LiberatedStatusBatchManualDialog(bool isPdf) : this() + { + if (isPdf) + this.Title = this.Title.Replace("book", "PDF"); + } - public LiberatedStatusBatchManualDialog() + public LiberatedStatusBatchManualDialog() { InitializeComponent(); SelectedItem = BookStatuses[0] as liberatedComboBoxItem; diff --git a/Source/LibationAvalonia/Dialogs/LocateAudiobooksDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/LocateAudiobooksDialog.axaml.cs index 7f4752bb..0ebc6166 100644 --- a/Source/LibationAvalonia/Dialogs/LocateAudiobooksDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/LocateAudiobooksDialog.axaml.cs @@ -2,7 +2,6 @@ using ApplicationServices; using Avalonia.Collections; using Avalonia.Controls; using Avalonia.Platform.Storage; -using Avalonia.Platform.Storage.FileIO; using DataLayer; using LibationAvalonia.ViewModels; using LibationFileManager; @@ -53,7 +52,7 @@ namespace LibationAvalonia.Dialogs private void LocateAudiobooks_FileFound(object sender, FilePathCache.CacheEntry e) { - var newItem = new Tuple($"[{e.Id}]", Path.GetFileName(e.Path)); + var newItem = new Tuple($"[{e.Id}]", Path.GetFileName(e.Path)); _viewModel.FoundFiles.Add(newItem); foundAudiobooksLB.SelectedItem = newItem; @@ -70,7 +69,7 @@ namespace LibationAvalonia.Dialogs { Title = "Select the folder to search for audiobooks", 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(); diff --git a/Source/LibationAvalonia/Dialogs/Login/ApprovalNeededDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/Login/ApprovalNeededDialog.axaml.cs index 49f1ef4b..c1f3a4b7 100644 --- a/Source/LibationAvalonia/Dialogs/Login/ApprovalNeededDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/Login/ApprovalNeededDialog.axaml.cs @@ -1,6 +1,3 @@ -using Avalonia; -using Avalonia.Controls; -using Avalonia.Markup.Xaml; using System.Threading.Tasks; namespace LibationAvalonia.Dialogs.Login diff --git a/Source/LibationAvalonia/Dialogs/Login/AvaloniaLoginCallback.cs b/Source/LibationAvalonia/Dialogs/Login/AvaloniaLoginCallback.cs index b7c1f682..e5234d7c 100644 --- a/Source/LibationAvalonia/Dialogs/Login/AvaloniaLoginCallback.cs +++ b/Source/LibationAvalonia/Dialogs/Login/AvaloniaLoginCallback.cs @@ -1,7 +1,6 @@ -using System; -using System.Threading.Tasks; -using AudibleApi; +using AudibleApi; using AudibleUtilities; +using System.Threading.Tasks; namespace LibationAvalonia.Dialogs.Login { diff --git a/Source/LibationAvalonia/Dialogs/Login/AvaloniaLoginChoiceEager.cs b/Source/LibationAvalonia/Dialogs/Login/AvaloniaLoginChoiceEager.cs index 1e0373b1..48bbe611 100644 --- a/Source/LibationAvalonia/Dialogs/Login/AvaloniaLoginChoiceEager.cs +++ b/Source/LibationAvalonia/Dialogs/Login/AvaloniaLoginChoiceEager.cs @@ -1,7 +1,7 @@ -using System; -using System.Threading.Tasks; -using AudibleApi; +using AudibleApi; using AudibleUtilities; +using System; +using System.Threading.Tasks; namespace LibationAvalonia.Dialogs.Login { diff --git a/Source/LibationAvalonia/Dialogs/Login/CaptchaDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/Login/CaptchaDialog.axaml.cs index 92b27485..df681ae3 100644 --- a/Source/LibationAvalonia/Dialogs/Login/CaptchaDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/Login/CaptchaDialog.axaml.cs @@ -1,5 +1,4 @@ using Avalonia.Controls; -using Avalonia.Markup.Xaml; using Avalonia.Media.Imaging; using LibationAvalonia.ViewModels; using ReactiveUI; @@ -21,7 +20,7 @@ namespace LibationAvalonia.Dialogs.Login captchaBox = this.FindControl(nameof(captchaBox)); } - public CaptchaDialog(string password, byte[] captchaImage) :this() + public CaptchaDialog(string password, byte[] captchaImage) : this() { //Avalonia doesn't support animated gifs. //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); - using var clonedFrame = gif.Frames.CloneFrame(i); + using var clonedFrame = gif.Frames.CloneFrame(i); using var framems = new MemoryStream(); clonedFrame.Save(framems, gifEncoder); @@ -66,7 +65,7 @@ namespace LibationAvalonia.Dialogs.Login protected override async Task CancelAndCloseAsync() { await _viewModel.StopAsync(); - await base.CancelAndCloseAsync(); + await base.CancelAndCloseAsync(); } 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) { int index = 0; - while(keepSwitching) + while (keepSwitching) { CaptchaImage = gifFrames[index]; await Task.Delay(frameDelayMs[index++]); diff --git a/Source/LibationAvalonia/Dialogs/Login/LoginCallbackDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/Login/LoginCallbackDialog.axaml.cs index c42526d3..35ed88b5 100644 --- a/Source/LibationAvalonia/Dialogs/Login/LoginCallbackDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/Login/LoginCallbackDialog.axaml.cs @@ -1,7 +1,5 @@ using AudibleUtilities; -using Avalonia; using Avalonia.Controls; -using Avalonia.Markup.Xaml; using Dinah.Core; using System.Linq; using System.Threading.Tasks; diff --git a/Source/LibationAvalonia/Dialogs/Login/LoginChoiceEagerDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/Login/LoginChoiceEagerDialog.axaml.cs index a2d4311d..861f1cb4 100644 --- a/Source/LibationAvalonia/Dialogs/Login/LoginChoiceEagerDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/Login/LoginChoiceEagerDialog.axaml.cs @@ -1,8 +1,6 @@ using AudibleApi; using AudibleUtilities; -using Avalonia; using Avalonia.Controls; -using Avalonia.Markup.Xaml; using System.Linq; using System.Threading.Tasks; @@ -26,7 +24,7 @@ namespace LibationAvalonia.Dialogs.Login DataContext = this; } } - public LoginChoiceEagerDialog(Account account):this() + public LoginChoiceEagerDialog(Account account) : this() { Account = account; DataContext = this; @@ -34,7 +32,7 @@ namespace LibationAvalonia.Dialogs.Login 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"); return; diff --git a/Source/LibationAvalonia/Dialogs/Login/LoginExternalDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/Login/LoginExternalDialog.axaml.cs index 2435f477..82404e09 100644 --- a/Source/LibationAvalonia/Dialogs/Login/LoginExternalDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/Login/LoginExternalDialog.axaml.cs @@ -1,7 +1,6 @@ using AudibleUtilities; using Avalonia; using Avalonia.Controls; -using Avalonia.Markup.Xaml; using Dinah.Core; using System; using System.Linq; @@ -28,7 +27,7 @@ namespace LibationAvalonia.Dialogs.Login DataContext = this; } } - public LoginExternalDialog(Account account, string loginUrl):this() + public LoginExternalDialog(Account account, string loginUrl) : this() { Account = account; ExternalLoginUrl = loginUrl; diff --git a/Source/LibationAvalonia/Dialogs/Login/MfaDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/Login/MfaDialog.axaml.cs index 6b492268..e0c4ad34 100644 --- a/Source/LibationAvalonia/Dialogs/Login/MfaDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/Login/MfaDialog.axaml.cs @@ -1,11 +1,10 @@ using Avalonia; using Avalonia.Controls; -using Avalonia.Markup.Xaml; -using System.Threading.Tasks; +using Avalonia.Data; using ReactiveUI; using System.Collections.Generic; using System.Linq; -using Avalonia.Data; +using System.Threading.Tasks; namespace LibationAvalonia.Dialogs.Login { diff --git a/Source/LibationAvalonia/Dialogs/Login/_2faCodeDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/Login/_2faCodeDialog.axaml.cs index fd0c3796..d0cf4736 100644 --- a/Source/LibationAvalonia/Dialogs/Login/_2faCodeDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/Login/_2faCodeDialog.axaml.cs @@ -1,5 +1,4 @@ using Avalonia.Controls; -using Avalonia.Markup.Xaml; using System.Threading.Tasks; namespace LibationAvalonia.Dialogs.Login @@ -12,7 +11,7 @@ namespace LibationAvalonia.Dialogs.Login public _2faCodeDialog() { - AvaloniaXamlLoader.Load(this); + InitializeComponent(); _2FABox = this.FindControl(nameof(_2FABox)); } diff --git a/Source/LibationAvalonia/Dialogs/MessageBoxAlertAdminDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/MessageBoxAlertAdminDialog.axaml.cs index 9837ab00..85d04f5c 100644 --- a/Source/LibationAvalonia/Dialogs/MessageBoxAlertAdminDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/MessageBoxAlertAdminDialog.axaml.cs @@ -1,5 +1,4 @@ using Avalonia.Controls; -using Avalonia.Markup.Xaml; using Dinah.Core; using FileManager; using System; diff --git a/Source/LibationAvalonia/Dialogs/MessageBoxWindow.axaml.cs b/Source/LibationAvalonia/Dialogs/MessageBoxWindow.axaml.cs index 18c153b7..c1cbb009 100644 --- a/Source/LibationAvalonia/Dialogs/MessageBoxWindow.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/MessageBoxWindow.axaml.cs @@ -1,6 +1,3 @@ -using Avalonia; -using Avalonia.Controls; -using Avalonia.Markup.Xaml; using LibationAvalonia.ViewModels.Dialogs; namespace LibationAvalonia.Dialogs @@ -12,7 +9,7 @@ namespace LibationAvalonia.Dialogs { InitializeComponent(); } - public MessageBoxWindow(bool saveAndRestorePosition):base(saveAndRestorePosition) + public MessageBoxWindow(bool saveAndRestorePosition) : base(saveAndRestorePosition) { InitializeComponent(); } diff --git a/Source/LibationAvalonia/Dialogs/ScanAccountsDialog.axaml b/Source/LibationAvalonia/Dialogs/ScanAccountsDialog.axaml index a7cf73a3..2c67cec8 100644 --- a/Source/LibationAvalonia/Dialogs/ScanAccountsDialog.axaml +++ b/Source/LibationAvalonia/Dialogs/ScanAccountsDialog.axaml @@ -5,7 +5,7 @@ mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="185" x:Class="LibationAvalonia.Dialogs.ScanAccountsDialog" MinWidth="500" MinHeight="160" - MaxWidth="500" MaxHeight="185" + Width="500" Height="200" Title="Which Accounts?" WindowStartupLocation="CenterOwner" Icon="/Assets/libation.ico"> diff --git a/Source/LibationAvalonia/Dialogs/ScanAccountsDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/ScanAccountsDialog.axaml.cs index 8748fe0b..2e62c3a6 100644 --- a/Source/LibationAvalonia/Dialogs/ScanAccountsDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/ScanAccountsDialog.axaml.cs @@ -1,12 +1,9 @@ using AudibleUtilities; -using Avalonia; using Avalonia.Controls; using Avalonia.Interactivity; -using Avalonia.Markup.Xaml; using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; namespace LibationAvalonia.Dialogs { diff --git a/Source/LibationAvalonia/Dialogs/SearchSyntaxDialog.axaml b/Source/LibationAvalonia/Dialogs/SearchSyntaxDialog.axaml index 0e769a26..ccadfc6f 100644 --- a/Source/LibationAvalonia/Dialogs/SearchSyntaxDialog.axaml +++ b/Source/LibationAvalonia/Dialogs/SearchSyntaxDialog.axaml @@ -5,6 +5,7 @@ mc:Ignorable="d" d:DesignWidth="950" d:DesignHeight="650" MinWidth="950" MinHeight="650" MaxWidth="950" MaxHeight="650" + Width="950" Height="650" x:Class="LibationAvalonia.Dialogs.SearchSyntaxDialog" Title="Filter Options" WindowStartupLocation="CenterOwner" diff --git a/Source/LibationAvalonia/Dialogs/SearchSyntaxDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/SearchSyntaxDialog.axaml.cs index 58f76533..4c3c45f9 100644 --- a/Source/LibationAvalonia/Dialogs/SearchSyntaxDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/SearchSyntaxDialog.axaml.cs @@ -1,5 +1,3 @@ -using Avalonia.Markup.Xaml; - namespace LibationAvalonia.Dialogs { public partial class SearchSyntaxDialog : DialogWindow diff --git a/Source/LibationAvalonia/Dialogs/SettingsDialog.axaml b/Source/LibationAvalonia/Dialogs/SettingsDialog.axaml index d984dc3b..e7771a78 100644 --- a/Source/LibationAvalonia/Dialogs/SettingsDialog.axaml +++ b/Source/LibationAvalonia/Dialogs/SettingsDialog.axaml @@ -4,6 +4,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="900" d:DesignHeight="750" MinWidth="900" MinHeight="700" + Width="900" Height="700" x:Class="LibationAvalonia.Dialogs.SettingsDialog" xmlns:controls="clr-namespace:LibationAvalonia.Controls" Title="Edit Settings" @@ -20,7 +21,7 @@ Content="Save" Click="SaveButton_Clicked" /> - + - - - - - - - @@ -121,19 +115,20 @@ - + - - + + + @@ -160,8 +155,8 @@ - - - + diff --git a/Source/LibationAvalonia/Views/ProcessBookControl.axaml.cs b/Source/LibationAvalonia/Views/ProcessBookControl.axaml.cs index ccbdbe43..f7ccc4d0 100644 --- a/Source/LibationAvalonia/Views/ProcessBookControl.axaml.cs +++ b/Source/LibationAvalonia/Views/ProcessBookControl.axaml.cs @@ -1,9 +1,7 @@ -using System; -using Avalonia.Controls; -using Avalonia.Markup.Xaml; -using LibationAvalonia.ViewModels; using ApplicationServices; +using Avalonia.Controls; using DataLayer; +using LibationAvalonia.ViewModels; using LibationUiBase; namespace LibationAvalonia.Views diff --git a/Source/LibationAvalonia/Views/ProcessQueueControl.axaml.cs b/Source/LibationAvalonia/Views/ProcessQueueControl.axaml.cs index 51f25bab..70c95408 100644 --- a/Source/LibationAvalonia/Views/ProcessQueueControl.axaml.cs +++ b/Source/LibationAvalonia/Views/ProcessQueueControl.axaml.cs @@ -2,7 +2,6 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Data.Converters; -using Avalonia.Markup.Xaml; using DataLayer; using LibationAvalonia.ViewModels; using LibationUiBase; @@ -14,7 +13,7 @@ using System.Linq; namespace LibationAvalonia.Views { public partial class ProcessQueueControl : UserControl - { + { private TrackedQueue Queue => _viewModel.Queue; private ProcessQueueViewModel _viewModel => DataContext as ProcessQueueViewModel; diff --git a/Source/LibationAvalonia/Views/ProductsDisplay.axaml b/Source/LibationAvalonia/Views/ProductsDisplay.axaml index 38a6b59d..62207098 100644 --- a/Source/LibationAvalonia/Views/ProductsDisplay.axaml +++ b/Source/LibationAvalonia/Views/ProductsDisplay.axaml @@ -3,6 +3,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:views="clr-namespace:LibationAvalonia.Views" + xmlns:uibase="clr-namespace:LibationUiBase.GridView;assembly=LibationUiBase" xmlns:controls="clr-namespace:LibationAvalonia.Controls" mc:Ignorable="d" d:DesignWidth="1560" d:DesignHeight="400" x:Class="LibationAvalonia.Views.ProductsDisplay"> @@ -37,6 +38,14 @@ + + @@ -51,25 +60,25 @@ Width="75"> - + + IsChecked="{CompiledBinding Remove, Mode=TwoWay}" /> - + @@ -77,17 +86,17 @@ - - + + - - - + + + @@ -95,9 +104,9 @@ - - - + + + @@ -105,9 +114,9 @@ - - - + + + @@ -115,9 +124,9 @@ - - - + + + @@ -125,9 +134,19 @@ - - - + + + + + + + + + + + + + @@ -135,9 +154,9 @@ - - - + + + @@ -145,49 +164,51 @@ - - - + + + + OpacityBinding="{CompiledBinding Liberate.Opacity}" + BackgroundBinding="{CompiledBinding Liberate.BackgroundBrush}" + ClipboardContentBinding="{CompiledBinding ProductRating}" + Binding="{CompiledBinding ProductRating}" /> - - - + + + + OpacityBinding="{CompiledBinding Liberate.Opacity}" + BackgroundBinding="{CompiledBinding Liberate.BackgroundBrush}" + ClipboardContentBinding="{CompiledBinding MyRating}" + Binding="{CompiledBinding MyRating, Mode=TwoWay}" /> - - - + + + @@ -195,9 +216,9 @@ - - - + + + @@ -205,13 +226,13 @@ - - diff --git a/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs b/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs index 8f29b4ca..51860b77 100644 --- a/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs +++ b/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs @@ -1,7 +1,6 @@ using ApplicationServices; using Avalonia; using Avalonia.Controls; -using Avalonia.Markup.Xaml; using Avalonia.Platform.Storage; using DataLayer; using FileLiberator; @@ -256,7 +255,7 @@ namespace LibationAvalonia.Views contextMenu.MenuClosed += ContextMenu_MenuClosed; contextMenu.ContextMenuOpening += ContextMenu_ContextMenuOpening; List menuItems = new(); - contextMenu.Items = menuItems; + contextMenu.ItemsSource = menuItems; menuItems.Add(new MenuItem { Header = "Show / Hide Columns" }); menuItems.Add(new MenuItem { Header = "-" }); @@ -274,13 +273,9 @@ namespace LibationAvalonia.Views ( new MenuItem { - Header = ((string)column.Header).Replace((char)0xa, ' '), + Header = ((string)column.Header).Replace('\n', ' '), Tag = column, - Margin = new Thickness(6, 0), - Icon = new CheckBox - { - Width = 50, - } + Icon = new CheckBox(), } ); @@ -419,7 +414,9 @@ namespace LibationAvalonia.Views if (!isDefault) PictureStorage.PictureCached -= PictureCached; - if (!imageDisplayDialog.IsVisible) + if (imageDisplayDialog.IsVisible) + imageDisplayDialog.Activate(); + else imageDisplayDialog.Show(); } diff --git a/Source/LibationAvalonia/Views/SeriesViewDialog.axaml.cs b/Source/LibationAvalonia/Views/SeriesViewDialog.axaml.cs index f80e0991..a337c349 100644 --- a/Source/LibationAvalonia/Views/SeriesViewDialog.axaml.cs +++ b/Source/LibationAvalonia/Views/SeriesViewDialog.axaml.cs @@ -1,16 +1,11 @@ -using AudibleApi.Common; -using AudibleApi; +using Avalonia.Collections; using Avalonia.Controls; +using Avalonia.Media; using DataLayer; using Dinah.Core; -using FileLiberator; -using System.Collections.Generic; -using System.Linq; -using Avalonia.Collections; using LibationAvalonia.Dialogs; using LibationUiBase.SeriesView; using System; -using Avalonia.Media; namespace LibationAvalonia.Views { diff --git a/Source/LibationAvalonia/Views/SeriesViewGrid.axaml b/Source/LibationAvalonia/Views/SeriesViewGrid.axaml index 712eb24e..ad7d3a91 100644 --- a/Source/LibationAvalonia/Views/SeriesViewGrid.axaml +++ b/Source/LibationAvalonia/Views/SeriesViewGrid.axaml @@ -4,6 +4,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" xmlns:controls="clr-namespace:LibationAvalonia.Controls" + xmlns:uibase="clr-namespace:LibationUiBase.SeriesView;assembly=LibationUiBase" x:Class="LibationAvalonia.Views.SeriesViewGrid"> - + @@ -46,10 +47,10 @@ - + @@ -59,7 +60,7 @@ - +