diff --git a/Source/AaxDecrypter/AaxcDownloadMultiConverter.cs b/Source/AaxDecrypter/AaxcDownloadMultiConverter.cs index bc21b0ea..5589ebe1 100644 --- a/Source/AaxDecrypter/AaxcDownloadMultiConverter.cs +++ b/Source/AaxDecrypter/AaxcDownloadMultiConverter.cs @@ -25,12 +25,19 @@ namespace AaxDecrypter { //Finishing configuring lame encoder. if (DownloadOptions.OutputFormat == OutputFormat.Mp3) + { + if (AaxFile is null) + throw new InvalidOperationException($"AaxFile is null during {nameof(OnInitialized)} in {nameof(AaxcDownloadConvertBase)}."); + if (DownloadOptions.LameConfig is null) + throw new InvalidOperationException($"LameConfig is null during {nameof(OnInitialized)} in {nameof(DownloadOptions)}."); + MpegUtil.ConfigureLameOptions( AaxFile, DownloadOptions.LameConfig, DownloadOptions.Downsample, DownloadOptions.MatchSourceBitrate, chapters: null); + } } /* diff --git a/Source/AaxDecrypter/AaxcDownloadSingleConverter.cs b/Source/AaxDecrypter/AaxcDownloadSingleConverter.cs index aa957746..73c089f1 100644 --- a/Source/AaxDecrypter/AaxcDownloadSingleConverter.cs +++ b/Source/AaxDecrypter/AaxcDownloadSingleConverter.cs @@ -31,12 +31,19 @@ namespace AaxDecrypter { //Finishing configuring lame encoder. if (DownloadOptions.OutputFormat == OutputFormat.Mp3) + { + if (AaxFile is null) + throw new InvalidOperationException($"AaxFile is null during {nameof(OnInitialized)} in {nameof(AaxcDownloadConvertBase)}."); + if (DownloadOptions.LameConfig is null) + throw new InvalidOperationException($"LameConfig is null during {nameof(OnInitialized)} in {nameof(DownloadOptions)}."); + MpegUtil.ConfigureLameOptions( AaxFile, DownloadOptions.LameConfig, DownloadOptions.Downsample, DownloadOptions.MatchSourceBitrate, DownloadOptions.ChapterInfo); + } } protected async override Task Step_DownloadAndDecryptAudiobookAsync() diff --git a/Source/AaxDecrypter/MpegUtil.cs b/Source/AaxDecrypter/MpegUtil.cs index be051b34..04837c3f 100644 --- a/Source/AaxDecrypter/MpegUtil.cs +++ b/Source/AaxDecrypter/MpegUtil.cs @@ -15,7 +15,7 @@ namespace AaxDecrypter LameConfig lameConfig, bool downsample, bool matchSourceBitrate, - ChapterInfo chapters) + ChapterInfo? chapters) { double bitrateMultiple = 1; diff --git a/Source/ApplicationServices/LibraryCommands.cs b/Source/ApplicationServices/LibraryCommands.cs index aff6c629..adb0517c 100644 --- a/Source/ApplicationServices/LibraryCommands.cs +++ b/Source/ApplicationServices/LibraryCommands.cs @@ -376,8 +376,8 @@ namespace ApplicationServices #endregion #region remove/restore books - public static Task RemoveBooksAsync(this IEnumerable idsToRemove) => Task.Run(() => removeBooks(idsToRemove)); - private static int removeBooks(IEnumerable removeLibraryBooks) + public static Task RemoveBooksAsync(this IEnumerable? idsToRemove) => Task.Run(() => removeBooks(idsToRemove)); + private static int removeBooks(IEnumerable? removeLibraryBooks) { if (removeLibraryBooks is null || !removeLibraryBooks.Any()) return 0; @@ -385,7 +385,7 @@ namespace ApplicationServices return DoDbSizeChangeOperation(ctx => { // Entry() NoTracking entities before SaveChanges() - foreach (var lb in removeLibraryBooks) + foreach (var lb in removeLibraryBooks.OfType()) { lb.IsDeleted = true; ctx.Entry(lb).State = Microsoft.EntityFrameworkCore.EntityState.Modified; @@ -417,8 +417,8 @@ namespace ApplicationServices } } - public static Task PermanentlyDeleteBooksAsync(this IEnumerable idsToRemove) => Task.Run(() => permanentlyDeleteBooks(idsToRemove)); - private static int permanentlyDeleteBooks(this IEnumerable libraryBooks) + public static Task PermanentlyDeleteBooksAsync(this IEnumerable? idsToRemove) => Task.Run(() => permanentlyDeleteBooks(idsToRemove)); + private static int permanentlyDeleteBooks(this IEnumerable? libraryBooks) { if (libraryBooks is null || !libraryBooks.Any()) return 0; @@ -426,8 +426,8 @@ namespace ApplicationServices { return DoDbSizeChangeOperation(ctx => { - ctx.LibraryBooks.RemoveRange(libraryBooks); - ctx.Books.RemoveRange(libraryBooks.Select(lb => lb.Book)); + ctx.LibraryBooks.RemoveRange(libraryBooks.OfType()); + ctx.Books.RemoveRange(libraryBooks.OfType().Select(lb => lb.Book)); }); } catch (Exception ex) @@ -514,7 +514,7 @@ namespace ApplicationServices udi.UpdateRating(rating.OverallRating, rating.PerformanceRating, rating.StoryRating); }); - public static async Task UpdateBookStatusAsync(this LibraryBook lb, LiberatedStatus bookStatus, Version? libationVersion, AudioFormat audioFormat, string audioVersion) + public static async Task UpdateBookStatusAsync(this LibraryBook lb, LiberatedStatus bookStatus, Version? libationVersion, AudioFormat? audioFormat, string audioVersion) => await lb.UpdateUserDefinedItemAsync(udi => { udi.BookStatus = bookStatus; udi.SetLastDownloaded(libationVersion, audioFormat, audioVersion); }); public static async Task UpdateBookStatusAsync(this LibraryBook libraryBook, LiberatedStatus bookStatus) @@ -529,27 +529,31 @@ namespace ApplicationServices public static async Task UpdateTagsAsync(this LibraryBook libraryBook, string tags) => await libraryBook.UpdateUserDefinedItemAsync(udi => udi.Tags = tags); - public static async Task UpdateTagsAsync(this IEnumerable libraryBooks, string tags) - => await libraryBooks.UpdateUserDefinedItemAsync(udi => udi.Tags = tags); + public static async Task UpdateTagsAsync(this IEnumerable libraryBooks, string? tags) + => await libraryBooks.UpdateUserDefinedItemAsync(udi => udi.Tags = tags ?? string.Empty); public static async Task UpdateUserDefinedItemAsync(this LibraryBook libraryBook, Action action) => await UpdateUserDefinedItemAsync([libraryBook], action); - public static Task UpdateUserDefinedItemAsync(this IEnumerable libraryBooks, Action action) + public static Task UpdateUserDefinedItemAsync(this IEnumerable? libraryBooks, Action action) => Task.Run(() => libraryBooks.updateUserDefinedItem(action)); - private static int updateUserDefinedItem(this IEnumerable libraryBooks, Action action) + private static int updateUserDefinedItem(this IEnumerable? libraryBooks, Action action) { try { if (libraryBooks is null || !libraryBooks.Any()) return 0; - int qtyChanges; + var nonNullBooks = libraryBooks.OfType(); + if (!nonNullBooks.Any()) + return 0; + + int qtyChanges; using (var context = DbContexts.GetContext()) { // Entry() instead of Attach() due to possible stack overflow with large tables - foreach (var book in libraryBooks) + foreach (var book in nonNullBooks) { action?.Invoke(book.Book.UserDefinedItem); @@ -563,7 +567,7 @@ namespace ApplicationServices qtyChanges = context.SaveChanges(); } if (qtyChanges > 0) - BookUserDefinedItemCommitted?.Invoke(null, libraryBooks); + BookUserDefinedItemCommitted?.Invoke(null, nonNullBooks); return qtyChanges; } diff --git a/Source/AudibleUtilities/AccountsSettings.cs b/Source/AudibleUtilities/AccountsSettings.cs index bdef1580..8d7c4ae7 100644 --- a/Source/AudibleUtilities/AccountsSettings.cs +++ b/Source/AudibleUtilities/AccountsSettings.cs @@ -79,7 +79,7 @@ namespace AudibleUtilities // more common naming convention alias for internal collection public IReadOnlyList GetAll() => Accounts; - public Account Upsert(string accountId, string locale) + public Account Upsert(string accountId, string? locale) { var acct = GetAccount(accountId, locale); diff --git a/Source/DataLayer/EfClasses/Rating.cs b/Source/DataLayer/EfClasses/Rating.cs index 656da1b0..f63f8f35 100644 --- a/Source/DataLayer/EfClasses/Rating.cs +++ b/Source/DataLayer/EfClasses/Rating.cs @@ -1,11 +1,10 @@ using System; -using System.Collections.Generic; -using Dinah.Core; +#nullable enable namespace DataLayer { /// Parameterless ctor and setters should be used by EF only. Everything else should treat it as immutable - public class Rating : ValueObject_Static, IComparable, IComparable + public record Rating : IComparable, IComparable { public float OverallRating { get; private set; } public float PerformanceRating { get; private set; } @@ -31,23 +30,17 @@ namespace DataLayer StoryRating = storyRating; } - protected override IEnumerable GetEqualityComponents() - { - yield return OverallRating; - yield return PerformanceRating; - yield return StoryRating; - } - public override string ToString() => $"Overall={OverallRating} Perf={PerformanceRating} Story={StoryRating}"; - public int CompareTo(Rating other) + public int CompareTo(Rating? other) { - var compare = OverallRating.CompareTo(other.OverallRating); + if (other is null) return 1; + var compare = OverallRating.CompareTo(other.OverallRating); if (compare != 0) return compare; compare = PerformanceRating.CompareTo(other.PerformanceRating); if (compare != 0) return compare; return StoryRating.CompareTo(other.StoryRating); } - public int CompareTo(object obj) => obj is Rating second ? CompareTo(second) : -1; + public int CompareTo(object? obj) => obj is Rating second ? CompareTo(second) : 1; } } diff --git a/Source/DataLayer/EfClasses/UserDefinedItem.cs b/Source/DataLayer/EfClasses/UserDefinedItem.cs index 7aa977f0..db0c4188 100644 --- a/Source/DataLayer/EfClasses/UserDefinedItem.cs +++ b/Source/DataLayer/EfClasses/UserDefinedItem.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text.RegularExpressions; using Dinah.Core; +#nullable enable namespace DataLayer { /// @@ -31,17 +32,17 @@ namespace DataLayer /// /// Version of Libation used the last time the audio file was downloaded. /// - public Version LastDownloadedVersion { get; private set; } + public Version? LastDownloadedVersion { get; private set; } /// /// Audio format of the last downloaded audio file. /// - public AudioFormat LastDownloadedFormat { get; private set; } + public AudioFormat? LastDownloadedFormat { get; private set; } /// /// Version of the audio file that was last downloaded. /// - public string LastDownloadedFileVersion { get; private set; } + public string? LastDownloadedFileVersion { get; private set; } - public void SetLastDownloaded(Version libationVersion, AudioFormat audioFormat, string audioVersion) + public void SetLastDownloaded(Version? libationVersion, AudioFormat? audioFormat, string? audioVersion) { if (LastDownloadedVersion != libationVersion) { @@ -71,9 +72,13 @@ namespace DataLayer OnItemChanged(nameof(LastDownloaded)); } } + private UserDefinedItem() + { + // for EF + Book = null!; + } - private UserDefinedItem() { } - internal UserDefinedItem(Book book) + internal UserDefinedItem(Book book) { ArgumentValidator.EnsureNotNull(book, nameof(book)); Book = book; @@ -162,7 +167,7 @@ namespace DataLayer /// Occurs when , , or values change. /// This signals the change of the in-memory value; it does not ensure that the new value has been persisted. /// - public static event EventHandler ItemChanged; + public static event EventHandler? ItemChanged; private void OnItemChanged(string e) { diff --git a/Source/FileManager/NamingTemplate/NamingTemplate.cs b/Source/FileManager/NamingTemplate/NamingTemplate.cs index f6beaba1..e7212ef2 100644 --- a/Source/FileManager/NamingTemplate/NamingTemplate.cs +++ b/Source/FileManager/NamingTemplate/NamingTemplate.cs @@ -51,7 +51,7 @@ public class NamingTemplate /// The template string to parse /// A collection of with /// properties registered to match to the - public static NamingTemplate Parse(string template, IEnumerable tagCollections) + public static NamingTemplate Parse(string? template, IEnumerable tagCollections) { var namingTemplate = new NamingTemplate(tagCollections); try diff --git a/Source/HangoverAvalonia/App.axaml b/Source/HangoverAvalonia/App.axaml index 6908440c..696a7bd1 100644 --- a/Source/HangoverAvalonia/App.axaml +++ b/Source/HangoverAvalonia/App.axaml @@ -2,9 +2,6 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:HangoverAvalonia" x:Class="HangoverAvalonia.App"> - - - diff --git a/Source/HangoverAvalonia/ViewLocator.cs b/Source/HangoverAvalonia/ViewLocator.cs deleted file mode 100644 index 8756c78d..00000000 --- a/Source/HangoverAvalonia/ViewLocator.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Avalonia.Controls; -using Avalonia.Controls.Templates; -using HangoverAvalonia.ViewModels; -using System; - -namespace HangoverAvalonia -{ - public class ViewLocator : IDataTemplate - { - public Control Build(object data) - { - var name = data.GetType().FullName!.Replace("ViewModel", "View"); - var type = Type.GetType(name); - - if (type != null) - { - return (Control)Activator.CreateInstance(type)!; - } - else - { - return new TextBlock { Text = "Not Found: " + name }; - } - } - - public bool Match(object data) - { - return data is ViewModelBase; - } - } -} diff --git a/Source/LibationAvalonia/App.axaml b/Source/LibationAvalonia/App.axaml index 75763c08..ebd9b60c 100644 --- a/Source/LibationAvalonia/App.axaml +++ b/Source/LibationAvalonia/App.axaml @@ -6,10 +6,6 @@ x:Class="LibationAvalonia.App" Name="Libation"> - - - - diff --git a/Source/LibationAvalonia/App.axaml.cs b/Source/LibationAvalonia/App.axaml.cs index 398a89df..c7b65a69 100644 --- a/Source/LibationAvalonia/App.axaml.cs +++ b/Source/LibationAvalonia/App.axaml.cs @@ -21,7 +21,6 @@ using System.IO; using System.Linq; using System.Threading.Tasks; -#nullable enable namespace LibationAvalonia; public class App : Application diff --git a/Source/LibationAvalonia/AvaloniaUtils.cs b/Source/LibationAvalonia/AvaloniaUtils.cs index 82a360ba..f2f5d8c2 100644 --- a/Source/LibationAvalonia/AvaloniaUtils.cs +++ b/Source/LibationAvalonia/AvaloniaUtils.cs @@ -5,9 +5,9 @@ using Avalonia.Media.Imaging; using Avalonia.VisualTree; using LibationFileManager; using LibationUiBase.Forms; +using System; using System.Threading.Tasks; -#nullable enable namespace LibationAvalonia { internal static class AvaloniaUtils @@ -23,7 +23,9 @@ namespace LibationAvalonia ? dialogWindow.ShowDialog(window) : Task.FromResult(DialogResult.None); - public static Window? GetParentWindow(this Control control) => control.GetVisualRoot() as Window; + public static Window GetParentWindow(this Control control) + => control.GetVisualRoot() as Window ?? App.MainWindow + ?? throw new InvalidOperationException("Cannot find parent window."); private static Bitmap? defaultImage; diff --git a/Source/LibationAvalonia/Controls/CheckedListBox.axaml.cs b/Source/LibationAvalonia/Controls/CheckedListBox.axaml.cs index ce8ea352..956a20bd 100644 --- a/Source/LibationAvalonia/Controls/CheckedListBox.axaml.cs +++ b/Source/LibationAvalonia/Controls/CheckedListBox.axaml.cs @@ -22,6 +22,6 @@ namespace LibationAvalonia.Controls public class CheckBoxViewModel : ViewModelBase { public bool IsChecked { get => field; set => this.RaiseAndSetIfChanged(ref field, value); } - public object Item { get => field; set => this.RaiseAndSetIfChanged(ref field, value); } + public object? Item { get => field; set => this.RaiseAndSetIfChanged(ref field, value); } } } diff --git a/Source/LibationAvalonia/Controls/DataGridCheckBoxColumnExt.cs b/Source/LibationAvalonia/Controls/DataGridCheckBoxColumnExt.cs index d4caadfc..df94bce5 100644 --- a/Source/LibationAvalonia/Controls/DataGridCheckBoxColumnExt.cs +++ b/Source/LibationAvalonia/Controls/DataGridCheckBoxColumnExt.cs @@ -5,11 +5,11 @@ namespace LibationAvalonia.Controls { public class DataGridCheckBoxColumnExt : DataGridCheckBoxColumn { - protected override Control GenerateEditingElementDirect(DataGridCell cell, object dataItem) + protected override Control? GenerateEditingElementDirect(DataGridCell cell, object dataItem) { //Only SeriesEntry types have three-state checks, individual LibraryEntry books are binary. var ele = base.GenerateEditingElementDirect(cell, dataItem) as CheckBox; - ele.IsThreeState = dataItem is SeriesEntry; + ele?.IsThreeState = dataItem is SeriesEntry; return ele; } } diff --git a/Source/LibationAvalonia/Controls/DataGridContextMenus.cs b/Source/LibationAvalonia/Controls/DataGridContextMenus.cs index 10b6c099..4e0a7b2d 100644 --- a/Source/LibationAvalonia/Controls/DataGridContextMenus.cs +++ b/Source/LibationAvalonia/Controls/DataGridContextMenus.cs @@ -9,17 +9,19 @@ namespace LibationAvalonia.Controls { internal static class DataGridContextMenus { - public static event EventHandler CellContextMenuStripNeeded; + public static event EventHandler? CellContextMenuStripNeeded; private static readonly ContextMenu ContextMenu = new(); - private static readonly AvaloniaList MenuItems = new(); + public static readonly AvaloniaList MenuItems = new(); private static readonly PropertyInfo OwningColumnProperty; private static readonly PropertyInfo OwningGridProperty; static DataGridContextMenus() { ContextMenu.ItemsSource = MenuItems; - OwningColumnProperty = typeof(DataGridCell).GetProperty("OwningColumn", BindingFlags.Instance | BindingFlags.NonPublic); - OwningGridProperty = typeof(DataGridColumn).GetProperty("OwningGrid", BindingFlags.Instance | BindingFlags.NonPublic); + OwningColumnProperty = typeof(DataGridCell).GetProperty("OwningColumn", BindingFlags.Instance | BindingFlags.NonPublic) + ?? throw new InvalidOperationException("Could not find OwningColumn property on DataGridCell"); + OwningGridProperty = typeof(DataGridColumn).GetProperty("OwningGrid", BindingFlags.Instance | BindingFlags.NonPublic) + ?? throw new InvalidOperationException("Could not find OwningGrid property on DataGridColumn"); } public static void AttachContextMenu(this DataGridCell cell) @@ -31,7 +33,7 @@ namespace LibationAvalonia.Controls } } - private static void Cell_ContextRequested(object sender, ContextRequestedEventArgs e) + private static void Cell_ContextRequested(object? sender, ContextRequestedEventArgs e) { if (sender is DataGridCell cell && cell.DataContext is GridEntry clickedEntry && @@ -74,7 +76,8 @@ namespace LibationAvalonia.Controls private static readonly MethodInfo GetCellValueMethod; static DataGridCellContextMenuStripNeededEventArgs() { - GetCellValueMethod = typeof(DataGridColumn).GetMethod("GetCellValue", BindingFlags.NonPublic | BindingFlags.Instance); + GetCellValueMethod = typeof(DataGridColumn).GetMethod("GetCellValue", BindingFlags.NonPublic | BindingFlags.Instance) + ?? throw new InvalidOperationException("Could not find GetCellValue method on DataGridColumn"); } private static string GetCellValue(DataGridColumn column, object item) @@ -96,7 +99,7 @@ namespace LibationAvalonia.Controls Grid.Columns .Where(c => c.IsVisible) .OrderBy(c => c.DisplayIndex) - .Select(c => RemoveLineBreaks(c.Header.ToString()))); + .Select(c => RemoveLineBreaks(c.Header.ToString() ?? ""))); private static string RemoveLineBreaks(string text) => text.Replace("\r\n", "").Replace('\r', ' ').Replace('\n', ' '); @@ -111,7 +114,6 @@ namespace LibationAvalonia.Controls public required DataGridColumn Column { get; init; } public required GridEntry[] GridEntries { get; init; } public required ContextMenu ContextMenu { get; init; } - public AvaloniaList ContextMenuItems - => ContextMenu.ItemsSource as AvaloniaList; + public AvaloniaList ContextMenuItems => DataGridContextMenus.MenuItems; } } diff --git a/Source/LibationAvalonia/Controls/DataGridMyRatingColumn.cs b/Source/LibationAvalonia/Controls/DataGridMyRatingColumn.cs index 871d7330..8ff392e7 100644 --- a/Source/LibationAvalonia/Controls/DataGridMyRatingColumn.cs +++ b/Source/LibationAvalonia/Controls/DataGridMyRatingColumn.cs @@ -9,8 +9,8 @@ namespace LibationAvalonia.Controls { public class DataGridMyRatingColumn : DataGridBoundColumn { - [AssignBinding] public IBinding BackgroundBinding { get; set; } - [AssignBinding] public IBinding OpacityBinding { get; set; } + [AssignBinding] public IBinding? BackgroundBinding { get; set; } + [AssignBinding] public IBinding? OpacityBinding { get; set; } private static Rating DefaultRating => new Rating(0, 0, 0); public DataGridMyRatingColumn() { diff --git a/Source/LibationAvalonia/Controls/DirectoryOrCustomSelectControl.axaml.cs b/Source/LibationAvalonia/Controls/DirectoryOrCustomSelectControl.axaml.cs index dc970a9b..2570150c 100644 --- a/Source/LibationAvalonia/Controls/DirectoryOrCustomSelectControl.axaml.cs +++ b/Source/LibationAvalonia/Controls/DirectoryOrCustomSelectControl.axaml.cs @@ -9,7 +9,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -#nullable enable namespace LibationAvalonia.Controls { public partial class DirectoryOrCustomSelectControl : UserControl diff --git a/Source/LibationAvalonia/Controls/DirectorySelectControl.axaml.cs b/Source/LibationAvalonia/Controls/DirectorySelectControl.axaml.cs index 65933290..5789e6ba 100644 --- a/Source/LibationAvalonia/Controls/DirectorySelectControl.axaml.cs +++ b/Source/LibationAvalonia/Controls/DirectorySelectControl.axaml.cs @@ -13,27 +13,27 @@ namespace LibationAvalonia.Controls { public class KnownDirectoryConverter : IValueConverter { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) { if (value is Configuration.KnownDirectories dir) return dir.GetDescription(); return new BindingNotification(new InvalidCastException(), BindingErrorType.Error); } - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) { return new BindingNotification(new InvalidCastException(), BindingErrorType.Error); } } public class KnownDirectoryPath : IMultiValueConverter { - public object Convert(IList values, Type targetType, object parameter, CultureInfo culture) + public object? Convert(IList values, Type targetType, object? parameter, CultureInfo culture) { if (values?.Count == 2 && values[0] is Configuration.KnownDirectories kdir && kdir is not Configuration.KnownDirectories.None) { var subdir = values[1] as string ?? ""; var path = kdir is Configuration.KnownDirectories.AppDir ? Configuration.AppDir_Absolute : Configuration.GetKnownDirectoryPath(kdir); - return Path.Combine(path, subdir); + return path is null ? "" : Path.Combine(path, subdir); } return ""; } diff --git a/Source/LibationAvalonia/Controls/LinkLabel.axaml.cs b/Source/LibationAvalonia/Controls/LinkLabel.axaml.cs index b65ed7b6..19437514 100644 --- a/Source/LibationAvalonia/Controls/LinkLabel.axaml.cs +++ b/Source/LibationAvalonia/Controls/LinkLabel.axaml.cs @@ -58,7 +58,7 @@ namespace LibationAvalonia.Controls Tapped += LinkLabel_Tapped; } - private void LinkLabel_Tapped(object sender, TappedEventArgs e) + private void LinkLabel_Tapped(object? sender, TappedEventArgs e) { Foreground = ForegroundVisited; if (IsEffectivelyEnabled) @@ -87,7 +87,7 @@ namespace LibationAvalonia.Controls } protected override bool IsEnabledCore => base.IsEnabledCore && _commandCanExecute; - protected override void UpdateDataValidation(AvaloniaProperty property, BindingValueType state, Exception error) + protected override void UpdateDataValidation(AvaloniaProperty property, BindingValueType state, Exception? error) { base.UpdateDataValidation(property, state, error); if (property == CommandProperty) diff --git a/Source/LibationAvalonia/Controls/MyRatingCellEditor.axaml.cs b/Source/LibationAvalonia/Controls/MyRatingCellEditor.axaml.cs index a23b960f..393c4070 100644 --- a/Source/LibationAvalonia/Controls/MyRatingCellEditor.axaml.cs +++ b/Source/LibationAvalonia/Controls/MyRatingCellEditor.axaml.cs @@ -61,18 +61,20 @@ namespace LibationAvalonia.Controls public void Panel_PointerExited(object sender, Avalonia.Input.PointerEventArgs e) { - var panel = sender as Panel; + if (sender is not Panel panel) + return; var stackPanel = panel.Children.OfType().Single(); //Restore defaults foreach (TextBlock child in stackPanel.Children) - child.Text = (string)child.Tag; + child.Text = child.Tag as string; } public void Star_PointerEntered(object sender, Avalonia.Input.PointerEventArgs e) { var thisTbox = sender as TextBlock; - var stackPanel = thisTbox.Parent as StackPanel; + if (thisTbox?.Parent is not StackPanel stackPanel) + return; var star = SOLID_STAR; foreach (TextBlock child in stackPanel.Children) @@ -89,7 +91,8 @@ namespace LibationAvalonia.Controls var story = Rating.StoryRating; var thisTbox = sender as TextBlock; - var stackPanel = thisTbox.Parent as StackPanel; + if (thisTbox?.Parent is not StackPanel stackPanel) + return; int newRatingValue = 0; foreach (var tbox in stackPanel.Children) diff --git a/Source/LibationAvalonia/Controls/Settings/Audio.axaml.cs b/Source/LibationAvalonia/Controls/Settings/Audio.axaml.cs index 1aad2411..19ae4482 100644 --- a/Source/LibationAvalonia/Controls/Settings/Audio.axaml.cs +++ b/Source/LibationAvalonia/Controls/Settings/Audio.axaml.cs @@ -5,7 +5,6 @@ using LibationAvalonia.ViewModels.Settings; using LibationFileManager; using LibationFileManager.Templates; using LibationUiBase.Forms; -using ReactiveUI; using System.Linq; using System.Threading.Tasks; @@ -13,7 +12,7 @@ namespace LibationAvalonia.Controls.Settings { public partial class Audio : UserControl { - private AudioSettingsVM _viewModel => DataContext as AudioSettingsVM; + private AudioSettingsVM? _viewModel => DataContext as AudioSettingsVM; public Audio() { InitializeComponent(); @@ -56,12 +55,12 @@ namespace LibationAvalonia.Controls.Settings } } - _viewModel.UseWidevine = false; + _viewModel?.UseWidevine = false; } } else { - _viewModel.Request_xHE_AAC = _viewModel.RequestSpatial = false; + _viewModel?.Request_xHE_AAC = _viewModel.RequestSpatial = false; } } @@ -73,7 +72,7 @@ namespace LibationAvalonia.Controls.Settings _viewModel.ChapterTitleTemplate = newTemplate; } - private async Task editTemplate(ITemplateEditor template) + private async Task editTemplate(ITemplateEditor template) { var form = new EditTemplateDialog(template); if (await form.ShowDialog(this.GetParentWindow()) == DialogResult.OK) diff --git a/Source/LibationAvalonia/Controls/Settings/DownloadDecrypt.axaml.cs b/Source/LibationAvalonia/Controls/Settings/DownloadDecrypt.axaml.cs index aa49d94d..bd1a8cd5 100644 --- a/Source/LibationAvalonia/Controls/Settings/DownloadDecrypt.axaml.cs +++ b/Source/LibationAvalonia/Controls/Settings/DownloadDecrypt.axaml.cs @@ -1,4 +1,5 @@ using Avalonia.Controls; +using FileManager; using LibationAvalonia.Dialogs; using LibationAvalonia.ViewModels.Settings; using LibationFileManager; @@ -10,7 +11,7 @@ namespace LibationAvalonia.Controls.Settings { public partial class DownloadDecrypt : UserControl { - private DownloadDecryptSettingsVM _viewModel => DataContext as DownloadDecryptSettingsVM; + private DownloadDecryptSettingsVM? _viewModel => DataContext as DownloadDecryptSettingsVM; public DownloadDecrypt() { InitializeComponent(); @@ -22,24 +23,24 @@ namespace LibationAvalonia.Controls.Settings public async void EditFolderTemplateButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) { - if (_viewModel is null) return; - var newTemplate = await editTemplate(TemplateEditor.CreateFilenameEditor(_viewModel.Config.Books, _viewModel.FolderTemplate)); + if (_viewModel is null || _viewModel.Config.Books is not LongPath books) return; + var newTemplate = await editTemplate(TemplateEditor.CreateFilenameEditor(books, _viewModel.FolderTemplate)); if (newTemplate is not null) _viewModel.FolderTemplate = newTemplate; } public async void EditFileTemplateButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) { - if (_viewModel is null) return; - var newTemplate = await editTemplate(TemplateEditor.CreateFilenameEditor(_viewModel.Config.Books, _viewModel.FileTemplate)); + if (_viewModel is null || _viewModel.Config.Books is not LongPath books) return; + var newTemplate = await editTemplate(TemplateEditor.CreateFilenameEditor(books, _viewModel.FileTemplate)); if (newTemplate is not null) _viewModel.FileTemplate = newTemplate; } public async void EditChapterFileTemplateButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) { - if (_viewModel is null) return; - var newTemplate = await editTemplate(TemplateEditor.CreateFilenameEditor(_viewModel.Config.Books, _viewModel.ChapterFileTemplate)); + if (_viewModel is null || _viewModel.Config.Books is not LongPath books) return; + var newTemplate = await editTemplate(TemplateEditor.CreateFilenameEditor(books, _viewModel.ChapterFileTemplate)); if (newTemplate is not null) _viewModel.ChapterFileTemplate = newTemplate; } @@ -52,7 +53,7 @@ namespace LibationAvalonia.Controls.Settings } - private async Task editTemplate(ITemplateEditor template) + private async Task editTemplate(ITemplateEditor template) { var form = new EditTemplateDialog(template); if (await form.ShowDialog(this.GetParentWindow()) == DialogResult.OK) diff --git a/Source/LibationAvalonia/Controls/Settings/Important.axaml.cs b/Source/LibationAvalonia/Controls/Settings/Important.axaml.cs index e00c97b0..1e341660 100644 --- a/Source/LibationAvalonia/Controls/Settings/Important.axaml.cs +++ b/Source/LibationAvalonia/Controls/Settings/Important.axaml.cs @@ -1,13 +1,10 @@ using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; -using Dinah.Core; -using FileManager; using LibationAvalonia.Dialogs; using LibationAvalonia.ViewModels.Settings; using LibationFileManager; using System.Linq; -#nullable enable namespace LibationAvalonia.Controls.Settings { public partial class Important : UserControl diff --git a/Source/LibationAvalonia/Controls/ThemePreviewControl.axaml.cs b/Source/LibationAvalonia/Controls/ThemePreviewControl.axaml.cs index ca326e65..b7e75e29 100644 --- a/Source/LibationAvalonia/Controls/ThemePreviewControl.axaml.cs +++ b/Source/LibationAvalonia/Controls/ThemePreviewControl.axaml.cs @@ -43,7 +43,7 @@ public partial class ThemePreviewControl : UserControl QueuedBook.AddDownloadPdf(); WorkingBook.AddDownloadPdf(); - typeof(ProcessBookViewModel).GetProperty(nameof(ProcessBookViewModel.Progress)).SetValue(WorkingBook, 50); + typeof(ProcessBookViewModel).GetProperty(nameof(ProcessBookViewModel.Progress))!.SetValue(WorkingBook, 50); ProductsDisplay = new ProductsDisplayViewModel(); _ = ProductsDisplay.BindToGridAsync(sampleEntries); diff --git a/Source/LibationAvalonia/Dialogs/AboutDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/AboutDialog.axaml.cs index fd2975da..ab43ff6d 100644 --- a/Source/LibationAvalonia/Dialogs/AboutDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/AboutDialog.axaml.cs @@ -26,11 +26,11 @@ namespace LibationAvalonia.Dialogs var mainWindow = Owner as Views.MainWindow; var upgrader = new Upgrader(); - upgrader.DownloadProgress += async (_, e) => await Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(() => mainWindow.ViewModel.DownloadProgress = e.ProgressPercentage); - upgrader.DownloadCompleted += async (_, _) => await Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(() => mainWindow.ViewModel.DownloadProgress = null); + upgrader.DownloadProgress += async (_, e) => await Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(() => mainWindow?.ViewModel?.DownloadProgress = e.ProgressPercentage); + upgrader.DownloadCompleted += async (_, _) => await Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(() => mainWindow?.ViewModel?.DownloadProgress = null); _viewModel.CanCheckForUpgrade = false; - Version latestVersion = null; + Version? latestVersion = null; await upgrader.CheckForUpgradeAsync(OnUpgradeAvailable); _viewModel.CanCheckForUpgrade = latestVersion is null; diff --git a/Source/LibationAvalonia/Dialogs/AccountsDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/AccountsDialog.axaml.cs index 68ee3d97..e9ef7fc5 100644 --- a/Source/LibationAvalonia/Dialogs/AccountsDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/AccountsDialog.axaml.cs @@ -21,7 +21,7 @@ namespace LibationAvalonia.Dialogs { public IReadOnlyList Locales => AccountsDialog.Locales; public bool LibraryScan { get; set; } = true; - public string AccountId + public string? AccountId { get => field; set @@ -31,8 +31,8 @@ namespace LibationAvalonia.Dialogs } } - public Locale SelectedLocale { get; set; } - public string AccountName { get; set; } + public Locale? SelectedLocale { get; set; } + public string? AccountName { get; set; } public bool IsDefault => string.IsNullOrEmpty(AccountId); public AccountDto() { } @@ -65,7 +65,7 @@ namespace LibationAvalonia.Dialogs addBlankAccount(); } - private void Accounts_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + private void Accounts_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) { if (e.Action is NotifyCollectionChangedAction.Add && e.NewItems?.Count > 0) { @@ -81,13 +81,13 @@ namespace LibationAvalonia.Dialogs 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)) 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) Accounts.Remove(acc); @@ -200,9 +200,9 @@ namespace LibationAvalonia.Dialogs } // upsert each. validation occurs through Account and AccountsSettings - foreach (var dto in Accounts) + foreach (var dto in Accounts.Where(a => a.AccountId is not null)) { - var acct = accountsSettings.Upsert(dto.AccountId, dto.SelectedLocale?.Name); + var acct = accountsSettings.Upsert(dto.AccountId!, dto.SelectedLocale?.Name); acct.LibraryScan = dto.LibraryScan; acct.AccountName = string.IsNullOrWhiteSpace(dto.AccountName) diff --git a/Source/LibationAvalonia/Dialogs/BookDetailsDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/BookDetailsDialog.axaml.cs index 7b2e9a51..1b3536ca 100644 --- a/Source/LibationAvalonia/Dialogs/BookDetailsDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/BookDetailsDialog.axaml.cs @@ -17,26 +17,27 @@ namespace LibationAvalonia.Dialogs { public partial class BookDetailsDialog : DialogWindow { - private BookDetailsDialogViewModel _viewModel; - public LibraryBook LibraryBook + private BookDetailsDialogViewModel? _viewModel; + public LibraryBook? LibraryBook { get => field; set { field = value; - Title = field.Book.TitleWithSubtitle; - DataContext = _viewModel = new BookDetailsDialogViewModel(field); + Title = field?.Book.TitleWithSubtitle; + if (field is not null) + DataContext = _viewModel = new BookDetailsDialogViewModel(field); } } - public string NewTags => _viewModel.Tags; - public LiberatedStatus BookLiberatedStatus => _viewModel.BookLiberatedSelectedItem.Status; - public LiberatedStatus? PdfLiberatedStatus => _viewModel.PdfLiberatedSelectedItem?.Status; + public string? NewTags => _viewModel?.Tags; + public LiberatedStatus BookLiberatedStatus => _viewModel?.BookLiberatedSelectedItem?.Status ?? default; + public LiberatedStatus? PdfLiberatedStatus => _viewModel?.PdfLiberatedSelectedItem?.Status; public BookDetailsDialog() { InitializeComponent(); - ControlToFocusOnShow = this.Find(nameof(tagsTbox)); + ControlToFocusOnShow = tagsTbox; if (Design.IsDesignMode) { @@ -60,14 +61,15 @@ namespace LibationAvalonia.Dialogs protected override async Task SaveAndCloseAsync() { - await LibraryBook.UpdateUserDefinedItemAsync(NewTags, bookStatus: BookLiberatedStatus, pdfStatus: PdfLiberatedStatus); + if (LibraryBook is not null) + await LibraryBook.UpdateUserDefinedItemAsync(NewTags, bookStatus: BookLiberatedStatus, pdfStatus: PdfLiberatedStatus); await base.SaveAndCloseAsync(); } public void BookStatus_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (sender is not WheelComboBox { SelectedItem: liberatedComboBoxItem { Status: LiberatedStatus.Error } } && - _viewModel.BookLiberatedItems.SingleOrDefault(s => s.Status == LiberatedStatus.Error) is liberatedComboBoxItem errorItem) + _viewModel?.BookLiberatedItems.SingleOrDefault(s => s.Status == LiberatedStatus.Error) is liberatedComboBoxItem errorItem) { _viewModel.BookLiberatedItems.Remove(errorItem); } @@ -78,8 +80,8 @@ namespace LibationAvalonia.Dialogs public class liberatedComboBoxItem { public LiberatedStatus Status { get; set; } - public string Text { get; set; } - public override string ToString() => Text; + public string? Text { get; set; } + public override string? ToString() => Text; } public class BookDetailsDialogViewModel : ViewModelBase @@ -92,8 +94,8 @@ namespace LibationAvalonia.Dialogs public bool HasPDF => PdfLiberatedItems?.Count > 0; public AvaloniaList BookLiberatedItems { get; } = new(); public List PdfLiberatedItems { get; } = new(); - public liberatedComboBoxItem PdfLiberatedSelectedItem { get; set; } - public liberatedComboBoxItem BookLiberatedSelectedItem { get; set; } + public liberatedComboBoxItem? PdfLiberatedSelectedItem { get; set; } + public liberatedComboBoxItem? BookLiberatedSelectedItem { get; set; } public ICommand OpenInAudibleCommand { get; } public BookDetailsDialogViewModel(LibraryBook libraryBook) diff --git a/Source/LibationAvalonia/Dialogs/BookRecordsDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/BookRecordsDialog.axaml.cs index 5265c99e..2bdb2093 100644 --- a/Source/LibationAvalonia/Dialogs/BookRecordsDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/BookRecordsDialog.axaml.cs @@ -24,6 +24,7 @@ namespace LibationAvalonia.Dialogs public BookRecordsDialog() { InitializeComponent(); + libraryBook = MockLibraryBook.CreateBook(); if (Design.IsDesignMode) { @@ -43,7 +44,7 @@ namespace LibationAvalonia.Dialogs Loaded += BookRecordsDialog_Loaded; } - private async void BookRecordsDialog_Loaded(object sender, Avalonia.Interactivity.RoutedEventArgs e) + private async void BookRecordsDialog_Loaded(object? sender, Avalonia.Interactivity.RoutedEventArgs e) { try { @@ -211,8 +212,8 @@ namespace LibationAvalonia.Dialogs public string Created => Record.Created.ToString(DateFormat); public string Modified => Record is IAnnotation annotation ? annotation.Created.ToString(DateFormat) : string.Empty; public string End => Record is IRangeAnnotation range ? formatTimeSpan(range.End) : string.Empty; - public string Note => Record is IRangeAnnotation range ? range.Text : string.Empty; - public string Title => Record is Clip range ? range.Title : string.Empty; + public string Note => (Record as IRangeAnnotation)?.Text ?? string.Empty; + public string Title => (Record as Clip)?.Title ?? string.Empty; public BookRecordEntry(IRecord record) => Record = record; private static string formatTimeSpan(TimeSpan timeSpan) diff --git a/Source/LibationAvalonia/Dialogs/DescriptionDisplayDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/DescriptionDisplayDialog.axaml.cs index 993c8861..b846f810 100644 --- a/Source/LibationAvalonia/Dialogs/DescriptionDisplayDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/DescriptionDisplayDialog.axaml.cs @@ -3,7 +3,6 @@ using Avalonia.Controls; using System; using System.Linq; -#nullable enable namespace LibationAvalonia.Dialogs { public partial class DescriptionDisplayDialog : Window diff --git a/Source/LibationAvalonia/Dialogs/DialogWindow.cs b/Source/LibationAvalonia/Dialogs/DialogWindow.cs index 7489cebe..9736fc0d 100644 --- a/Source/LibationAvalonia/Dialogs/DialogWindow.cs +++ b/Source/LibationAvalonia/Dialogs/DialogWindow.cs @@ -14,7 +14,7 @@ namespace LibationAvalonia.Dialogs protected bool CancelOnEscape { get; set; } = true; protected bool SaveOnEnter { get; set; } = true; public bool SaveAndRestorePosition { get; set; } - public Control ControlToFocusOnShow { get; set; } + public Control? ControlToFocusOnShow { get; set; } protected override Type StyleKeyOverride => typeof(DialogWindow); public DialogResult DialogResult { get; private set; } = DialogResult.None; @@ -39,7 +39,7 @@ namespace LibationAvalonia.Dialogs } } - private void DialogWindow_Loaded(object sender, Avalonia.Interactivity.RoutedEventArgs e) + private void DialogWindow_Loaded(object? sender, Avalonia.Interactivity.RoutedEventArgs e) { if (!CanResize) this.HideMinMaxBtns(); @@ -57,20 +57,20 @@ namespace LibationAvalonia.Dialogs } } - private void DialogWindow_Initialized(object sender, EventArgs e) + private void DialogWindow_Initialized(object? sender, EventArgs e) { this.WindowStartupLocation = WindowStartupLocation.CenterOwner; if (SaveAndRestorePosition) this.RestoreSizeAndLocation(Configuration.Instance); } - private void DialogWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e) + private void DialogWindow_Closing(object? sender, System.ComponentModel.CancelEventArgs e) { if (SaveAndRestorePosition) this.SaveSizeAndLocation(Configuration.Instance); } - private void DialogWindow_Opened(object sender, EventArgs e) + private void DialogWindow_Opened(object? sender, EventArgs e) { ControlToFocusOnShow?.Focus(); } @@ -86,7 +86,7 @@ namespace LibationAvalonia.Dialogs protected virtual void CancelAndClose() => Close(DialogResult.Cancel); protected virtual async Task CancelAndCloseAsync() => await Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(CancelAndClose); - private async void DialogWindow_KeyDown(object sender, Avalonia.Input.KeyEventArgs e) + private async void DialogWindow_KeyDown(object? sender, Avalonia.Input.KeyEventArgs e) { if (CancelOnEscape && e.Key == Avalonia.Input.Key.Escape) await CancelAndCloseAsync(); diff --git a/Source/LibationAvalonia/Dialogs/EditQuickFilters.axaml.cs b/Source/LibationAvalonia/Dialogs/EditQuickFilters.axaml.cs index b37fdc6f..1b64c064 100644 --- a/Source/LibationAvalonia/Dialogs/EditQuickFilters.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/EditQuickFilters.axaml.cs @@ -13,13 +13,13 @@ namespace LibationAvalonia.Dialogs public class Filter : ViewModels.ViewModelBase { - public string Name + public string? Name { get => field; set => this.RaiseAndSetIfChanged(ref field, value); } - public string FilterString + public string? FilterString { get => field; set @@ -33,7 +33,7 @@ namespace LibationAvalonia.Dialogs public bool IsTop { get => field; set => this.RaiseAndSetIfChanged(ref field, value); } public bool IsBottom { get => field; set => this.RaiseAndSetIfChanged(ref field, value); } - public QuickFilters.NamedFilter AsNamedFilter() => new(FilterString, Name); + public QuickFilters.NamedFilter? AsNamedFilter() => FilterString is null ? null : new(FilterString, Name); } public EditQuickFilters() @@ -76,7 +76,7 @@ namespace LibationAvalonia.Dialogs DataContext = this; } - private void Filter_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + private void Filter_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e) { if (Filters.Any(f => f.IsDefault)) return; @@ -88,7 +88,7 @@ namespace LibationAvalonia.Dialogs protected override void SaveAndClose() { - QuickFilters.ReplaceAll(Filters.Where(f => !f.IsDefault).Select(x => x.AsNamedFilter())); + QuickFilters.ReplaceAll(Filters.Select(x => x.AsNamedFilter()).OfType()); base.SaveAndClose(); } diff --git a/Source/LibationAvalonia/Dialogs/EditReplacementChars.axaml.cs b/Source/LibationAvalonia/Dialogs/EditReplacementChars.axaml.cs index 1a3949e5..4b67400d 100644 --- a/Source/LibationAvalonia/Dialogs/EditReplacementChars.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/EditReplacementChars.axaml.cs @@ -6,7 +6,6 @@ using ReactiveUI; using System.Collections.Generic; using System.Linq; -#nullable enable namespace LibationAvalonia.Dialogs { public partial class EditReplacementChars : DialogWindow diff --git a/Source/LibationAvalonia/Dialogs/EditTemplateDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/EditTemplateDialog.axaml.cs index aba6bc88..386bbfa9 100644 --- a/Source/LibationAvalonia/Dialogs/EditTemplateDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/EditTemplateDialog.axaml.cs @@ -17,7 +17,7 @@ namespace LibationAvalonia.Dialogs; public partial class EditTemplateDialog : DialogWindow { - private EditTemplateViewModel _viewModel; + private EditTemplateViewModel? _viewModel; public EditTemplateDialog() { @@ -51,18 +51,18 @@ public partial class EditTemplateDialog : DialogWindow { var dataGrid = sender as DataGrid; - var item = (dataGrid.SelectedItem as Tuple).Item3; + var item = (dataGrid?.SelectedItem as Tuple)?.Item3; if (string.IsNullOrWhiteSpace(item)) return; var text = userEditTbox.Text; - userEditTbox.Text = text.Insert(Math.Min(Math.Max(0, userEditTbox.CaretIndex), text.Length), item); + userEditTbox.Text = text?.Insert(Math.Min(Math.Max(0, userEditTbox.CaretIndex), text.Length), item); userEditTbox.CaretIndex += item.Length; } protected override async Task SaveAndCloseAsync() { - if (!await _viewModel.Validate()) + if (_viewModel is null || !await _viewModel.Validate()) return; await base.SaveAndCloseAsync(); @@ -101,7 +101,7 @@ public partial class EditTemplateDialog : DialogWindow => Go.To.Url(@"ht" + "tps://github.com/rmcrackan/Libation/blob/master/Documentation/NamingTemplates.md"); // hold the work-in-progress value. not guaranteed to be valid - public string UserTemplateText + public string? UserTemplateText { get => field; set @@ -111,7 +111,7 @@ public partial class EditTemplateDialog : DialogWindow } } - public string WarningText { get => field; set => this.RaiseAndSetIfChanged(ref field, value); } + public string? WarningText { get => field; set => this.RaiseAndSetIfChanged(ref field, value); } public string Description { get; } @@ -147,7 +147,7 @@ public partial class EditTemplateDialog : DialogWindow // \books\author with a very <= normal line break on space between words // long name\narrator narrator // \title <= line break on the zero-with space we added before slashes - string slashWrap(string val) => val.Replace(sing, $"{ZERO_WIDTH_SPACE}{sing}"); + string slashWrap(string? val) => val?.Replace(sing, $"{ZERO_WIDTH_SPACE}{sing}") ?? string.Empty; WarningText = !TemplateEditor.EditingTemplate.HasWarnings diff --git a/Source/LibationAvalonia/Dialogs/FindBetterQualityBooksDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/FindBetterQualityBooksDialog.axaml.cs index 953a70d5..6201dc14 100644 --- a/Source/LibationAvalonia/Dialogs/FindBetterQualityBooksDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/FindBetterQualityBooksDialog.axaml.cs @@ -17,7 +17,6 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; -#nullable enable namespace LibationAvalonia.Dialogs; public partial class FindBetterQualityBooksDialog : DialogWindow @@ -60,6 +59,7 @@ public partial class FindBetterQualityBooksDialog : DialogWindow => lb.Book.ContentType is ContentType.Product //only scan books, not podcasts && !lb.Book.IsSpatial //skip spatial audio books. When querying the /metadata endpoint, it will only show ac-4 data for spatial audiobooks. && lb.Book.UserDefinedItem.BookStatus is LiberatedStatus.Liberated //only check if the book is liberated + && lb.Book.UserDefinedItem.LastDownloadedFormat is not null //Don't check if it wast downloaded prior to adding format tracking && lb.Book.UserDefinedItem.LastDownloadedFormat.Codec is not Codec.Mp3 //If they downloaded as mp3, no way to tell what source material was. Skip. && lb.Book.AudioExists; //only check if audio files exist @@ -224,7 +224,7 @@ public partial class FindBetterQualityBooksDialog : DialogWindow LibraryBook = libraryBook; Asin = libraryBook.Book.AudibleProductId; Title = libraryBook.Book.Title; - Codec = libraryBook.Book.UserDefinedItem.LastDownloadedFormat.CodecString; + Codec = libraryBook.Book.UserDefinedItem.LastDownloadedFormat!.CodecString; Bitrate = libraryBook.Book.UserDefinedItem.LastDownloadedFormat.BitRate; BitrateString = GetBitrateString(Bitrate); } diff --git a/Source/LibationAvalonia/Dialogs/ImageDisplayDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/ImageDisplayDialog.axaml.cs index 01ef562e..acab2938 100644 --- a/Source/LibationAvalonia/Dialogs/ImageDisplayDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/ImageDisplayDialog.axaml.cs @@ -9,8 +9,8 @@ namespace LibationAvalonia.Dialogs { public partial class ImageDisplayDialog : DialogWindow, INotifyPropertyChanged { - public string PictureFileName { get; set; } - public string BookSaveDirectory { get; set; } + public string? PictureFileName { get; set; } + public string? BookSaveDirectory { get; set; } private readonly BitmapHolder _bitmapHolder = new BitmapHolder(); @@ -50,7 +50,7 @@ namespace LibationAvalonia.Dialogs try { - _bitmapHolder.CoverImage.Save(selectedFile); + _bitmapHolder.CoverImage?.Save(selectedFile); } catch (Exception ex) { @@ -61,7 +61,7 @@ namespace LibationAvalonia.Dialogs public class BitmapHolder : ViewModels.ViewModelBase { - public Bitmap CoverImage { get => field; set => this.RaiseAndSetIfChanged(ref field, value); } + public Bitmap? CoverImage { get => field; set => this.RaiseAndSetIfChanged(ref field, value); } } } } diff --git a/Source/LibationAvalonia/Dialogs/LibationFilesDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/LibationFilesDialog.axaml.cs index 0105c0cb..5a81a1d6 100644 --- a/Source/LibationAvalonia/Dialogs/LibationFilesDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/LibationFilesDialog.axaml.cs @@ -17,11 +17,11 @@ namespace LibationAvalonia.Dialogs Configuration.KnownDirectories.MyDocs }; - public string Directory { get; set; } + public string? Directory { get; set; } } private readonly DirSelectOptions dirSelectOptions; - public string SelectedDirectory => dirSelectOptions.Directory; + public string? SelectedDirectory => dirSelectOptions.Directory; public LibationFilesDialog() : base(saveAndRestorePosition: false) { @@ -42,7 +42,7 @@ namespace LibationAvalonia.Dialogs public async void Save_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) { - if (!System.IO.Directory.Exists(SelectedDirectory)) + if (SelectedDirectory is not null && !Directory.Exists(SelectedDirectory)) { try { diff --git a/Source/LibationAvalonia/Dialogs/LiberatedStatusBatchManualDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/LiberatedStatusBatchManualDialog.axaml.cs index bd56702e..c93cb21c 100644 --- a/Source/LibationAvalonia/Dialogs/LiberatedStatusBatchManualDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/LiberatedStatusBatchManualDialog.axaml.cs @@ -9,21 +9,21 @@ namespace LibationAvalonia.Dialogs private class liberatedComboBoxItem { public LiberatedStatus Status { get; set; } - public string Text { get; set; } - public override string ToString() => Text; + public string? Text { get; set; } + public override string? ToString() => Text; } public LiberatedStatus BookLiberatedStatus { get; private set; } - private liberatedComboBoxItem _selectedStatus; - public object SelectedItem + private liberatedComboBoxItem? _selectedStatus; + public object? SelectedItem { get => _selectedStatus; set { _selectedStatus = value as liberatedComboBoxItem; - BookLiberatedStatus = _selectedStatus.Status; + BookLiberatedStatus = _selectedStatus?.Status ?? default; } } @@ -36,7 +36,7 @@ namespace LibationAvalonia.Dialogs public LiberatedStatusBatchManualDialog(bool isPdf) : this() { if (isPdf) - this.Title = this.Title.Replace("book", "PDF"); + Title = Title?.Replace("book", "PDF"); } public LiberatedStatusBatchManualDialog() diff --git a/Source/LibationAvalonia/Dialogs/LocateAudiobooksDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/LocateAudiobooksDialog.axaml.cs index a06e19c6..f265d404 100644 --- a/Source/LibationAvalonia/Dialogs/LocateAudiobooksDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/LocateAudiobooksDialog.axaml.cs @@ -11,9 +11,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; -using System.Threading.Tasks; -#nullable enable namespace LibationAvalonia.Dialogs { public partial class LocateAudiobooksDialog : DialogWindow diff --git a/Source/LibationAvalonia/Dialogs/Login/AvaloniaLoginChoiceEager.cs b/Source/LibationAvalonia/Dialogs/Login/AvaloniaLoginChoiceEager.cs index 8ec087bd..c0c7bd7a 100644 --- a/Source/LibationAvalonia/Dialogs/Login/AvaloniaLoginChoiceEager.cs +++ b/Source/LibationAvalonia/Dialogs/Login/AvaloniaLoginChoiceEager.cs @@ -9,7 +9,6 @@ using LibationUiBase.Forms; using System; using System.Threading.Tasks; -#nullable enable namespace LibationAvalonia.Dialogs.Login { public class AvaloniaLoginChoiceEager : ILoginChoiceEager diff --git a/Source/LibationAvalonia/Dialogs/Login/LoginExternalDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/Login/LoginExternalDialog.axaml.cs index 77268722..3f7cbce3 100644 --- a/Source/LibationAvalonia/Dialogs/Login/LoginExternalDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/Login/LoginExternalDialog.axaml.cs @@ -10,9 +10,9 @@ namespace LibationAvalonia.Dialogs.Login { public partial class LoginExternalDialog : DialogWindow { - public Account Account { get; } - public string ExternalLoginUrl { get; } - public string ResponseUrl { get; set; } + public Account? Account { get; } + public string? ExternalLoginUrl { get; } + public string? ResponseUrl { get; set; } public LoginExternalDialog() : base(saveAndRestorePosition: false) { @@ -54,7 +54,10 @@ namespace LibationAvalonia.Dialogs.Login public async void CopyUrlToClipboard_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) - => await App.MainWindow.Clipboard.SetTextAsync(ExternalLoginUrl); + { + if (App.MainWindow?.Clipboard is not null) + await App.MainWindow.Clipboard.SetTextAsync(ExternalLoginUrl); + } public void LaunchInBrowser_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) => Go.To.Url(ExternalLoginUrl); diff --git a/Source/LibationAvalonia/Dialogs/MessageBoxWindow.axaml.cs b/Source/LibationAvalonia/Dialogs/MessageBoxWindow.axaml.cs index bd77f825..89aa71fb 100644 --- a/Source/LibationAvalonia/Dialogs/MessageBoxWindow.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/MessageBoxWindow.axaml.cs @@ -22,7 +22,7 @@ namespace LibationAvalonia.Dialogs public void Button1_Click(object sender, Avalonia.Interactivity.RoutedEventArgs args) { var vm = DataContext as MessageBoxViewModel; - var dialogResult = vm.Buttons switch + var dialogResult = vm?.Buttons switch { MessageBoxButtons.OK => DialogResult.OK, MessageBoxButtons.OKCancel => DialogResult.OK, @@ -38,7 +38,7 @@ namespace LibationAvalonia.Dialogs public void Button2_Click(object sender, Avalonia.Interactivity.RoutedEventArgs args) { var vm = DataContext as MessageBoxViewModel; - var dialogResult = vm.Buttons switch + var dialogResult = vm?.Buttons switch { MessageBoxButtons.OKCancel => DialogResult.Cancel, MessageBoxButtons.AbortRetryIgnore => DialogResult.Retry, @@ -53,7 +53,7 @@ namespace LibationAvalonia.Dialogs public void Button3_Click(object sender, Avalonia.Interactivity.RoutedEventArgs args) { var vm = DataContext as MessageBoxViewModel; - var dialogResult = vm.Buttons switch + var dialogResult = vm?.Buttons switch { MessageBoxButtons.AbortRetryIgnore => DialogResult.Ignore, MessageBoxButtons.YesNoCancel => DialogResult.Cancel, diff --git a/Source/LibationAvalonia/Dialogs/SearchSyntaxDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/SearchSyntaxDialog.axaml.cs index 37fdcb0a..93957672 100644 --- a/Source/LibationAvalonia/Dialogs/SearchSyntaxDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/SearchSyntaxDialog.axaml.cs @@ -3,7 +3,6 @@ using LibationSearchEngine; using System; using System.Linq; -#nullable enable namespace LibationAvalonia.Dialogs { public partial class SearchSyntaxDialog : DialogWindow diff --git a/Source/LibationAvalonia/Dialogs/SetupDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/SetupDialog.axaml.cs index 88383ca2..9077fc53 100644 --- a/Source/LibationAvalonia/Dialogs/SetupDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/SetupDialog.axaml.cs @@ -9,7 +9,7 @@ namespace LibationAvalonia.Dialogs { public bool IsNewUser { get; private set; } public bool IsReturningUser { get; private set; } - public ComboBoxItem SelectedTheme { get; set; } + public ComboBoxItem? SelectedTheme { get; set; } public SetupDialog() { InitializeComponent(); diff --git a/Source/LibationAvalonia/Dialogs/TagsBatchDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/TagsBatchDialog.axaml.cs index dc814422..7ea92fa0 100644 --- a/Source/LibationAvalonia/Dialogs/TagsBatchDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/TagsBatchDialog.axaml.cs @@ -4,7 +4,7 @@ namespace LibationAvalonia.Dialogs { public partial class TagsBatchDialog : DialogWindow { - public string NewTags { get; set; } + public string? NewTags { get; set; } public TagsBatchDialog() { InitializeComponent(); diff --git a/Source/LibationAvalonia/Dialogs/ThemePickerDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/ThemePickerDialog.axaml.cs index d418cf0d..c169ec81 100644 --- a/Source/LibationAvalonia/Dialogs/ThemePickerDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/ThemePickerDialog.axaml.cs @@ -10,7 +10,6 @@ using System.Linq; using Avalonia.Platform.Storage; using LibationUiBase.Forms; -#nullable enable namespace LibationAvalonia.Dialogs; public partial class ThemePickerDialog : DialogWindow diff --git a/Source/LibationAvalonia/Dialogs/TrashBinDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/TrashBinDialog.axaml.cs index 15518e50..027fb558 100644 --- a/Source/LibationAvalonia/Dialogs/TrashBinDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/TrashBinDialog.axaml.cs @@ -129,7 +129,7 @@ namespace LibationAvalonia.Dialogs } private IDisposable tracker; - private void CheckboxPropertyChanged(Tuple e) + private void CheckboxPropertyChanged(Tuple e) { if (e.Item2.PropertyName == nameof(CheckBoxViewModel.IsChecked)) CheckedBooksCount = DeletedBooks.Count(b => b.IsChecked); diff --git a/Source/LibationAvalonia/Dialogs/UpgradeNotificationDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/UpgradeNotificationDialog.axaml.cs index 4a586680..8ab54f5a 100644 --- a/Source/LibationAvalonia/Dialogs/UpgradeNotificationDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/UpgradeNotificationDialog.axaml.cs @@ -9,11 +9,11 @@ namespace LibationAvalonia.Dialogs public partial class UpgradeNotificationDialog : DialogWindow { private const string UpdateMessage = "There is a new version available. Would you like to update?\r\n\r\nAfter you close Libation, the upgrade will start automatically."; - public string TopMessage { get; } - public string DownloadLinkText { get; } - public string ReleaseNotes { get; } - public string OkText { get; } - private string PackageUrl { get; } + public string? TopMessage { get; } + public string? DownloadLinkText { get; } + public string? ReleaseNotes { get; } + public string? OkText { get; } + private string? PackageUrl { get; } public UpgradeNotificationDialog() { if (Design.IsDesignMode) diff --git a/Source/LibationAvalonia/FormSaveExtension.cs b/Source/LibationAvalonia/FormSaveExtension.cs index 91fa42ed..8476bbd5 100644 --- a/Source/LibationAvalonia/FormSaveExtension.cs +++ b/Source/LibationAvalonia/FormSaveExtension.cs @@ -7,7 +7,6 @@ using LibationFileManager; using System; using System.Linq; -#nullable enable namespace LibationAvalonia { public static class FormSaveExtension diff --git a/Source/LibationAvalonia/LibationAvalonia.csproj b/Source/LibationAvalonia/LibationAvalonia.csproj index a0599682..1076a3c1 100644 --- a/Source/LibationAvalonia/LibationAvalonia.csproj +++ b/Source/LibationAvalonia/LibationAvalonia.csproj @@ -11,6 +11,7 @@ true false false + enable diff --git a/Source/LibationAvalonia/MessageBox.cs b/Source/LibationAvalonia/MessageBox.cs index 34db9730..947d7d47 100644 --- a/Source/LibationAvalonia/MessageBox.cs +++ b/Source/LibationAvalonia/MessageBox.cs @@ -11,9 +11,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using Avalonia.Platform; -#nullable enable namespace LibationAvalonia { public class MessageBox diff --git a/Source/LibationAvalonia/Program.cs b/Source/LibationAvalonia/Program.cs index d2179412..9b966aa3 100644 --- a/Source/LibationAvalonia/Program.cs +++ b/Source/LibationAvalonia/Program.cs @@ -12,9 +12,7 @@ using LibationAvalonia.Dialogs; using Avalonia.Threading; using FileManager; using System.Linq; -using System.Reflection; -#nullable enable namespace LibationAvalonia { static class Program diff --git a/Source/LibationAvalonia/Themes/ChardonnayTheme.cs b/Source/LibationAvalonia/Themes/ChardonnayTheme.cs index 177fa1b5..c3a28274 100644 --- a/Source/LibationAvalonia/Themes/ChardonnayTheme.cs +++ b/Source/LibationAvalonia/Themes/ChardonnayTheme.cs @@ -11,7 +11,6 @@ using System.Linq.Expressions; using System.Reflection; using System.Collections.Frozen; -#nullable enable namespace LibationAvalonia; public class ChardonnayTheme : IUpdatable, ICloneable diff --git a/Source/LibationAvalonia/Themes/ChardonnayThemePersister.cs b/Source/LibationAvalonia/Themes/ChardonnayThemePersister.cs index 94ecb243..a644fd15 100644 --- a/Source/LibationAvalonia/Themes/ChardonnayThemePersister.cs +++ b/Source/LibationAvalonia/Themes/ChardonnayThemePersister.cs @@ -5,7 +5,6 @@ using LibationFileManager; using Newtonsoft.Json; using System; -#nullable enable namespace LibationAvalonia.Themes; public class ChardonnayThemePersister : JsonFilePersister diff --git a/Source/LibationAvalonia/ViewLocator.cs b/Source/LibationAvalonia/ViewLocator.cs deleted file mode 100644 index c604e083..00000000 --- a/Source/LibationAvalonia/ViewLocator.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Avalonia.Controls; -using Avalonia.Controls.Templates; -using LibationAvalonia.ViewModels; -using System; - -namespace LibationAvalonia -{ - public class ViewLocator : IDataTemplate - { - public Control Build(object data) - { - var name = data.GetType().FullName!.Replace("ViewModel", "View"); - var type = Type.GetType(name); - - if (type != null) - { - return (Control)Activator.CreateInstance(type)!; - } - else - { - return new TextBlock { Text = "Not Found: " + name }; - } - } - - public bool Match(object data) - { - return data is ViewModelBase; - } - } -} diff --git a/Source/LibationAvalonia/ViewModels/Dialogs/MessageBoxViewModel.cs b/Source/LibationAvalonia/ViewModels/Dialogs/MessageBoxViewModel.cs index c673b697..a43cc407 100644 --- a/Source/LibationAvalonia/ViewModels/Dialogs/MessageBoxViewModel.cs +++ b/Source/LibationAvalonia/ViewModels/Dialogs/MessageBoxViewModel.cs @@ -5,7 +5,7 @@ namespace LibationAvalonia.ViewModels.Dialogs { public class MessageBoxViewModel { - public string Message { get => field; set => field = value; } + public string? Message { get => field; set => field = value; } public string Caption { get; } = "Message Box"; private MessageBoxButtons _button; private MessageBoxIcon _icon; diff --git a/Source/LibationAvalonia/ViewModels/LiberateStatusButtonViewModel.cs b/Source/LibationAvalonia/ViewModels/LiberateStatusButtonViewModel.cs index b5bfc7b7..ea9307c6 100644 --- a/Source/LibationAvalonia/ViewModels/LiberateStatusButtonViewModel.cs +++ b/Source/LibationAvalonia/ViewModels/LiberateStatusButtonViewModel.cs @@ -1,6 +1,5 @@ using ReactiveUI; -#nullable enable namespace LibationAvalonia.ViewModels { public class LiberateStatusButtonViewModel : ViewModelBase diff --git a/Source/LibationAvalonia/ViewModels/MainVM.BackupCounts.cs b/Source/LibationAvalonia/ViewModels/MainVM.BackupCounts.cs index 4a3311fd..009af3ba 100644 --- a/Source/LibationAvalonia/ViewModels/MainVM.BackupCounts.cs +++ b/Source/LibationAvalonia/ViewModels/MainVM.BackupCounts.cs @@ -5,7 +5,6 @@ using ReactiveUI; using System.Collections.Generic; using System.Threading.Tasks; -#nullable enable namespace LibationAvalonia.ViewModels { partial class MainVM diff --git a/Source/LibationAvalonia/ViewModels/MainVM.Export.cs b/Source/LibationAvalonia/ViewModels/MainVM.Export.cs index 6f551fd3..e0b58802 100644 --- a/Source/LibationAvalonia/ViewModels/MainVM.Export.cs +++ b/Source/LibationAvalonia/ViewModels/MainVM.Export.cs @@ -5,7 +5,6 @@ using LibationFileManager; using System; using System.Threading.Tasks; -#nullable enable namespace LibationAvalonia.ViewModels { partial class MainVM diff --git a/Source/LibationAvalonia/ViewModels/MainVM.Filters.cs b/Source/LibationAvalonia/ViewModels/MainVM.Filters.cs index d38109fa..dfedc63c 100644 --- a/Source/LibationAvalonia/ViewModels/MainVM.Filters.cs +++ b/Source/LibationAvalonia/ViewModels/MainVM.Filters.cs @@ -10,7 +10,6 @@ using System; using System.Linq; using System.Threading.Tasks; -#nullable enable namespace LibationAvalonia.ViewModels { partial class MainVM diff --git a/Source/LibationAvalonia/ViewModels/MainVM.Import.cs b/Source/LibationAvalonia/ViewModels/MainVM.Import.cs index 6082e690..b93f8658 100644 --- a/Source/LibationAvalonia/ViewModels/MainVM.Import.cs +++ b/Source/LibationAvalonia/ViewModels/MainVM.Import.cs @@ -9,7 +9,6 @@ using System.Threading.Tasks; using Avalonia.Input; using LibationUiBase.Forms; -#nullable enable namespace LibationAvalonia.ViewModels { public partial class MainVM diff --git a/Source/LibationAvalonia/ViewModels/MainVM.Liberate.cs b/Source/LibationAvalonia/ViewModels/MainVM.Liberate.cs index d8bab350..6f0cfc49 100644 --- a/Source/LibationAvalonia/ViewModels/MainVM.Liberate.cs +++ b/Source/LibationAvalonia/ViewModels/MainVM.Liberate.cs @@ -5,11 +5,9 @@ using System.Linq; using System.Threading.Tasks; using DataLayer; using LibationUiBase.Forms; -using LibationUiBase; using System.Collections.Generic; using Avalonia.Threading; -#nullable enable namespace LibationAvalonia.ViewModels { partial class MainVM diff --git a/Source/LibationAvalonia/ViewModels/MainVM.ProcessQueue.cs b/Source/LibationAvalonia/ViewModels/MainVM.ProcessQueue.cs index 6910f298..3acb9312 100644 --- a/Source/LibationAvalonia/ViewModels/MainVM.ProcessQueue.cs +++ b/Source/LibationAvalonia/ViewModels/MainVM.ProcessQueue.cs @@ -1,13 +1,11 @@ using DataLayer; using Dinah.Core; using LibationFileManager; -using LibationUiBase; using LibationUiBase.GridView; using ReactiveUI; using System; using System.Linq; -#nullable enable namespace LibationAvalonia.ViewModels { partial class MainVM diff --git a/Source/LibationAvalonia/ViewModels/MainVM.ScanAuto.cs b/Source/LibationAvalonia/ViewModels/MainVM.ScanAuto.cs index 4a324ec5..118df593 100644 --- a/Source/LibationAvalonia/ViewModels/MainVM.ScanAuto.cs +++ b/Source/LibationAvalonia/ViewModels/MainVM.ScanAuto.cs @@ -7,7 +7,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -#nullable enable namespace LibationAvalonia.ViewModels { partial class MainVM diff --git a/Source/LibationAvalonia/ViewModels/MainVM.Settings.cs b/Source/LibationAvalonia/ViewModels/MainVM.Settings.cs index f525cd05..91d90721 100644 --- a/Source/LibationAvalonia/ViewModels/MainVM.Settings.cs +++ b/Source/LibationAvalonia/ViewModels/MainVM.Settings.cs @@ -4,7 +4,6 @@ using ReactiveUI; using System; using System.Threading.Tasks; -#nullable enable namespace LibationAvalonia.ViewModels { partial class MainVM diff --git a/Source/LibationAvalonia/ViewModels/MainVM.VisibleBooks.cs b/Source/LibationAvalonia/ViewModels/MainVM.VisibleBooks.cs index 84a979bc..1cddef02 100644 --- a/Source/LibationAvalonia/ViewModels/MainVM.VisibleBooks.cs +++ b/Source/LibationAvalonia/ViewModels/MainVM.VisibleBooks.cs @@ -7,9 +7,7 @@ using LibationAvalonia.Dialogs; using ReactiveUI; using LibationUiBase.Forms; using System.Linq; -using LibationUiBase; -#nullable enable namespace LibationAvalonia.ViewModels { partial class MainVM diff --git a/Source/LibationAvalonia/ViewModels/MainVM._NoUI.cs b/Source/LibationAvalonia/ViewModels/MainVM._NoUI.cs index 384d247e..2958d5c9 100644 --- a/Source/LibationAvalonia/ViewModels/MainVM._NoUI.cs +++ b/Source/LibationAvalonia/ViewModels/MainVM._NoUI.cs @@ -3,7 +3,6 @@ using LibationUiBase; using System; using System.IO; -#nullable enable namespace LibationAvalonia.ViewModels { partial class MainVM diff --git a/Source/LibationAvalonia/ViewModels/MainVM.cs b/Source/LibationAvalonia/ViewModels/MainVM.cs index 1115dc7b..540bdb52 100644 --- a/Source/LibationAvalonia/ViewModels/MainVM.cs +++ b/Source/LibationAvalonia/ViewModels/MainVM.cs @@ -7,7 +7,6 @@ using ReactiveUI; using System.Collections.Generic; using System.Threading.Tasks; -#nullable enable namespace LibationAvalonia.ViewModels { public partial class MainVM : ViewModelBase diff --git a/Source/LibationAvalonia/ViewModels/ProductsDisplayViewModel.cs b/Source/LibationAvalonia/ViewModels/ProductsDisplayViewModel.cs index cfa1b966..a072768c 100644 --- a/Source/LibationAvalonia/ViewModels/ProductsDisplayViewModel.cs +++ b/Source/LibationAvalonia/ViewModels/ProductsDisplayViewModel.cs @@ -16,7 +16,6 @@ using System.Linq; using System.Runtime.CompilerServices; using System.Threading.Tasks; -#nullable enable namespace LibationAvalonia.ViewModels { public class ProductsDisplayViewModel : ViewModelBase @@ -196,8 +195,8 @@ namespace LibationAvalonia.ViewModels .ExceptBy(dbBooks.Select(lb => lb.Book.AudibleProductId), ge => ge.AudibleProductId); //Remove books in series from their parents' Children list - foreach (var removed in removedBooks.Where(b => b.Liberate.IsEpisode)) - removed.Parent.RemoveChild(removed); + foreach (var removed in removedBooks.Where(b => b.Liberate?.IsEpisode is true)) + removed.Parent?.RemoveChild(removed); //Remove series that have no children var removedSeries = sourceSnapshot.EmptySeries(); @@ -265,7 +264,7 @@ namespace LibationAvalonia.ViewModels seriesEntries.Add(seriesEntry); episodeEntry = seriesEntry.Children[0]; - seriesEntry.Liberate.Expanded = true; + seriesEntry.Liberate?.Expanded = true; SOURCE.Insert(0, seriesEntry); } else @@ -300,7 +299,7 @@ namespace LibationAvalonia.ViewModels public async Task ToggleSeriesExpanded(SeriesEntry seriesEntry) { - seriesEntry.Liberate.Expanded = !seriesEntry.Liberate.Expanded; + seriesEntry.Liberate?.Expanded = !seriesEntry.Liberate.Expanded; await refreshGrid(); } @@ -324,7 +323,7 @@ namespace LibationAvalonia.ViewModels private bool CollectionFilter(object item) { if (item is LibraryBookEntry lbe - && lbe.Liberate.IsEpisode + && lbe.Liberate?.IsEpisode is true && lbe.Parent?.Liberate?.Expanded != true) return false; diff --git a/Source/LibationAvalonia/ViewModels/RowComparer.cs b/Source/LibationAvalonia/ViewModels/RowComparer.cs index bcdf50b0..e35725e3 100644 --- a/Source/LibationAvalonia/ViewModels/RowComparer.cs +++ b/Source/LibationAvalonia/ViewModels/RowComparer.cs @@ -3,7 +3,6 @@ using LibationUiBase.GridView; using System.ComponentModel; using System.Reflection; -#nullable enable namespace LibationAvalonia.ViewModels { internal class RowComparer : RowComparerBase diff --git a/Source/LibationAvalonia/ViewModels/Settings/AudioSettingsVM.cs b/Source/LibationAvalonia/ViewModels/Settings/AudioSettingsVM.cs index 329bb7ad..bbe7568f 100644 --- a/Source/LibationAvalonia/ViewModels/Settings/AudioSettingsVM.cs +++ b/Source/LibationAvalonia/ViewModels/Settings/AudioSettingsVM.cs @@ -8,7 +8,6 @@ using ReactiveUI; using System; using System.Linq; -#nullable enable namespace LibationAvalonia.ViewModels.Settings { public class AudioSettingsVM : ViewModelBase diff --git a/Source/LibationAvalonia/ViewModels/Settings/DownloadDecryptSettingsVM.cs b/Source/LibationAvalonia/ViewModels/Settings/DownloadDecryptSettingsVM.cs index cc8dcbef..72a43eaa 100644 --- a/Source/LibationAvalonia/ViewModels/Settings/DownloadDecryptSettingsVM.cs +++ b/Source/LibationAvalonia/ViewModels/Settings/DownloadDecryptSettingsVM.cs @@ -3,7 +3,6 @@ using LibationFileManager; using ReactiveUI; using System.Collections.Generic; -#nullable enable namespace LibationAvalonia.ViewModels.Settings { public class DownloadDecryptSettingsVM : ViewModelBase diff --git a/Source/LibationAvalonia/ViewModels/Settings/ImportSettingsVM.cs b/Source/LibationAvalonia/ViewModels/Settings/ImportSettingsVM.cs index f9417ca9..3afae000 100644 --- a/Source/LibationAvalonia/ViewModels/Settings/ImportSettingsVM.cs +++ b/Source/LibationAvalonia/ViewModels/Settings/ImportSettingsVM.cs @@ -1,6 +1,5 @@ using LibationFileManager; -#nullable enable namespace LibationAvalonia.ViewModels.Settings { public class ImportSettingsVM diff --git a/Source/LibationAvalonia/ViewModels/Settings/ImportantSettingsVM.cs b/Source/LibationAvalonia/ViewModels/Settings/ImportantSettingsVM.cs index d0d71550..62dff4a6 100644 --- a/Source/LibationAvalonia/ViewModels/Settings/ImportantSettingsVM.cs +++ b/Source/LibationAvalonia/ViewModels/Settings/ImportantSettingsVM.cs @@ -7,7 +7,6 @@ using System; using System.Collections.Generic; using System.Linq; -#nullable enable namespace LibationAvalonia.ViewModels.Settings { public class ImportantSettingsVM : ViewModelBase diff --git a/Source/LibationAvalonia/ViewModels/Settings/SettingsVM.cs b/Source/LibationAvalonia/ViewModels/Settings/SettingsVM.cs index cef49e40..9aa7453c 100644 --- a/Source/LibationAvalonia/ViewModels/Settings/SettingsVM.cs +++ b/Source/LibationAvalonia/ViewModels/Settings/SettingsVM.cs @@ -6,7 +6,10 @@ namespace LibationAvalonia.ViewModels.Settings { public SettingsVM(Configuration config) { - LoadSettings(config); + ImportantSettings = new ImportantSettingsVM(config); + ImportSettings = new ImportSettingsVM(config); + DownloadDecryptSettings = new DownloadDecryptSettingsVM(config); + AudioSettings = new AudioSettingsVM(config); } public ImportantSettingsVM ImportantSettings { get; private set; } @@ -14,14 +17,6 @@ namespace LibationAvalonia.ViewModels.Settings public DownloadDecryptSettingsVM DownloadDecryptSettings { get; private set; } public AudioSettingsVM AudioSettings { get; private set; } - public void LoadSettings(Configuration config) - { - ImportantSettings = new ImportantSettingsVM(config); - ImportSettings = new ImportSettingsVM(config); - DownloadDecryptSettings = new DownloadDecryptSettingsVM(config); - AudioSettings = new AudioSettingsVM(config); - } - public void SaveSettings(Configuration config) { ImportantSettings.SaveSettings(config); diff --git a/Source/LibationAvalonia/Views/LiberateStatusButton.axaml.cs b/Source/LibationAvalonia/Views/LiberateStatusButton.axaml.cs index 072137ce..0f6dc94d 100644 --- a/Source/LibationAvalonia/Views/LiberateStatusButton.axaml.cs +++ b/Source/LibationAvalonia/Views/LiberateStatusButton.axaml.cs @@ -10,7 +10,7 @@ namespace LibationAvalonia.Views { public partial class LiberateStatusButton : UserControl { - public event EventHandler Click; + public event EventHandler? Click; public static readonly StyledProperty BookStatusProperty = AvaloniaProperty.Register(nameof(BookStatus)); @@ -50,12 +50,12 @@ namespace LibationAvalonia.Views DataContextChanged += LiberateStatusButton_DataContextChanged; } - private void LiberateStatusButton_DataContextChanged(object sender, EventArgs e) + 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 partially downloaded file. var status = DataContext as LibraryBookEntry; - status?.Liberate.Invalidate(nameof(status.Liberate.BookStatus)); + status?.Liberate?.Invalidate(nameof(status.Liberate.BookStatus)); } private void Button_Click(object sender, RoutedEventArgs e) => Click?.Invoke(this, EventArgs.Empty); diff --git a/Source/LibationAvalonia/Views/MainWindow.axaml.cs b/Source/LibationAvalonia/Views/MainWindow.axaml.cs index 8b6299a7..4f1065e6 100644 --- a/Source/LibationAvalonia/Views/MainWindow.axaml.cs +++ b/Source/LibationAvalonia/Views/MainWindow.axaml.cs @@ -37,11 +37,11 @@ namespace LibationAvalonia.Views KeyBindings.Add(new KeyBinding { Command = ReactiveCommand.Create(selectAndFocusSearchBox), Gesture = new KeyGesture(Key.F, Configuration.IsMacOs ? KeyModifiers.Meta : KeyModifiers.Control) }); - if (!Configuration.IsMacOs) + if (!Configuration.IsMacOs && ViewModel is MainVM vm) { - KeyBindings.Add(new KeyBinding { Command = ReactiveCommand.Create(ViewModel.ShowSettingsAsync), Gesture = new KeyGesture(Key.P, KeyModifiers.Control) }); - KeyBindings.Add(new KeyBinding { Command = ReactiveCommand.Create(ViewModel.ShowAccountsAsync), Gesture = new KeyGesture(Key.A, KeyModifiers.Control | KeyModifiers.Shift) }); - KeyBindings.Add(new KeyBinding { Command = ReactiveCommand.Create(ViewModel.ExportLibraryAsync), Gesture = new KeyGesture(Key.S, KeyModifiers.Control) }); + KeyBindings.Add(new KeyBinding { Command = ReactiveCommand.Create(vm.ShowSettingsAsync), Gesture = new KeyGesture(Key.P, KeyModifiers.Control) }); + KeyBindings.Add(new KeyBinding { Command = ReactiveCommand.Create(vm.ShowAccountsAsync), Gesture = new KeyGesture(Key.A, KeyModifiers.Control | KeyModifiers.Shift) }); + KeyBindings.Add(new KeyBinding { Command = ReactiveCommand.Create(vm.ExportLibraryAsync), Gesture = new KeyGesture(Key.S, KeyModifiers.Control) }); } Configuration.Instance.PropertyChanged += Settings_PropertyChanged; @@ -49,7 +49,7 @@ namespace LibationAvalonia.Views } [Dinah.Core.PropertyChangeFilter(nameof(Configuration.Books))] - private void Settings_PropertyChanged(object sender, Dinah.Core.PropertyChangedEventArgsEx e) + private void Settings_PropertyChanged(object? sender, Dinah.Core.PropertyChangedEventArgsEx? e) { if (!Configuration.IsWindows) { @@ -61,7 +61,7 @@ namespace LibationAvalonia.Views } } - private void AudibleApiStorage_LoadError(object sender, AccountSettingsLoadErrorEventArgs e) + private void AudibleApiStorage_LoadError(object? sender, AccountSettingsLoadErrorEventArgs e) { try { @@ -111,13 +111,13 @@ namespace LibationAvalonia.Views //Force the message box to show synchronously because we're not handling the exception //and libation will crash after the event handler returns var frame = new DispatcherFrame(); - _ = messageBoxWindow.ContinueWith(static (_, s) => ((DispatcherFrame)s).Continue = false, frame); + _ = messageBoxWindow.ContinueWith(static (_, s) => (s as DispatcherFrame)?.Continue = false, frame); Dispatcher.UIThread.PushFrame(frame); messageBoxWindow.GetAwaiter().GetResult(); } } - private async void MainWindow_Opened(object sender, EventArgs e) + private async void MainWindow_Opened(object? sender, EventArgs e) { if (AudibleFileStorage.BooksDirectory is null) { @@ -146,7 +146,7 @@ namespace LibationAvalonia.Views } } - private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e) + private void MainWindow_Closing(object? sender, System.ComponentModel.CancelEventArgs e) { productsDisplay?.CloseImageDisplay(); this.SaveSizeAndLocation(Configuration.Instance); @@ -163,21 +163,24 @@ namespace LibationAvalonia.Views public async Task OnLibraryLoadedAsync(List initialLibrary) { //Get the ViewModel before crossing the await boundary - var vm = ViewModel; + if (ViewModel is not MainVM vm) + return; + if (QuickFilters.UseDefault) await vm.PerformFilter(QuickFilters.Filters.FirstOrDefault()); - ViewModel.BindToGridTask = Task.WhenAll( + vm.BindToGridTask = Task.WhenAll( vm.SetBackupCountsAsync(initialLibrary), Task.Run(() => vm.ProductsDisplay.BindToGridAsync(initialLibrary))); - await ViewModel.BindToGridTask; + + await vm.BindToGridTask; } - public void ProductsDisplay_LiberateClicked(object _, IList libraryBook, Configuration config) => ViewModel.LiberateClicked(libraryBook, config); - public void ProductsDisplay_LiberateSeriesClicked(object _, SeriesEntry series) => ViewModel.LiberateSeriesClicked(series); - public void ProductsDisplay_ConvertToMp3Clicked(object _, LibraryBook[] libraryBook) => ViewModel.ConvertToMp3Clicked(libraryBook); + public void ProductsDisplay_LiberateClicked(object _, IList libraryBook, Configuration config) => ViewModel?.LiberateClicked(libraryBook, config); + public void ProductsDisplay_LiberateSeriesClicked(object _, SeriesEntry series) => ViewModel?.LiberateSeriesClicked(series); + public void ProductsDisplay_ConvertToMp3Clicked(object _, LibraryBook[] libraryBook) => ViewModel?.ConvertToMp3Clicked(libraryBook); - BookDetailsDialog bookDetailsForm; + BookDetailsDialog? bookDetailsForm; public void ProductsDisplay_TagsButtonClicked(object _, LibraryBook libraryBook) { if (bookDetailsForm is null || !bookDetailsForm.IsVisible) @@ -191,9 +194,9 @@ namespace LibationAvalonia.Views public async void filterSearchTb_KeyPress(object _, KeyEventArgs e) { - if (e.Key == Key.Return) + if (e.Key == Key.Return && ViewModel is not null) { - await ViewModel.FilterBtn(filterSearchTb.Text); + await ViewModel.FilterBtn(filterSearchTb.Text ?? string.Empty); // silence the 'ding' e.Handled = true; @@ -214,7 +217,7 @@ namespace LibationAvalonia.Views #pragma warning restore CS8321 // Local function is declared but never used var upgrader = new LibationUiBase.Upgrader(); - upgrader.DownloadProgress += async (_, e) => await Dispatcher.UIThread.InvokeAsync(() => ViewModel.DownloadProgress = e.ProgressPercentage); + upgrader.DownloadProgress += async (_, e) => await Dispatcher.UIThread.InvokeAsync(() => ViewModel?.DownloadProgress = e.ProgressPercentage); upgrader.DownloadBegin += async (_, _) => await Dispatcher.UIThread.InvokeAsync(() => setProgressVisible(true)); upgrader.DownloadCompleted += async (_, _) => await Dispatcher.UIThread.InvokeAsync(() => setProgressVisible(false)); upgrader.UpgradeFailed += async (_, message) => await Dispatcher.UIThread.InvokeAsync(() => { setProgressVisible(false); MessageBox.Show(this, message, "Upgrade Failed", MessageBoxButtons.OK, MessageBoxIcon.Error); }); @@ -224,7 +227,7 @@ namespace LibationAvalonia.Views #endif } - private void setProgressVisible(bool visible) => ViewModel.DownloadProgress = visible ? 0 : null; + private void setProgressVisible(bool visible) => ViewModel?.DownloadProgress = visible ? 0 : null; public SearchSyntaxDialog ShowSearchSyntaxDialog() { @@ -235,15 +238,15 @@ namespace LibationAvalonia.Views dialog.Show(this); return dialog; - void Dialog_Closed(object sender, EventArgs e) + void Dialog_Closed(object? sender, EventArgs e) { dialog.TagDoubleClicked -= Dialog_TagDoubleClicked; filterHelpBtn.IsEnabled = true; } - void Dialog_TagDoubleClicked(object sender, string tag) + void Dialog_TagDoubleClicked(object? sender, string tag) { var text = filterSearchTb.Text; - filterSearchTb.Text = text.Insert(Math.Min(Math.Max(0, filterSearchTb.CaretIndex), text.Length), tag); + filterSearchTb.Text = text?.Insert(Math.Min(Math.Max(0, filterSearchTb.CaretIndex), text.Length), tag); filterSearchTb.CaretIndex += tag.Length; filterSearchTb.Focus(); } diff --git a/Source/LibationAvalonia/Views/ProcessBookControl.axaml.cs b/Source/LibationAvalonia/Views/ProcessBookControl.axaml.cs index 5bb43034..23d42356 100644 --- a/Source/LibationAvalonia/Views/ProcessBookControl.axaml.cs +++ b/Source/LibationAvalonia/Views/ProcessBookControl.axaml.cs @@ -1,11 +1,9 @@ -using ApplicationServices; using Avalonia; using Avalonia.Controls; using DataLayer; using LibationUiBase; using LibationUiBase.ProcessQueue; -#nullable enable namespace LibationAvalonia.Views { public delegate void QueueItemPositionButtonClicked(ProcessBookViewModel? item, QueuePosition queueButton); diff --git a/Source/LibationAvalonia/Views/ProcessQueueControl.axaml.cs b/Source/LibationAvalonia/Views/ProcessQueueControl.axaml.cs index bb85da73..3e24e9bd 100644 --- a/Source/LibationAvalonia/Views/ProcessQueueControl.axaml.cs +++ b/Source/LibationAvalonia/Views/ProcessQueueControl.axaml.cs @@ -1,18 +1,14 @@ -using ApplicationServices; -using Avalonia.Controls; +using Avalonia.Controls; using Avalonia.Data.Converters; using Avalonia.Threading; using DataLayer; -using LibationFileManager; using LibationUiBase; using LibationUiBase.ProcessQueue; using System; -using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Threading.Tasks; -#nullable enable namespace LibationAvalonia.Views { public partial class ProcessQueueControl : UserControl diff --git a/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs b/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs index 48aa4b0c..b4c7d25a 100644 --- a/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs +++ b/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs @@ -21,7 +21,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -#nullable enable namespace LibationAvalonia.Views { public partial class ProductsDisplay : UserControl @@ -94,7 +93,7 @@ namespace LibationAvalonia.Views private void ProductsDisplay_LoadingRow(object sender, DataGridRowEventArgs e) { - if (e.Row.DataContext is LibraryBookEntry entry && entry.Liberate.IsEpisode) + if (e.Row.DataContext is LibraryBookEntry entry && entry.Liberate?.IsEpisode is true) e.Row.DynamicResource(DataGridRow.BackgroundProperty, "SeriesEntryGridBackgroundBrush"); else e.Row.DynamicResource(DataGridRow.BackgroundProperty, "SystemRegionColor"); @@ -543,7 +542,7 @@ namespace LibationAvalonia.Views public void Version_DoubleClick(object sender, Avalonia.Input.TappedEventArgs args) { - if (sender is Control panel && panel.DataContext is LibraryBookEntry lbe && lbe.LastDownload.IsValid) + if (sender is Control panel && panel.DataContext is LibraryBookEntry lbe && lbe.LastDownload?.IsValid is true) lbe.LastDownload.OpenReleaseUrl(); } diff --git a/Source/LibationAvalonia/Views/SeriesViewDialog.axaml.cs b/Source/LibationAvalonia/Views/SeriesViewDialog.axaml.cs index a337c349..726fbce3 100644 --- a/Source/LibationAvalonia/Views/SeriesViewDialog.axaml.cs +++ b/Source/LibationAvalonia/Views/SeriesViewDialog.axaml.cs @@ -11,7 +11,7 @@ namespace LibationAvalonia.Views { public partial class SeriesViewDialog : DialogWindow { - private readonly LibraryBook LibraryBook; + private readonly LibraryBook? LibraryBook; public AvaloniaList TabItems { get; } = new(); public SeriesViewDialog() { @@ -33,10 +33,12 @@ namespace LibationAvalonia.Views LibraryBook = ArgumentValidator.EnsureNotNull(libraryBook, "libraryBook"); } - private async void SeriesViewDialog_Loaded(object sender, Avalonia.Interactivity.RoutedEventArgs e) + private async void SeriesViewDialog_Loaded(object? sender, Avalonia.Interactivity.RoutedEventArgs e) { try { + if (LibraryBook == null) + return; var seriesEntries = await SeriesItem.GetAllSeriesItemsAsync(LibraryBook); foreach (var series in seriesEntries.Keys) diff --git a/Source/LibationAvalonia/Views/SeriesViewGrid.axaml.cs b/Source/LibationAvalonia/Views/SeriesViewGrid.axaml.cs index b41f3f55..2c58471c 100644 --- a/Source/LibationAvalonia/Views/SeriesViewGrid.axaml.cs +++ b/Source/LibationAvalonia/Views/SeriesViewGrid.axaml.cs @@ -14,8 +14,8 @@ namespace LibationAvalonia.Views { public partial class SeriesViewGrid : UserControl { - private ImageDisplayDialog imageDisplayDialog; - private readonly LibraryBook LibraryBook; + private ImageDisplayDialog? imageDisplayDialog; + private readonly LibraryBook? LibraryBook; public AvaloniaList SeriesEntries { get; } = new(); @@ -36,14 +36,14 @@ namespace LibationAvalonia.Views public async void Availability_Click(object sender, Avalonia.Interactivity.RoutedEventArgs args) { - if (sender is Button button && button.DataContext is SeriesItem sentry && sentry.Button.HasButtonAction) + if (LibraryBook is not null && sender is Button button && button.DataContext is SeriesItem sentry && sentry.Button.HasButtonAction) { await sentry.Button.PerformClickAsync(LibraryBook); } } public void Title_Click(object sender, Avalonia.Input.TappedEventArgs args) { - if (sender is not LinkLabel label || label.DataContext is not SeriesItem sentry) + if (LibraryBook is null || sender is not LinkLabel label || label.DataContext is not SeriesItem sentry) return; sentry.ViewOnAudible(LibraryBook.Book.Locale); @@ -63,7 +63,7 @@ namespace LibationAvalonia.Views var picDef = new PictureDefinition(libraryBook.PictureLarge ?? libraryBook.PictureId, PictureSize.Native); - void PictureCached(object sender, PictureCachedEventArgs e) + void PictureCached(object? sender, PictureCachedEventArgs e) { if (e.Definition.PictureId == picDef.PictureId) imageDisplayDialog.SetCoverBytes(e.Picture); diff --git a/Source/LibationAvalonia/Walkthrough.cs b/Source/LibationAvalonia/Walkthrough.cs index e0eb9d2f..378d1bb5 100644 --- a/Source/LibationAvalonia/Walkthrough.cs +++ b/Source/LibationAvalonia/Walkthrough.cs @@ -90,11 +90,13 @@ namespace LibationAvalonia return true; - async Task ShowTabPageMessageBoxAsync(TabItem selectedTab) + async Task ShowTabPageMessageBoxAsync(TabItem? selectedTab) { + if (selectedTab is null) + return; tabsToVisit.Remove(selectedTab); - if (!selectedTab.IsVisible || !(selectedTab.Header is TextBlock header && settingTabMessages.ContainsKey(header.Text))) return; + if (!selectedTab.IsVisible || !(selectedTab.Header is TextBlock header && header.Text is string text && settingTabMessages.ContainsKey(text))) return; if (tabsToVisit.Count == 0) settingsDialog.saveBtn.Content = "Save"; @@ -104,12 +106,12 @@ namespace LibationAvalonia settingTabMessages.Remove(header.Text); } - async void SettingsDialog_Opened(object sender, System.EventArgs e) + async void SettingsDialog_Opened(object? sender, System.EventArgs e) { await ShowTabPageMessageBoxAsync(tabsToVisit[0]); } - async void TabControl_PropertyChanged(object sender, Avalonia.AvaloniaPropertyChangedEventArgs e) + async void TabControl_PropertyChanged(object? sender, Avalonia.AvaloniaPropertyChangedEventArgs e) { if (e.Property == TabItem.IsSelectedProperty && settingsDialog.IsLoaded) { @@ -117,7 +119,7 @@ namespace LibationAvalonia } } - void SettingsDialog_FormClosing(object sender, WindowClosingEventArgs e) + void SettingsDialog_FormClosing(object? sender, WindowClosingEventArgs e) { if (tabsToVisit.Count > 0) { @@ -150,27 +152,27 @@ namespace LibationAvalonia await displayControlAsync(MainForm.importToolStripMenuItem); await displayControlAsync(scanItem); - scanItem.Command.Execute(null); + scanItem.Command?.Execute(null); MainForm.importToolStripMenuItem.Close(); var tcs = new TaskCompletionSource(); LibraryCommands.ScanEnd += LibraryCommands_ScanEnd; await tcs.Task; LibraryCommands.ScanEnd -= LibraryCommands_ScanEnd; - MainForm.ViewModel.ProductsDisplay.VisibleCountChanged -= productsDisplay_VisibleCountChanged; + MainForm.ViewModel?.ProductsDisplay.VisibleCountChanged -= productsDisplay_VisibleCountChanged; return true; - void LibraryCommands_ScanEnd(object sender, int newCount) + void LibraryCommands_ScanEnd(object? sender, int newCount) { //if we imported new books, wait for the grid to update before proceeding. if (newCount > 0) Avalonia.Threading.Dispatcher.UIThread.Invoke(() => - MainForm.ViewModel.ProductsDisplay.VisibleCountChanged += productsDisplay_VisibleCountChanged); + MainForm.ViewModel?.ProductsDisplay.VisibleCountChanged += productsDisplay_VisibleCountChanged); else tcs.SetResult(); } - void productsDisplay_VisibleCountChanged(object sender, int e) => tcs.SetResult(); + void productsDisplay_VisibleCountChanged(object? sender, int e) => tcs.SetResult(); } private async Task ShowSearching() @@ -195,7 +197,7 @@ namespace LibationAvalonia await displayControlAsync(MainForm.filterBtn); - MainForm.filterBtn.Command.Execute(firstAuthor); + MainForm.filterBtn.Command?.Execute(firstAuthor); await Task.Delay(1000); @@ -220,11 +222,11 @@ namespace LibationAvalonia MainForm.filterSearchTb.Text = firstAuthor; - var editQuickFiltersToolStripMenuItem = MainForm.quickFiltersToolStripMenuItem.ItemsSource.OfType().ElementAt(1); + var editQuickFiltersToolStripMenuItem = MainForm.quickFiltersToolStripMenuItem.ItemsSource?.OfType().ElementAt(1); await Task.Delay(750); await displayControlAsync(MainForm.addQuickFilterBtn); - MainForm.addQuickFilterBtn.Command.Execute(firstAuthor); + MainForm.addQuickFilterBtn.Command?.Execute(firstAuthor); await displayControlAsync(MainForm.quickFiltersToolStripMenuItem); await displayControlAsync(editQuickFiltersToolStripMenuItem); @@ -241,14 +243,16 @@ namespace LibationAvalonia return true; } - private string getFirstAuthor() + 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 displayControlAsync(TemplatedControl control) + private async Task displayControlAsync(TemplatedControl? control) { + if (control is null) + return; control.IsEnabled = false; MainForm.productsDisplay.Focus(); await flashControlAsync(control); diff --git a/Source/LibationFileManager/Templates/TemplateEditor[T].cs b/Source/LibationFileManager/Templates/TemplateEditor[T].cs index edd060c2..724f1989 100644 --- a/Source/LibationFileManager/Templates/TemplateEditor[T].cs +++ b/Source/LibationFileManager/Templates/TemplateEditor[T].cs @@ -15,7 +15,7 @@ namespace LibationFileManager.Templates string TemplateName { get; } string TemplateDescription { get; } Templates EditingTemplate { get; } - bool SetTemplateText(string templateText); + bool SetTemplateText(string? templateText); string? GetFolderName(); string? GetFileName(); string? GetName(); @@ -40,7 +40,7 @@ namespace LibationFileManager.Templates private Templates _editingTemplate; - public bool SetTemplateText(string templateText) + public bool SetTemplateText(string? templateText) { if (Templates.TryGetTemplate(templateText, out var template)) { diff --git a/Source/LibationFileManager/Templates/Templates.cs b/Source/LibationFileManager/Templates/Templates.cs index aa44e552..185b0d6c 100644 --- a/Source/LibationFileManager/Templates/Templates.cs +++ b/Source/LibationFileManager/Templates/Templates.cs @@ -41,12 +41,11 @@ namespace LibationFileManager.Templates #region Template Parsing public static T GetTemplate(string? templateText) where T : Templates, ITemplate, new() - => TryGetTemplate(templateText ?? "", out var template) ? template : GetDefaultTemplate(); + => TryGetTemplate(templateText, out var template) ? template : GetDefaultTemplate(); - public static bool TryGetTemplate(string templateText, [NotNullWhen(true)] out T? template) where T : Templates, ITemplate, new() + public static bool TryGetTemplate(string? templateText, out T template) where T : Templates, ITemplate, new() { var namingTemplate = NamingTemplate.Parse(templateText, T.TagCollections); - template = new() { NamingTemplate = namingTemplate }; return !namingTemplate.Errors.Any(); } diff --git a/Source/LibationUiBase/BaseUtil.cs b/Source/LibationUiBase/BaseUtil.cs index 99a6bd8d..9680bb56 100644 --- a/Source/LibationUiBase/BaseUtil.cs +++ b/Source/LibationUiBase/BaseUtil.cs @@ -1,7 +1,6 @@ using LibationFileManager; using System; -#nullable enable namespace LibationUiBase { public static class BaseUtil diff --git a/Source/LibationUiBase/EnumDisplay[T].cs b/Source/LibationUiBase/EnumDisplay[T].cs index 9093a86b..4e94e206 100644 --- a/Source/LibationUiBase/EnumDisplay[T].cs +++ b/Source/LibationUiBase/EnumDisplay[T].cs @@ -7,14 +7,14 @@ namespace LibationUiBase { public T Value { get; } public string Description { get; } - public EnumDisplay(T value, string description = null) + public EnumDisplay(T value, string? description = null) { Value = value; Description = description ?? value.GetDescription() ?? value.ToString(); } public override string ToString() => Description; - public override bool Equals(object obj) + public override bool Equals(object? obj) => (obj is EnumDisplay other && other.Value.Equals(Value)) || (obj is T value && value.Equals(Value)); public override int GetHashCode() => Value.GetHashCode(); } diff --git a/Source/LibationUiBase/GridView/EntryStatus.cs b/Source/LibationUiBase/GridView/EntryStatus.cs index b0d7ccb7..e10687ad 100644 --- a/Source/LibationUiBase/GridView/EntryStatus.cs +++ b/Source/LibationUiBase/GridView/EntryStatus.cs @@ -53,14 +53,14 @@ namespace LibationUiBase.GridView || PdfStatus is not null and not LiberatedStatus.Liberated ); public double Opacity => !IsSeries && Book.UserDefinedItem.Tags.ContainsInsensitive("hidden") ? 0.4 : 1; - public object ButtonImage => GetLiberateIcon(); + public object? ButtonImage => GetLiberateIcon(); public string ToolTip => GetTooltip(); private Book Book { get; } private DateTime lastBookUpdate; private LiberatedStatus bookStatus; private readonly bool isAbsent; - private static readonly Dictionary iconCache = new(); + private static readonly Dictionary iconCache = new(); internal EntryStatus(LibraryBook libraryBook) { @@ -79,7 +79,7 @@ namespace LibationUiBase.GridView } /// Defines the Liberate column's sorting behavior - public int CompareTo(object obj) + public int CompareTo(object? obj) { if (obj is not EntryStatus second) return -1; @@ -94,12 +94,12 @@ namespace LibationUiBase.GridView var statusCompare = BookStatus.CompareTo(second.BookStatus); if (statusCompare != 0) return statusCompare; else if (PdfStatus is null && second.PdfStatus is null) return 0; - else if (PdfStatus is null && second.PdfStatus is not null) return 1; - else if (PdfStatus is not null && second.PdfStatus is null) return -1; + else if (PdfStatus is null) return 1; + else if (second.PdfStatus is null) return -1; else return PdfStatus.Value.CompareTo(second.PdfStatus.Value); } - private object GetLiberateIcon() + private object? GetLiberateIcon() { if (IsSeries) return Expanded ? GetAndCacheResource("minus") : GetAndCacheResource("plus"); @@ -165,7 +165,7 @@ namespace LibationUiBase.GridView return mouseoverText; } - private object GetAndCacheResource(string rescName) + private object? GetAndCacheResource(string rescName) { if (!iconCache.ContainsKey(rescName)) iconCache[rescName] = BaseUtil.LoadResourceImage(rescName); diff --git a/Source/LibationUiBase/GridView/GridContextMenu.cs b/Source/LibationUiBase/GridView/GridContextMenu.cs index e0ff4d91..a5d05d96 100644 --- a/Source/LibationUiBase/GridView/GridContextMenu.cs +++ b/Source/LibationUiBase/GridView/GridContextMenu.cs @@ -18,7 +18,7 @@ public class GridContextMenu public string SetNotDownloadedText => $"Set Download status to '{Accelerator}Not Downloaded'"; public string RemoveText => $"{Accelerator}Remove from library"; public string LocateFileText => $"{Accelerator}Locate file..."; - public string LocateFileDialogTitle => $"Locate the audio file for '{GridEntries[0].Book.TitleWithSubtitle}'"; + public string LocateFileDialogTitle => $"Locate the audio file for '{GridEntries[0].Book?.TitleWithSubtitle ?? "[null]"}'"; public string LocateFileErrorMessage => "Error saving book's location"; public string ConvertToMp3Text => $"{Accelerator}Convert to Mp3"; public string DownloadAsChapters => $"Download {Accelerator}split by chapters"; @@ -29,14 +29,14 @@ public class GridContextMenu public string FileTemplateText => "File Template"; public string MultipartTemplateText => "Multipart File Template"; public string ViewBookmarksText => $"View {Accelerator}Bookmarks/Clips"; - public string ViewSeriesText => GridEntries[0].Liberate.IsSeries ? "View All Episodes in Series" : "View All Books in Series"; + public string ViewSeriesText => GridEntries[0].Liberate?.IsSeries is true ? "View All Episodes in Series" : "View All Books in Series"; - public bool LiberateEpisodesEnabled => GridEntries.OfType().Any(sEntry => sEntry.Children.Any(c => c.Liberate.BookStatus is LiberatedStatus.NotLiberated or LiberatedStatus.PartialDownload)); - public bool SetDownloadedEnabled => LibraryBookEntries.Any(ge => ge.Book.UserDefinedItem.BookStatus != LiberatedStatus.Liberated || ge.Liberate.IsSeries); - public bool SetNotDownloadedEnabled => LibraryBookEntries.Any(ge => ge.Book.UserDefinedItem.BookStatus != LiberatedStatus.NotLiberated || ge.Liberate.IsSeries); - public bool ConvertToMp3Enabled => LibraryBookEntries.Any(ge => ge.Book.UserDefinedItem.BookStatus is LiberatedStatus.Liberated); - public bool DownloadAsChaptersEnabled => LibraryBookEntries.Any(ge => ge.Book.UserDefinedItem.BookStatus is not LiberatedStatus.Error); - public bool ReDownloadEnabled => LibraryBookEntries.Any(ge => ge.Book.UserDefinedItem.BookStatus is LiberatedStatus.Liberated); + public bool LiberateEpisodesEnabled => GridEntries.OfType().Any(sEntry => sEntry.Children.Any(c => c.Liberate?.BookStatus is LiberatedStatus.NotLiberated or LiberatedStatus.PartialDownload)); + public bool SetDownloadedEnabled => LibraryBookEntries.Any(ge => ge.Book?.UserDefinedItem.BookStatus != LiberatedStatus.Liberated || ge.Liberate?.IsSeries is true); + public bool SetNotDownloadedEnabled => LibraryBookEntries.Any(ge => ge.Book?.UserDefinedItem.BookStatus != LiberatedStatus.NotLiberated || ge.Liberate?.IsSeries is true); + public bool ConvertToMp3Enabled => LibraryBookEntries.Any(ge => ge.Book?.UserDefinedItem.BookStatus is LiberatedStatus.Liberated); + public bool DownloadAsChaptersEnabled => LibraryBookEntries.Any(ge => ge.Book?.UserDefinedItem.BookStatus is not LiberatedStatus.Error); + public bool ReDownloadEnabled => LibraryBookEntries.Any(ge => ge.Book?.UserDefinedItem.BookStatus is LiberatedStatus.Liberated); private GridEntry[] GridEntries { get; } public LibraryBookEntry[] LibraryBookEntries { get; } @@ -97,6 +97,6 @@ public class GridContextMenu folderDto = seriesParent?.ToDto() ?? fileDto; } - return TemplateEditor.CreateFilenameEditor(Configuration.Instance.Books, existingTemplate, folderDto, fileDto); + return TemplateEditor.CreateFilenameEditor(Configuration.Instance.Books ?? System.IO.Path.GetTempPath(), existingTemplate, folderDto, fileDto); } } \ No newline at end of file diff --git a/Source/LibationUiBase/GridView/GridEntry.cs b/Source/LibationUiBase/GridView/GridEntry.cs index d2162bb7..84c0f9d5 100644 --- a/Source/LibationUiBase/GridView/GridEntry.cs +++ b/Source/LibationUiBase/GridView/GridEntry.cs @@ -32,34 +32,34 @@ namespace LibationUiBase.GridView #region Model properties exposed to the view protected bool? remove = false; - private Lazy _lazyCover; - private Rating _myRating; + private Lazy? _lazyCover; + private Rating? _myRating; public abstract bool? Remove { get; set; } - public EntryStatus Liberate { get => field; private set => RaiseAndSetIfChanged(ref field, value); } - public string PurchaseDate { get => field; protected set => RaiseAndSetIfChanged(ref field, value); } - public string Length { get => field; protected set => RaiseAndSetIfChanged(ref field, value); } - public LastDownloadStatus LastDownload { get => field; protected set => RaiseAndSetIfChanged(ref field, value); } - public object Cover { get => _lazyCover.Value; } - public string Series { get => field; private set => RaiseAndSetIfChanged(ref field, value); } - public SeriesOrder SeriesOrder { get => field; private set => RaiseAndSetIfChanged(ref field, value); } - public string Title { get => field; private set => RaiseAndSetIfChanged(ref field, value); } - public string Authors { get => field; private set => RaiseAndSetIfChanged(ref field, value); } - public string Narrators { get => field; private set => RaiseAndSetIfChanged(ref field, value); } - public string Category { get => field; private set => RaiseAndSetIfChanged(ref field, value); } - public string Misc { get => field; private set => RaiseAndSetIfChanged(ref field, value); } - public string Description { get => field; private set => RaiseAndSetIfChanged(ref field, value); } - public Rating ProductRating { get => field; private set => RaiseAndSetIfChanged(ref field, value); } - public string BookTags { get => field; private set => RaiseAndSetIfChanged(ref field, value); } + public EntryStatus? Liberate { get => field; private set => RaiseAndSetIfChanged(ref field, value); } + public string? PurchaseDate { get => field; protected set => RaiseAndSetIfChanged(ref field, value); } + public string? Length { get => field; protected set => RaiseAndSetIfChanged(ref field, value); } + public LastDownloadStatus? LastDownload { get => field; protected set => RaiseAndSetIfChanged(ref field, value); } + public object? Cover { get => _lazyCover?.Value; } + public string? Series { get => field; private set => RaiseAndSetIfChanged(ref field, value); } + public SeriesOrder? SeriesOrder { get => field; private set => RaiseAndSetIfChanged(ref field, value); } + public string? Title { get => field; private set => RaiseAndSetIfChanged(ref field, value); } + public string? Authors { get => field; private set => RaiseAndSetIfChanged(ref field, value); } + public string? Narrators { get => field; private set => RaiseAndSetIfChanged(ref field, value); } + public string? Category { get => field; private set => RaiseAndSetIfChanged(ref field, value); } + public string? Misc { get => field; private set => RaiseAndSetIfChanged(ref field, value); } + public string? Description { get => field; private set => RaiseAndSetIfChanged(ref field, value); } + public Rating? ProductRating { get => field; private set => RaiseAndSetIfChanged(ref field, value); } + public string? BookTags { get => field; private set => RaiseAndSetIfChanged(ref field, value); } public bool IsSpatial { get => field; protected set => RaiseAndSetIfChanged(ref field, value); } - public string IncludedUntil { get => field; protected set => RaiseAndSetIfChanged(ref field, value); } - public string Account { get => field; protected set => RaiseAndSetIfChanged(ref field, value); } + public string? IncludedUntil { get => field; protected set => RaiseAndSetIfChanged(ref field, value); } + public string? Account { get => field; protected set => RaiseAndSetIfChanged(ref field, value); } - public Rating MyRating + public Rating? MyRating { get => _myRating; set { - if (_myRating != value && value.OverallRating != 0 && updateReviewTask?.IsCompleted is not false) + if (value is not null && _myRating != value && value.OverallRating != 0 && updateReviewTask?.IsCompleted is not false) updateReviewTask = UpdateRating(value); } } @@ -68,7 +68,7 @@ namespace LibationUiBase.GridView #region User rating - private Task updateReviewTask; + private Task? updateReviewTask; private async Task UpdateRating(Rating rating) { var api = await LibraryBook.GetApiAsync(); @@ -78,6 +78,10 @@ namespace LibationUiBase.GridView } #endregion + protected GridEntry(LibraryBook libraryBook) + { + LibraryBook = libraryBook; + } #region View property updating @@ -113,7 +117,7 @@ namespace LibationUiBase.GridView UserDefinedItem.ItemChanged += UserDefinedItem_ItemChanged; } - protected abstract string GetBookTags(); + protected abstract string? GetBookTags(); protected virtual DateTime GetPurchaseDate() => LibraryBook.DateAdded; protected virtual DateTime? GetIncludedUntil() => LibraryBook.IncludedUntil; protected virtual int GetLengthInMinutes() => Book.LengthInMinutes; @@ -133,11 +137,9 @@ namespace LibationUiBase.GridView /// This event handler receives notifications from the model that it has changed. /// Notify the view that it's changed. /// - private void UserDefinedItem_ItemChanged(object sender, string itemName) + private void UserDefinedItem_ItemChanged(object? sender, string itemName) { - var udi = sender as UserDefinedItem; - - if (udi.Book.AudibleProductId != Book.AudibleProductId) + if (sender is not UserDefinedItem udi || udi.Book.AudibleProductId != Book.AudibleProductId) return; if (udi.Book != LibraryBook.Book) @@ -153,12 +155,12 @@ namespace LibationUiBase.GridView // - Don't restrict notifying view to 'only if property changed'. This same book instance can get passed to a different view, then changed there. When the chain of events makes its way back here, the property is unchanged (because it's the same instance), but this view is out of sync. NotifyPropertyChanged will then update this view. switch (itemName) { - case nameof(udi.BookStatus): - case nameof(udi.PdfStatus): + case nameof(udi.BookStatus) when Liberate is not null: + case nameof(udi.PdfStatus) when Liberate is not null: Liberate.Invalidate(nameof(Liberate.BookStatus), nameof(Liberate.PdfStatus), nameof(Liberate.IsUnavailable), nameof(Liberate.ButtonImage), nameof(Liberate.ToolTip)); RaisePropertyChanged(nameof(Liberate)); break; - case nameof(udi.Tags): + case nameof(udi.Tags) when Liberate is not null: BookTags = GetBookTags(); Liberate.Invalidate(nameof(Liberate.Opacity)); RaisePropertyChanged(nameof(Liberate)); @@ -179,7 +181,7 @@ namespace LibationUiBase.GridView #region Sorting - public object GetMemberValue(string memberName) => memberName switch + public object? GetMemberValue(string? memberName) => memberName switch { nameof(Remove) => Remove.HasValue ? Remove.Value ? RemoveStatus.Removed : RemoveStatus.NotRemoved : RemoveStatus.SomeRemoved, nameof(Title) => Book.TitleSortable(), @@ -204,10 +206,10 @@ namespace LibationUiBase.GridView _ => null }; - public bool MemberValueIsDefault(string memberName) => memberName switch + public bool MemberValueIsDefault(string? memberName) => memberName switch { nameof(Series) => Book.SeriesLink?.Any() is not true, - nameof(SeriesOrder) => string.IsNullOrWhiteSpace(SeriesOrder.OrderString), + nameof(SeriesOrder) => string.IsNullOrWhiteSpace(SeriesOrder?.OrderString), nameof(MyRating) => RatingIsDefault(Book.UserDefinedItem.Rating), nameof(ProductRating) => RatingIsDefault(Book.Rating), nameof(Authors) => string.IsNullOrWhiteSpace(Authors), @@ -223,11 +225,13 @@ namespace LibationUiBase.GridView => rating is null || (rating.OverallRating == 0 && rating.PerformanceRating == 0 && rating.StoryRating == 0); public IComparer GetMemberComparer(Type memberType) - => memberTypeComparers.TryGetValue(memberType, out IComparer value) ? value : memberTypeComparers[memberType.BaseType]; + => memberTypeComparers.TryGetValue(memberType, out IComparer? value) ? value + : memberTypeComparers[memberType?.BaseType ?? typeof(object)]; // Instantiate comparers for every exposed member object type. private static readonly Dictionary memberTypeComparers = new() { + { typeof(object), Comparer.Default }, { typeof(RemoveStatus), Comparer.Default }, { typeof(string), Comparer.Default }, { typeof(int), Comparer .Default }, @@ -253,21 +257,22 @@ namespace LibationUiBase.GridView PictureStorage.PictureCached += PictureStorage_PictureCached; // Mutable property. Set the field so PropertyChanged isn't fired. - _lazyCover = new Lazy(() => BaseUtil.LoadImage(picture, PictureSize._80x80)); + _lazyCover = new Lazy(() => BaseUtil.LoadImage(picture, PictureSize._80x80)); } - private void PictureStorage_PictureCached(object sender, PictureCachedEventArgs e) + private void PictureStorage_PictureCached(object? sender, PictureCachedEventArgs e) { // state validation if (e?.Definition.PictureId is null || Book?.PictureId is null || - e.Picture?.Length == 0) + e.Picture is null || + e.Picture.Length == 0) return; // logic validation if (e.Definition.PictureId == Book.PictureId) { - _lazyCover = new Lazy(() => BaseUtil.LoadImage(e.Picture, PictureSize._80x80)); + _lazyCover = new Lazy(() => BaseUtil.LoadImage(e.Picture, PictureSize._80x80)); RaisePropertyChanged(nameof(Cover)); PictureStorage.PictureCached -= PictureStorage_PictureCached; } @@ -328,7 +333,7 @@ namespace LibationUiBase.GridView /// Creates for all non-episode books in an enumeration of . /// /// Can be called from any thread, but requires the calling thread's to be valid. - public static async Task> GetAllProductsAsync(IEnumerable libraryBooks, Func includeIf, Func factory) + public static async Task> GetAllProductsAsync(IEnumerable libraryBooks, Func includeIf, Func factory) where TEntry : GridEntry { var products = libraryBooks.Where(includeIf).ToArray(); diff --git a/Source/LibationUiBase/GridView/LastDownloadStatus.cs b/Source/LibationUiBase/GridView/LastDownloadStatus.cs index 2427e55f..7c1028b7 100644 --- a/Source/LibationUiBase/GridView/LastDownloadStatus.cs +++ b/Source/LibationUiBase/GridView/LastDownloadStatus.cs @@ -7,11 +7,11 @@ namespace LibationUiBase.GridView public class LastDownloadStatus : IComparable { public bool IsValid => LastDownloadedVersion is not null && LastDownloaded.HasValue; - public AudioFormat LastDownloadedFormat { get; } - public string LastDownloadedFileVersion { get; } - public Version LastDownloadedVersion { get; } + public AudioFormat? LastDownloadedFormat { get; } + public string? LastDownloadedFileVersion { get; } + public Version? LastDownloadedVersion { get; } public DateTime? LastDownloaded { get; } - public string ToolTipText => IsValid ? $"Double click to open v{LastDownloadedVersion.ToVersionString()} release notes" : ""; + public string ToolTipText => IsValid ? $"Double click to open v{LastDownloadedVersion!.ToVersionString()} release notes" : ""; public LastDownloadStatus() { } public LastDownloadStatus(UserDefinedItem udi) @@ -25,28 +25,28 @@ namespace LibationUiBase.GridView public void OpenReleaseUrl() { if (IsValid) - Dinah.Core.Go.To.Url($"{AppScaffolding.LibationScaffolding.RepositoryUrl}/releases/tag/v{LastDownloadedVersion.ToVersionString()}"); + Dinah.Core.Go.To.Url($"{AppScaffolding.LibationScaffolding.RepositoryUrl}/releases/tag/v{LastDownloadedVersion!.ToVersionString()}"); } public override string ToString() => IsValid ? $""" {dateString()} {versionString()} {LastDownloadedFormat} - Libation v{LastDownloadedVersion.ToVersionString()} + Libation v{LastDownloadedVersion!.ToVersionString()} """ : ""; private string versionString() => LastDownloadedFileVersion is string ver ? $"(File v.{ver})" : ""; //Call ToShortDateString to use current culture's date format. - private string dateString() => $"{LastDownloaded.Value.ToShortDateString()} {LastDownloaded.Value:HH:mm}"; + private string dateString() => LastDownloaded.HasValue ? $"{LastDownloaded.Value.ToShortDateString()} {LastDownloaded.Value:HH:mm}" : string.Empty; - public int CompareTo(object obj) + public int CompareTo(object? obj) { if (obj is not LastDownloadStatus second) return -1; - else if (IsValid && !second.IsValid) return -1; - else if (!IsValid && second.IsValid) return 1; else if (!IsValid && !second.IsValid) return 0; - else return LastDownloaded.Value.CompareTo(second.LastDownloaded.Value); + else if (!second.IsValid) return -1; + else if (!IsValid) return 1; + else return LastDownloaded!.Value.CompareTo(second.LastDownloaded!.Value); } } } diff --git a/Source/LibationUiBase/GridView/LibraryBookEntry.cs b/Source/LibationUiBase/GridView/LibraryBookEntry.cs index 10595c24..766b3414 100644 --- a/Source/LibationUiBase/GridView/LibraryBookEntry.cs +++ b/Source/LibationUiBase/GridView/LibraryBookEntry.cs @@ -9,8 +9,8 @@ namespace LibationUiBase.GridView /// The View Model for a LibraryBook that is ContentType.Product or ContentType.Episode public class LibraryBookEntry : GridEntry { - [Browsable(false)] public override DateTime DateAdded => LibraryBook.DateAdded; - [Browsable(false)] public SeriesEntry Parent { get; } + [Browsable(false)] public override DateTime DateAdded => LibraryBook?.DateAdded ?? default; + [Browsable(false)] public SeriesEntry? Parent { get; } public override bool? Remove { @@ -24,7 +24,7 @@ namespace LibationUiBase.GridView } } - public LibraryBookEntry(LibraryBook libraryBook, SeriesEntry parent = null) + public LibraryBookEntry(LibraryBook libraryBook, SeriesEntry? parent = null) : base(libraryBook) { Parent = parent; UpdateLibraryBook(libraryBook); @@ -38,6 +38,7 @@ namespace LibationUiBase.GridView public static async Task> GetAllProductsAsync(IEnumerable libraryBooks) => await GetAllProductsAsync(libraryBooks, lb => lb.Book.IsProduct(), lb => new LibraryBookEntry(lb) as GridEntry); - protected override string GetBookTags() => string.Join("\r\n", Book.UserDefinedItem.TagsEnumerated); + protected override string? GetBookTags() + => Book is null ? null : string.Join("\r\n", Book.UserDefinedItem.TagsEnumerated); } } diff --git a/Source/LibationUiBase/GridView/QueryExtensions.cs b/Source/LibationUiBase/GridView/QueryExtensions.cs index 11c2cefe..d31d315f 100644 --- a/Source/LibationUiBase/GridView/QueryExtensions.cs +++ b/Source/LibationUiBase/GridView/QueryExtensions.cs @@ -7,7 +7,6 @@ using System.Linq; namespace LibationUiBase.GridView { -#nullable enable public static class QueryExtensions { public static IEnumerable BookEntries(this IEnumerable gridEntries) @@ -59,10 +58,9 @@ namespace LibationUiBase.GridView var booksFilteredIn = entries.IntersectBy(searchResultSet.Docs.Select(d => d.ProductId), l => l.AudibleProductId); //Find all series containing children that match the search criteria - var seriesFilteredIn = booksFilteredIn.OfType().Where(lbe => lbe.Parent is not null).Select(lbe => lbe.Parent).Distinct(); + var seriesFilteredIn = booksFilteredIn.OfType().Select(lbe => lbe.Parent).OfType().Distinct(); return booksFilteredIn.Concat(seriesFilteredIn).ToHashSet(); } } -#nullable disable } diff --git a/Source/LibationUiBase/GridView/RowComparerBase.cs b/Source/LibationUiBase/GridView/RowComparerBase.cs index 91298207..677e8ebd 100644 --- a/Source/LibationUiBase/GridView/RowComparerBase.cs +++ b/Source/LibationUiBase/GridView/RowComparerBase.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.ComponentModel; -#nullable enable namespace LibationUiBase.GridView { /// @@ -32,9 +31,14 @@ namespace LibationUiBase.GridView { var val1 = x.GetMemberValue(PropertyName); var val2 = y.GetMemberValue(PropertyName); - var compare = x.GetMemberComparer(val1.GetType()).Compare(val1, val2); - return compare == 0 && x.Liberate.IsSeries && y.Liberate.IsSeries + var type = val1?.GetType() ?? val2?.GetType(); + if (type is null) + return 0; //both null + + var compare = x.GetMemberComparer(type).Compare(val1, val2); + + return compare == 0 && x.Liberate?.IsSeries is true && y.Liberate?.IsSeries is true //Both a and b are series parents and compare as equal, so break the tie. ? x.AudibleProductId.CompareTo(y.AudibleProductId) : compare; @@ -65,10 +69,10 @@ namespace LibationUiBase.GridView { //Podcast episodes usually all have the same PurchaseDate and DateAdded property: //the date that the series was added to the library. So when sorting by PurchaseDate - //and DateAdded, compare SeriesOrder instead.. + //and DateAdded, compare SeriesOrder instead. return PropertyName switch { - nameof(GridEntry.DateAdded) or nameof(GridEntry.PurchaseDate) => geA.SeriesOrder.CompareTo(geB.SeriesOrder), + nameof(GridEntry.DateAdded) or nameof(GridEntry.PurchaseDate) => SeriesOrder.Compare(geA.SeriesOrder, geB.SeriesOrder), _ => InternalCompare(geA, geB), }; } diff --git a/Source/LibationUiBase/GridView/SeriesEntry.cs b/Source/LibationUiBase/GridView/SeriesEntry.cs index 998d8842..3736514a 100644 --- a/Source/LibationUiBase/GridView/SeriesEntry.cs +++ b/Source/LibationUiBase/GridView/SeriesEntry.cs @@ -43,7 +43,7 @@ namespace LibationUiBase.GridView } public SeriesEntry(LibraryBook parent, LibraryBook child) : this(parent, new[] { child }) { } - public SeriesEntry(LibraryBook parent, IEnumerable children) + public SeriesEntry(LibraryBook parent, IEnumerable children) : base(parent) { LastDownload = new(); SeriesIndex = -1; @@ -70,14 +70,14 @@ namespace LibationUiBase.GridView //sort episodes by series order descending and update SeriesEntry foreach (var series in seriesEntries) { - series.Children.Sort((a, b) => -a.SeriesOrder.CompareTo(b.SeriesOrder)); + series.Children.Sort((a, b) => -SeriesOrder.Compare(a.SeriesOrder, b.SeriesOrder)); series.UpdateLibraryBook(series.LibraryBook); } return seriesEntries.Where(s => s.Children.Count != 0).ToList(); //Create a LibraryBookEntry for an episode and link it to its series parent - LibraryBookEntry CreateAndLinkEpisodeEntry(LibraryBook episode) + LibraryBookEntry? CreateAndLinkEpisodeEntry(LibraryBook episode) { foreach (var s in episode.Book.SeriesLink) { @@ -99,7 +99,7 @@ namespace LibationUiBase.GridView Length = GetBookLengthString(); } - protected override string GetBookTags() => null; + protected override string? GetBookTags() => null; protected override int GetLengthInMinutes() => Children.Count == 0 ? 0 : Children.Sum(c => c.LibraryBook.Book.LengthInMinutes); protected override DateTime GetPurchaseDate() => Children.Count == 0 ? default : Children.Min(c => c.LibraryBook.DateAdded); protected override DateTime? GetIncludedUntil() => Children.Count == 0 ? default : Children.Min(c => c.LibraryBook.IncludedUntil); diff --git a/Source/LibationUiBase/GridView/SeriesOrder.cs b/Source/LibationUiBase/GridView/SeriesOrder.cs index 944df92e..ccf8f5bc 100644 --- a/Source/LibationUiBase/GridView/SeriesOrder.cs +++ b/Source/LibationUiBase/GridView/SeriesOrder.cs @@ -22,7 +22,7 @@ namespace LibationUiBase.GridView } public override string ToString() => OrderString; - public int CompareTo(object obj) + public int CompareTo(object? obj) { if (obj is not SeriesOrder other) return 1; @@ -37,5 +37,13 @@ namespace LibationUiBase.GridView if (Orders.Length > other.Orders.Length) return -1; return 0; } + + public static int Compare(SeriesOrder? a, SeriesOrder? b) + { + if (a is null && b is null) return 0; + else if (a is null) return 1; + else if (b is null) return -1; + else return a.CompareTo(b); + } } } \ No newline at end of file diff --git a/Source/LibationUiBase/LibationSetup.cs b/Source/LibationUiBase/LibationSetup.cs index 9d456e73..8df3e9c2 100644 --- a/Source/LibationUiBase/LibationSetup.cs +++ b/Source/LibationUiBase/LibationSetup.cs @@ -9,7 +9,6 @@ using System; using System.IO; using System.Threading.Tasks; -#nullable enable namespace LibationUiBase; /// diff --git a/Source/LibationUiBase/LibationUiBase.csproj b/Source/LibationUiBase/LibationUiBase.csproj index 9e9efc79..d97f8424 100644 --- a/Source/LibationUiBase/LibationUiBase.csproj +++ b/Source/LibationUiBase/LibationUiBase.csproj @@ -3,6 +3,7 @@ net10.0 true + enable true false false diff --git a/Source/LibationUiBase/MessageBoxBase.cs b/Source/LibationUiBase/MessageBoxBase.cs index 02aed519..740ee0b2 100644 --- a/Source/LibationUiBase/MessageBoxBase.cs +++ b/Source/LibationUiBase/MessageBoxBase.cs @@ -1,6 +1,5 @@ using System.Threading.Tasks; -#nullable enable namespace LibationUiBase.Forms; public enum DialogResult diff --git a/Source/LibationUiBase/ProcessQueue/ProcessBookViewModel.cs b/Source/LibationUiBase/ProcessQueue/ProcessBookViewModel.cs index 5d8dd692..9b1a886e 100644 --- a/Source/LibationUiBase/ProcessQueue/ProcessBookViewModel.cs +++ b/Source/LibationUiBase/ProcessQueue/ProcessBookViewModel.cs @@ -12,7 +12,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -#nullable enable namespace LibationUiBase.ProcessQueue; public enum ProcessBookResult diff --git a/Source/LibationUiBase/ProcessQueue/ProcessQueueViewModel.cs b/Source/LibationUiBase/ProcessQueue/ProcessQueueViewModel.cs index bba5da0b..252271e6 100644 --- a/Source/LibationUiBase/ProcessQueue/ProcessQueueViewModel.cs +++ b/Source/LibationUiBase/ProcessQueue/ProcessQueueViewModel.cs @@ -8,7 +8,6 @@ using System.Collections.ObjectModel; using System.Linq; using System.Threading.Tasks; -#nullable enable namespace LibationUiBase.ProcessQueue; public record LogEntry(DateTime LogDate, string LogMessage) diff --git a/Source/LibationUiBase/ReactiveObject.cs b/Source/LibationUiBase/ReactiveObject.cs index 53392311..b7762132 100644 --- a/Source/LibationUiBase/ReactiveObject.cs +++ b/Source/LibationUiBase/ReactiveObject.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.ComponentModel; using System.Runtime.CompilerServices; -#nullable enable namespace LibationUiBase; /// diff --git a/Source/LibationUiBase/SeriesView/AyceButton.cs b/Source/LibationUiBase/SeriesView/AyceButton.cs index 842dc85f..2d0919d7 100644 --- a/Source/LibationUiBase/SeriesView/AyceButton.cs +++ b/Source/LibationUiBase/SeriesView/AyceButton.cs @@ -14,7 +14,7 @@ namespace LibationUiBase.SeriesView { //Making this event and field static prevents concurrent additions to the library. //Search engine indexer does not support concurrent re-indexing. - private static event EventHandler ButtonEnabled; + private static event EventHandler? ButtonEnabled; private static bool globalEnabled = true; public override bool HasButtonAction => true; @@ -82,7 +82,7 @@ namespace LibationUiBase.SeriesView if (!await api.AddItemToLibraryAsync(Item.ProductId)) return; - Item item = null; + Item? item = null; for (int tryCount = 0; tryCount < 5 && item is null; tryCount++) { @@ -115,10 +115,10 @@ namespace LibationUiBase.SeriesView InLibrary = await LibraryCommands.ImportSingleToDbAsync(item, accountBook.Account, accountBook.Book.Locale) != 0; } - private void DownloadButton_ButtonEnabled(object sender, EventArgs e) + private void DownloadButton_ButtonEnabled(object? sender, EventArgs e) => RaisePropertyChanged(nameof(Enabled)); - public override int CompareTo(object ob) + public override int CompareTo(object? ob) { if (ob is not AyceButton other) return 1; return other.InLibrary.CompareTo(InLibrary); diff --git a/Source/LibationUiBase/SeriesView/SeriesButton.cs b/Source/LibationUiBase/SeriesView/SeriesButton.cs index 94d79f2d..4e95e17f 100644 --- a/Source/LibationUiBase/SeriesView/SeriesButton.cs +++ b/Source/LibationUiBase/SeriesView/SeriesButton.cs @@ -39,6 +39,6 @@ namespace LibationUiBase.SeriesView public override string ToString() => DisplayText; - public abstract int CompareTo(object ob); + public abstract int CompareTo(object? ob); } } diff --git a/Source/LibationUiBase/SeriesView/SeriesItem.cs b/Source/LibationUiBase/SeriesView/SeriesItem.cs index cd7d19e1..37f725c6 100644 --- a/Source/LibationUiBase/SeriesView/SeriesItem.cs +++ b/Source/LibationUiBase/SeriesView/SeriesItem.cs @@ -16,7 +16,7 @@ namespace LibationUiBase.SeriesView { public class SeriesItem : ReactiveObject { - public object Cover { get; private set; } + public object? Cover { get; private set; } public SeriesOrder Order { get; } public string Title => Item.TitleWithSubtitle; public SeriesButton Button { get; } @@ -38,7 +38,7 @@ namespace LibationUiBase.SeriesView Go.To.Url(link); } - private void DownloadButton_PropertyChanged(object sender, PropertyChangedEventArgs e) + private void DownloadButton_PropertyChanged(object? sender, PropertyChangedEventArgs e) => RaisePropertyChanged(nameof(Button)); private void LoadCover(string pictureId) @@ -51,7 +51,7 @@ namespace LibationUiBase.SeriesView Cover = BaseUtil.LoadImage(picture, PictureSize._80x80); } - private void PictureStorage_PictureCached(object sender, PictureCachedEventArgs e) + private void PictureStorage_PictureCached(object? sender, PictureCachedEventArgs e) { if (e?.Definition.PictureId != null && Item?.PictureId != null) { diff --git a/Source/LibationUiBase/SeriesView/SeriesOrder.cs b/Source/LibationUiBase/SeriesView/SeriesOrder.cs index ad09dc5f..795097fd 100644 --- a/Source/LibationUiBase/SeriesView/SeriesOrder.cs +++ b/Source/LibationUiBase/SeriesView/SeriesOrder.cs @@ -14,7 +14,7 @@ namespace LibationUiBase.SeriesView } public override string ToString() => OrderString; - public int CompareTo(object obj) + public int CompareTo(object? obj) { if (obj is not SeriesOrder other) return 1; return Order.CompareTo(other.Order); diff --git a/Source/LibationUiBase/SeriesView/WishlistButton.cs b/Source/LibationUiBase/SeriesView/WishlistButton.cs index 8a236165..3f5a4b99 100644 --- a/Source/LibationUiBase/SeriesView/WishlistButton.cs +++ b/Source/LibationUiBase/SeriesView/WishlistButton.cs @@ -75,7 +75,7 @@ namespace LibationUiBase.SeriesView finally { Enabled = true; } } - public override int CompareTo(object ob) + public override int CompareTo(object? ob) { if (ob is not WishlistButton other) return -1; diff --git a/Source/LibationUiBase/TrackedQueue[T].cs b/Source/LibationUiBase/TrackedQueue[T].cs index e90c63b2..5ca28f16 100644 --- a/Source/LibationUiBase/TrackedQueue[T].cs +++ b/Source/LibationUiBase/TrackedQueue[T].cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; -#nullable enable namespace LibationUiBase { public enum QueuePosition diff --git a/Source/LibationUiBase/Upgrader.cs b/Source/LibationUiBase/Upgrader.cs index 1a552fe4..3166bf18 100644 --- a/Source/LibationUiBase/Upgrader.cs +++ b/Source/LibationUiBase/Upgrader.cs @@ -6,7 +6,6 @@ using System.IO; using System.Net.Http; using System.Threading.Tasks; -#nullable enable namespace LibationUiBase { public class UpgradeEventArgs diff --git a/Source/LibationWinForms/GridView/LiberateDataGridViewImageButtonColumn.cs b/Source/LibationWinForms/GridView/LiberateDataGridViewImageButtonColumn.cs index 05b12a75..164a37f1 100644 --- a/Source/LibationWinForms/GridView/LiberateDataGridViewImageButtonColumn.cs +++ b/Source/LibationWinForms/GridView/LiberateDataGridViewImageButtonColumn.cs @@ -38,7 +38,8 @@ namespace LibationWinForms.GridView row.DefaultCellStyle.ForeColor = status.Opacity == 1 ? grid.DefaultCellStyle.ForeColor : HiddenForeColor; base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, null, null, null, cellStyle, advancedBorderStyle, paintParts); - DrawButtonImage(graphics, (Image)status.ButtonImage, cellBounds); + if (status.ButtonImage is Image buttonImage) + DrawButtonImage(graphics, buttonImage, cellBounds); AccessibilityDescription = status.ToolTip; if (status.IsUnavailable || status.Opacity < 1) diff --git a/Source/LibationWinForms/GridView/ProductsGrid.cs b/Source/LibationWinForms/GridView/ProductsGrid.cs index 0d754e2a..a3ef2d78 100644 --- a/Source/LibationWinForms/GridView/ProductsGrid.cs +++ b/Source/LibationWinForms/GridView/ProductsGrid.cs @@ -239,7 +239,7 @@ namespace LibationWinForms.GridView } else if (entry is SeriesEntry sEntry) { - if (e.ColumnIndex == liberateGVColumn.Index) + if (e.ColumnIndex == liberateGVColumn.Index && sEntry.Liberate is not null) { if (sEntry.Liberate.Expanded) bindingList?.CollapseItem(sEntry); @@ -390,8 +390,8 @@ namespace LibationWinForms.GridView throw new InvalidOperationException($"Must call {nameof(BindToGridAsync)} before calling {nameof(RemoveBooks)}"); //Remove books in series from their parents' Children list - foreach (var removed in removedBooks.Where(b => b.Liberate.IsEpisode)) - removed.Parent.RemoveChild(removed); + foreach (var removed in removedBooks.Where(b => b.Liberate?.IsEpisode is true)) + removed.Parent?.RemoveChild(removed); //Remove series that have no children var removedSeries = @@ -448,7 +448,7 @@ namespace LibationWinForms.GridView seriesEntries.Add(seriesEntry); episodeEntry = seriesEntry.Children[0]; - seriesEntry.Liberate.Expanded = true; + seriesEntry.Liberate?.Expanded = true; bindingList.Insert(0, seriesEntry); } else @@ -463,7 +463,7 @@ namespace LibationWinForms.GridView //Series entry must be expanded so its child can //be placed in the correct position beneath it. - var isExpanded = seriesEntry.Liberate.Expanded; + var isExpanded = seriesEntry.Liberate?.Expanded; bindingList.ExpandItem(seriesEntry); //Add episode to the grid beneath the parent @@ -471,9 +471,9 @@ namespace LibationWinForms.GridView int episodeIndex = seriesEntry.Children.IndexOf(episodeEntry); bindingList.Insert(seriesIndex + 1 + episodeIndex, episodeEntry); - if (isExpanded) + if (isExpanded.HasValue && isExpanded.Value) bindingList.ExpandItem(seriesEntry); - else + else if (isExpanded.HasValue) bindingList.CollapseItem(seriesEntry); } else diff --git a/Source/_Tests/AudibleUtilities.Tests/AccountTests.cs b/Source/_Tests/AudibleUtilities.Tests/AccountTests.cs index 8a40991f..6979a239 100644 --- a/Source/_Tests/AudibleUtilities.Tests/AccountTests.cs +++ b/Source/_Tests/AudibleUtilities.Tests/AccountTests.cs @@ -7,6 +7,8 @@ using AudibleUtilities; using Microsoft.VisualStudio.TestTools.UnitTesting; using Newtonsoft.Json; +[assembly: Parallelize] + namespace AccountsTests { #pragma warning disable CS8981 // The type name only contains lower-cased ascii characters. Such names may become reserved for the language. diff --git a/Source/_Tests/FileLiberator.Tests/DownloadDecryptBookTests.cs b/Source/_Tests/FileLiberator.Tests/DownloadDecryptBookTests.cs index d6f64d9c..7e13097c 100644 --- a/Source/_Tests/FileLiberator.Tests/DownloadDecryptBookTests.cs +++ b/Source/_Tests/FileLiberator.Tests/DownloadDecryptBookTests.cs @@ -3,6 +3,8 @@ using AssertionHelper; using AudibleApi.Common; using Microsoft.VisualStudio.TestTools.UnitTesting; +[assembly: Parallelize] + namespace FileLiberator.Tests { [TestClass] diff --git a/Source/_Tests/FileManager.Tests/FileUtilityTests.cs b/Source/_Tests/FileManager.Tests/FileUtilityTests.cs index 4cc67b30..4256ef65 100644 --- a/Source/_Tests/FileManager.Tests/FileUtilityTests.cs +++ b/Source/_Tests/FileManager.Tests/FileUtilityTests.cs @@ -3,6 +3,8 @@ using AssertionHelper; using FileManager; using Microsoft.VisualStudio.TestTools.UnitTesting; +[assembly: Parallelize] + namespace FileUtilityTests { [TestClass] diff --git a/Source/_Tests/LibationFileManager.Tests/TemplatesTests.cs b/Source/_Tests/LibationFileManager.Tests/TemplatesTests.cs index 8c823700..db339c92 100644 --- a/Source/_Tests/LibationFileManager.Tests/TemplatesTests.cs +++ b/Source/_Tests/LibationFileManager.Tests/TemplatesTests.cs @@ -11,6 +11,8 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using static TemplatesTests.Shared; +[assembly: Parallelize] + namespace TemplatesTests { ///////////////////////////////////////////////// diff --git a/Source/_Tests/LibationSearchEngine.Tests/SearchEngineTests.cs b/Source/_Tests/LibationSearchEngine.Tests/SearchEngineTests.cs index eb62fd52..e3975860 100644 --- a/Source/_Tests/LibationSearchEngine.Tests/SearchEngineTests.cs +++ b/Source/_Tests/LibationSearchEngine.Tests/SearchEngineTests.cs @@ -3,6 +3,8 @@ using LibationSearchEngine; using Lucene.Net.Analysis.Standard; using Microsoft.VisualStudio.TestTools.UnitTesting; +[assembly: Parallelize] + namespace SearchEngineTests { [TestClass] diff --git a/Source/_Tests/LibationUiBase.Tests/LibationFilesTest.cs b/Source/_Tests/LibationUiBase.Tests/LibationFilesTest.cs index fd6d8239..c2afe00c 100644 --- a/Source/_Tests/LibationUiBase.Tests/LibationFilesTest.cs +++ b/Source/_Tests/LibationUiBase.Tests/LibationFilesTest.cs @@ -2,6 +2,8 @@ using LibationUiBase.Forms; using Newtonsoft.Json.Linq; +[assembly: DoNotParallelize] + namespace LibationUiBase.Tests; [TestClass]