using ApplicationServices; using Avalonia; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Input; using Avalonia.Markup.Xaml; using Avalonia.Media; using Avalonia.Platform; using Avalonia.Styling; using LibationAvalonia.Dialogs; using LibationAvalonia.Views; using LibationFileManager; using System; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; using ReactiveUI; using DataLayer; namespace LibationAvalonia { public class App : Application { public static Window MainWindow { get; private set; } public static IBrush ProcessQueueBookFailedBrush { get; private set; } public static IBrush ProcessQueueBookCompletedBrush { get; private set; } public static IBrush ProcessQueueBookCancelledBrush { get; private set; } public static IBrush ProcessQueueBookDefaultBrush { get; private set; } public static IBrush SeriesEntryGridBackgroundBrush { get; private set; } public static readonly Uri AssetUriBase = new("avares://Libation/Assets/"); public static Stream OpenAsset(string assetRelativePath) => AssetLoader.Open(new Uri(AssetUriBase, assetRelativePath)); public override void Initialize() { AvaloniaXamlLoader.Load(this); } public static Task> LibraryTask; public override void OnFrameworkInitializationCompleted() { if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { var config = Configuration.Instance; if (!config.LibationSettingsAreValid) { var defaultLibationFilesDir = Configuration.UserProfile; // check for existing settings in default location var defaultSettingsFile = Path.Combine(defaultLibationFilesDir, "Settings.json"); if (Configuration.SettingsFileIsValid(defaultSettingsFile)) Configuration.SetLibationFiles(defaultLibationFilesDir); if (config.LibationSettingsAreValid) { LibraryTask = Task.Run(() => DbContexts.GetLibrary_Flat_NoTracking(includeParents: true)); ShowMainWindow(desktop); } else { var setupDialog = new SetupDialog { Config = config }; setupDialog.Closing += Setup_Closing; desktop.MainWindow = setupDialog; } } else ShowMainWindow(desktop); } base.OnFrameworkInitializationCompleted(); } private async void Setup_Closing(object sender, System.ComponentModel.CancelEventArgs e) { var setupDialog = sender as SetupDialog; var desktop = ApplicationLifetime as IClassicDesktopStyleApplicationLifetime; try { // all returns should be preceded by either: // - if config.LibationSettingsAreValid // - error message, Exit() if (setupDialog.IsNewUser) { Configuration.SetLibationFiles(Configuration.UserProfile); setupDialog.Config.Books = Path.Combine(Configuration.UserProfile, nameof(Configuration.Books)); if (setupDialog.Config.LibationSettingsAreValid) { var theme = setupDialog.SelectedTheme.Content is nameof(ThemeVariant.Dark) ? nameof(ThemeVariant.Dark) : nameof(ThemeVariant.Light); setupDialog.Config.SetString(theme, nameof(ThemeVariant)); await RunMigrationsAsync(setupDialog.Config); LibraryTask = Task.Run(() => DbContexts.GetLibrary_Flat_NoTracking(includeParents: true)); AudibleUtilities.AudibleApiStorage.EnsureAccountsSettingsFileExists(); ShowMainWindow(desktop); } else await CancelInstallation(); } else if (setupDialog.IsReturningUser) { ShowLibationFilesDialog(desktop, setupDialog.Config, OnLibationFilesCompleted); } else { await CancelInstallation(); return; } } catch (Exception ex) { var title = "Fatal error, pre-logging"; var body = "An unrecoverable error occurred. Since this error happened before logging could be initialized, this error can not be written to the log file."; try { await MessageBox.ShowAdminAlert(null, body, title, ex); } catch { await MessageBox.Show($"{body}\r\n\r\n{ex.Message}\r\n\r\n{ex.StackTrace}", title, MessageBoxButtons.OK, MessageBoxIcon.Error); } return; } } private async Task RunMigrationsAsync(Configuration config) { // most migrations go in here AppScaffolding.LibationScaffolding.RunPostConfigMigrations(config); await MessageBox.VerboseLoggingWarning_ShowIfTrue(); // logging is init'd here AppScaffolding.LibationScaffolding.RunPostMigrationScaffolding(config); } private void ShowLibationFilesDialog(IClassicDesktopStyleApplicationLifetime desktop, Configuration config, Action OnClose) { var libationFilesDialog = new LibationFilesDialog(); desktop.MainWindow = libationFilesDialog; libationFilesDialog.Show(); void WindowClosing(object sender, System.ComponentModel.CancelEventArgs e) { libationFilesDialog.Closing -= WindowClosing; e.Cancel = true; OnClose?.Invoke(desktop, libationFilesDialog, config); } libationFilesDialog.Closing += WindowClosing; } private async void OnLibationFilesCompleted(IClassicDesktopStyleApplicationLifetime desktop, LibationFilesDialog libationFilesDialog, Configuration config) { Configuration.SetLibationFiles(libationFilesDialog.SelectedDirectory); if (config.LibationSettingsAreValid) { await RunMigrationsAsync(config); LibraryTask = Task.Run(() => DbContexts.GetLibrary_Flat_NoTracking(includeParents: true)); AudibleUtilities.AudibleApiStorage.EnsureAccountsSettingsFileExists(); ShowMainWindow(desktop); } else { // path did not result in valid settings var continueResult = await MessageBox.Show( $"No valid settings were found at this location.\r\nWould you like to create a new install settings in this folder?\r\n\r\n{libationFilesDialog.SelectedDirectory}", "New install?", MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (continueResult == DialogResult.Yes) { config.Books = Path.Combine(libationFilesDialog.SelectedDirectory, nameof(Configuration.Books)); if (config.LibationSettingsAreValid) { await RunMigrationsAsync(config); LibraryTask = Task.Run(() => DbContexts.GetLibrary_Flat_NoTracking(includeParents: true)); AudibleUtilities.AudibleApiStorage.EnsureAccountsSettingsFileExists(); ShowMainWindow(desktop); } else await CancelInstallation(); } else await CancelInstallation(); } libationFilesDialog.Close(); } static async Task CancelInstallation() { await MessageBox.Show("Initial set up cancelled.", "Cancelled", MessageBoxButtons.OK, MessageBoxIcon.Warning); Environment.Exit(0); } private static void ShowMainWindow(IClassicDesktopStyleApplicationLifetime desktop) { Current.RequestedThemeVariant = Configuration.Instance.GetString(propertyName: nameof(ThemeVariant)) is "Dark" ? ThemeVariant.Dark : ThemeVariant.Light; //Reload colors for current theme LoadStyles(); var mainWindow = new MainWindow(); desktop.MainWindow = MainWindow = mainWindow; mainWindow.OnLibraryLoaded(LibraryTask.GetAwaiter().GetResult()); mainWindow.RestoreSizeAndLocation(Configuration.Instance); mainWindow.Show(); } private static void LoadStyles() { ProcessQueueBookFailedBrush = AvaloniaUtils.GetBrushFromResources(nameof(ProcessQueueBookFailedBrush)); ProcessQueueBookCompletedBrush = AvaloniaUtils.GetBrushFromResources(nameof(ProcessQueueBookCompletedBrush)); ProcessQueueBookCancelledBrush = AvaloniaUtils.GetBrushFromResources(nameof(ProcessQueueBookCancelledBrush)); SeriesEntryGridBackgroundBrush = AvaloniaUtils.GetBrushFromResources(nameof(SeriesEntryGridBackgroundBrush)); ProcessQueueBookDefaultBrush = AvaloniaUtils.GetBrushFromResources(nameof(ProcessQueueBookDefaultBrush)); } } }