Add Avalonia setup
This commit is contained in:
parent
ffd947eb2e
commit
fe6cfc899b
@ -6,6 +6,11 @@ using LibationFileManager;
|
||||
using LibationAvalonia.Views;
|
||||
using System;
|
||||
using Avalonia.Platform;
|
||||
using LibationAvalonia.Dialogs;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using ApplicationServices;
|
||||
|
||||
namespace LibationAvalonia
|
||||
{
|
||||
@ -31,6 +36,9 @@ namespace LibationAvalonia
|
||||
AssetLoader = AvaloniaLocator.Current.GetService<IAssetLoader>();
|
||||
}
|
||||
|
||||
public static Task<List<DataLayer.LibraryBook>> LibraryTask;
|
||||
public static bool SetupRequired;
|
||||
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
{
|
||||
LoadStyles();
|
||||
@ -41,16 +49,140 @@ namespace LibationAvalonia
|
||||
|
||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
var mainWindow = new MainWindow();
|
||||
desktop.MainWindow = mainWindow;
|
||||
mainWindow.RestoreSizeAndLocation(Configuration.Instance);
|
||||
mainWindow.OnLoad();
|
||||
if (SetupRequired)
|
||||
{
|
||||
var config = Configuration.Instance;
|
||||
|
||||
var defaultLibationFilesDir = Configuration.UserProfile;
|
||||
|
||||
// check for existing settings in default location
|
||||
var defaultSettingsFile = Path.Combine(defaultLibationFilesDir, "Settings.json");
|
||||
if (Configuration.SettingsFileIsValid(defaultSettingsFile))
|
||||
config.SetLibationFiles(defaultLibationFilesDir);
|
||||
|
||||
if (config.LibationSettingsAreValid)
|
||||
return;
|
||||
|
||||
var setupDialog = new SetupDialog { Config = config };
|
||||
setupDialog.Closing += Setup_Closing;
|
||||
|
||||
desktop.MainWindow = setupDialog;
|
||||
}
|
||||
else
|
||||
ShowMainWindow(desktop);
|
||||
}
|
||||
|
||||
base.OnFrameworkInitializationCompleted();
|
||||
}
|
||||
|
||||
private void LoadStyles()
|
||||
private 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
|
||||
&& !setupDialog.IsReturningUser) ||
|
||||
!RunInstall(setupDialog))
|
||||
{
|
||||
CancelInstallation();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// most migrations go in here
|
||||
AppScaffolding.LibationScaffolding.RunPostConfigMigrations(setupDialog.Config);
|
||||
|
||||
MessageBox.VerboseLoggingWarning_ShowIfTrue();
|
||||
|
||||
#if !DEBUG
|
||||
checkForUpdate();
|
||||
#endif
|
||||
// logging is init'd here
|
||||
AppScaffolding.LibationScaffolding.RunPostMigrationScaffolding(setupDialog.Config);
|
||||
|
||||
}
|
||||
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
|
||||
{
|
||||
MessageBox.ShowAdminAlert(null, body, title, ex);
|
||||
}
|
||||
catch
|
||||
{
|
||||
MessageBox.Show($"{body}\r\n\r\n{ex.Message}\r\n\r\n{ex.StackTrace}", title, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
LibraryTask = Task.Run(() => DbContexts.GetLibrary_Flat_NoTracking(includeParents: true));
|
||||
AudibleUtilities.AudibleApiStorage.EnsureAccountsSettingsFileExists();
|
||||
ShowMainWindow(desktop);
|
||||
}
|
||||
|
||||
private static bool RunInstall(SetupDialog setupDialog)
|
||||
{
|
||||
var config = setupDialog.Config;
|
||||
|
||||
if (setupDialog.IsNewUser)
|
||||
{
|
||||
config.SetLibationFiles(Configuration.UserProfile);
|
||||
}
|
||||
else if (setupDialog.IsReturningUser)
|
||||
{
|
||||
|
||||
var libationFilesDialog = new LibationFilesDialog();
|
||||
|
||||
if (libationFilesDialog.ShowDialogSynchronously<DialogResult>(setupDialog) != DialogResult.OK)
|
||||
return false;
|
||||
|
||||
config.SetLibationFiles(libationFilesDialog.SelectedDirectory);
|
||||
if (config.LibationSettingsAreValid)
|
||||
return true;
|
||||
|
||||
// path did not result in valid settings
|
||||
var continueResult = 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)
|
||||
return false;
|
||||
}
|
||||
|
||||
// INIT DEFAULT SETTINGS
|
||||
// if 'new user' was clicked, or if 'returning user' chose new install: show basic settings dialog
|
||||
config.Books ??= Path.Combine(Configuration.UserProfile, "Books");
|
||||
|
||||
return new SettingsDialog().ShowDialogSynchronously<DialogResult>(setupDialog) == DialogResult.OK
|
||||
&& config.LibationSettingsAreValid;
|
||||
}
|
||||
|
||||
static void CancelInstallation()
|
||||
{
|
||||
MessageBox.Show("Initial set up cancelled.", "Cancelled", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
private static void ShowMainWindow(IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
var mainWindow = new MainWindow();
|
||||
desktop.MainWindow = mainWindow;
|
||||
mainWindow.RestoreSizeAndLocation(Configuration.Instance);
|
||||
mainWindow.OnLoad();
|
||||
mainWindow.OnLibraryLoaded(LibraryTask.GetAwaiter().GetResult());
|
||||
mainWindow.Show();
|
||||
}
|
||||
|
||||
private static void LoadStyles()
|
||||
{
|
||||
ProcessQueueBookFailedBrush = AvaloniaUtils.GetBrushFromResources("ProcessQueueBookFailedBrush");
|
||||
ProcessQueueBookCompletedBrush = AvaloniaUtils.GetBrushFromResources("ProcessQueueBookCompletedBrush");
|
||||
|
||||
16
Source/LibationAvalonia/AppBasic.axaml
Normal file
16
Source/LibationAvalonia/AppBasic.axaml
Normal file
@ -0,0 +1,16 @@
|
||||
<Application xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x2="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:LibationAvalonia"
|
||||
x2:Class="LibationAvalonia.AppBasic">
|
||||
|
||||
<Application.DataTemplates>
|
||||
<local:ViewLocator/>
|
||||
</Application.DataTemplates>
|
||||
|
||||
<Application.Styles>
|
||||
<FluentTheme Mode="Light"/>
|
||||
<StyleInclude Source="avares://Avalonia.Themes.Default/DefaultTheme.xaml"/>
|
||||
<StyleInclude Source="avares://Avalonia.Themes.Default/Accents/BaseLight.xaml"/>
|
||||
<StyleInclude Source="/Assets/LibationStyles.xaml"/>
|
||||
</Application.Styles>
|
||||
</Application>
|
||||
28
Source/LibationAvalonia/AppBasic.axaml.cs
Normal file
28
Source/LibationAvalonia/AppBasic.axaml.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Controls;
|
||||
using LibationAvalonia.Dialogs;
|
||||
|
||||
namespace LibationAvalonia
|
||||
{
|
||||
public class AppBasic : Application
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
public Window MainWindow { get; set; }
|
||||
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
{
|
||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
desktop.MainWindow = new SetupDialog();
|
||||
}
|
||||
|
||||
base.OnFrameworkInitializationCompleted();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -8,6 +8,7 @@ namespace LibationAvalonia.Dialogs
|
||||
{
|
||||
public abstract class DialogWindow : Window
|
||||
{
|
||||
public bool SaveAndRestorePosition { get; set; } = true;
|
||||
public Control ControlToFocusOnShow { get; set; }
|
||||
public DialogWindow()
|
||||
{
|
||||
@ -21,15 +22,21 @@ namespace LibationAvalonia.Dialogs
|
||||
this.AttachDevTools();
|
||||
#endif
|
||||
}
|
||||
public DialogWindow(bool saveAndRestorePosition) : this()
|
||||
{
|
||||
SaveAndRestorePosition = saveAndRestorePosition;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (SaveAndRestorePosition)
|
||||
this.SaveSizeAndLocation(Configuration.Instance);
|
||||
}
|
||||
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
Grid.Row="0"
|
||||
Margin="5"
|
||||
Directory="{Binding Directory, Mode=TwoWay}"
|
||||
SubDirectory=""
|
||||
KnownDirectories="{Binding KnownDirectories}" />
|
||||
|
||||
<Button
|
||||
@ -24,6 +25,7 @@
|
||||
HorizontalAlignment="Right"
|
||||
Margin="5"
|
||||
Padding="30,3,30,3"
|
||||
Content="Save" />
|
||||
Content="Save"
|
||||
Click="SaveButton_Click" />
|
||||
</Grid>
|
||||
</Window>
|
||||
|
||||
@ -8,7 +8,7 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace LibationAvalonia.Dialogs
|
||||
{
|
||||
public partial class LibationFilesDialog : DialogWindow
|
||||
public partial class LibationFilesDialog : Window
|
||||
{
|
||||
private class DirSelectOptions
|
||||
{
|
||||
@ -19,27 +19,32 @@ namespace LibationAvalonia.Dialogs
|
||||
Configuration.KnownDirectories.MyDocs
|
||||
};
|
||||
|
||||
public string Directory { get; set; } = Configuration.Instance.LibationFiles;
|
||||
public string Directory { get; set; } = Configuration.GetKnownDirectoryPath(Configuration.KnownDirectories.UserProfile);
|
||||
}
|
||||
private DirSelectOptions dirSelectOptions;
|
||||
public string SelectedDirectory => dirSelectOptions.Directory;
|
||||
public LibationFilesDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
#if DEBUG
|
||||
this.AttachDevTools();
|
||||
#endif
|
||||
DataContext = dirSelectOptions = new();
|
||||
}
|
||||
|
||||
protected override async Task SaveAndCloseAsync()
|
||||
public void SaveButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
|
||||
var libationDir = dirSelectOptions.Directory;
|
||||
|
||||
if (!System.IO.Directory.Exists(libationDir))
|
||||
{
|
||||
MessageBox.Show("Not saving change to Libation Files location. This folder does not exist:\r\n" + libationDir, "Folder does not exist", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
MessageBox.Show("Not saving change to Libation Files location. This folder does not exist:\r\n" + libationDir, "Folder does not exist", MessageBoxButtons.OK, MessageBoxIcon.Error, saveAndRestorePosition: false);
|
||||
return;
|
||||
}
|
||||
|
||||
await base.SaveAndCloseAsync();
|
||||
Close(DialogResult.OK);
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
|
||||
@ -12,6 +12,10 @@ namespace LibationAvalonia.Dialogs
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
public MessageBoxWindow(bool saveAndRestorePosition):base(saveAndRestorePosition)
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
|
||||
50
Source/LibationAvalonia/Dialogs/SetupDialog.axaml
Normal file
50
Source/LibationAvalonia/Dialogs/SetupDialog.axaml
Normal file
@ -0,0 +1,50 @@
|
||||
<Window xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="330"
|
||||
MinWidth="500" MinHeight="330"
|
||||
MaxWidth="500" MaxHeight="330"
|
||||
x:Class="LibationAvalonia.Dialogs.SetupDialog"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
Icon="/Assets/libation.ico"
|
||||
Title="Welcome to Libation">
|
||||
|
||||
<Grid Margin="10" RowDefinitions="*,Auto,Auto">
|
||||
|
||||
<TextBlock Grid.Row="0" Text="This appears to be your first time using Libation or a previous setup was incomplete.
|
||||


|
||||

Please fill in a few settings. You can also change these settings later.
|
||||


|
||||

After you make your selections, get started by importing your library.
|
||||

Go to Import > Scan Library
|
||||


|
||||

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

liberate your books one at a time by clicking the stoplight." />
|
||||
|
||||
<Button
|
||||
Grid.Row="1"
|
||||
Margin="0,10,0,10"
|
||||
Padding="0,10,0,10"
|
||||
HorizontalAlignment="Stretch"
|
||||
Click="NewUser_Click">
|
||||
|
||||
<TextBlock
|
||||
TextAlignment="Center"
|
||||
Text="NEW USER

Choose Settings"/>
|
||||
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
Grid.Row="2"
|
||||
Padding="0,10,0,10"
|
||||
HorizontalAlignment="Stretch"
|
||||
Click="ReturningUser_Click">
|
||||
|
||||
<TextBlock
|
||||
TextAlignment="Center"
|
||||
Text="RETURNING USER

I have previously installed Libation. This is an upgrade or re-install."/>
|
||||
|
||||
</Button>
|
||||
</Grid>
|
||||
</Window>
|
||||
39
Source/LibationAvalonia/Dialogs/SetupDialog.axaml.cs
Normal file
39
Source/LibationAvalonia/Dialogs/SetupDialog.axaml.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using LibationFileManager;
|
||||
|
||||
namespace LibationAvalonia.Dialogs
|
||||
{
|
||||
public partial class SetupDialog : Window
|
||||
{
|
||||
public bool IsNewUser { get;private set; }
|
||||
public bool IsReturningUser { get;private set; }
|
||||
public Configuration Config { get; init; }
|
||||
public SetupDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
#if DEBUG
|
||||
this.AttachDevTools();
|
||||
#endif
|
||||
}
|
||||
|
||||
public void NewUser_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
IsNewUser = true;
|
||||
Close(DialogResult.OK);
|
||||
}
|
||||
|
||||
public void ReturningUser_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
IsReturningUser = true;
|
||||
Close(DialogResult.OK);
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -12,7 +12,7 @@ namespace LibationAvalonia
|
||||
static readonly WindowIcon WindowIcon;
|
||||
static FormSaveExtension()
|
||||
{
|
||||
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop && desktop.MainWindow is not null)
|
||||
WindowIcon = desktop.MainWindow.Icon;
|
||||
else
|
||||
WindowIcon = null;
|
||||
|
||||
@ -80,6 +80,9 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="AppBasic.axaml.cs">
|
||||
<DependentUpon>AppBasic.axaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Views\ProcessBookControl.axaml.cs">
|
||||
<DependentUpon>ProcessBookControl.axaml</DependentUpon>
|
||||
</Compile>
|
||||
|
||||
@ -62,8 +62,8 @@ namespace LibationAvalonia
|
||||
|
||||
public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton)
|
||||
=> ShowCoreAsync(null, text, caption, buttons, icon, defaultButton);
|
||||
public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon)
|
||||
=> ShowCoreAsync(null, text, caption, buttons, icon, MessageBoxDefaultButton.Button1);
|
||||
public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, bool saveAndRestorePosition = true)
|
||||
=> ShowCoreAsync(null, text, caption, buttons, icon, MessageBoxDefaultButton.Button1, saveAndRestorePosition);
|
||||
public static DialogResult Show(string text, string caption, MessageBoxButtons buttons)
|
||||
=> ShowCoreAsync(null, text, caption, buttons, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
|
||||
public static DialogResult Show(string text, string caption)
|
||||
@ -148,9 +148,9 @@ Libation.
|
||||
}
|
||||
|
||||
|
||||
private static DialogResult ShowCoreAsync(Window owner, string message, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton)
|
||||
private static DialogResult ShowCoreAsync(Window owner, string message, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton, bool saveAndRestorePosition = true)
|
||||
{
|
||||
var dialog = new MessageBoxWindow();
|
||||
var dialog = new MessageBoxWindow(saveAndRestorePosition);
|
||||
|
||||
dialog.HideMinMaxBtns();
|
||||
|
||||
|
||||
@ -18,24 +18,33 @@ namespace LibationAvalonia
|
||||
|
||||
static async Task Main()
|
||||
{
|
||||
//***********************************************//
|
||||
// //
|
||||
// do not use Configuration before this line //
|
||||
// //
|
||||
//***********************************************//
|
||||
// Migrations which must occur before configuration is loaded for the first time. Usually ones which alter the Configuration
|
||||
var config = AppScaffolding.LibationScaffolding.RunPreConfigMigrations();
|
||||
|
||||
App.SetupRequired = !config.LibationSettingsAreValid;
|
||||
|
||||
//Start as much work in parallel as possible.
|
||||
var runDbMigrationsTask = Task.Run(() => RunDbMigrations());
|
||||
var classicLifetimeTask = Task.Run(() => new ClassicDesktopStyleApplicationLifetime());
|
||||
var appBuilderTask = Task.Run(BuildAvaloniaApp);
|
||||
|
||||
if (!await runDbMigrationsTask)
|
||||
|
||||
if (!App.SetupRequired)
|
||||
{
|
||||
if (!RunDbMigrations(config))
|
||||
return;
|
||||
|
||||
var dbLibraryTask = Task.Run(() => DbContexts.GetLibrary_Flat_NoTracking(includeParents: true));
|
||||
App.LibraryTask = Task.Run(() => DbContexts.GetLibrary_Flat_NoTracking(includeParents: true));
|
||||
}
|
||||
|
||||
|
||||
|
||||
(await appBuilderTask).SetupWithLifetime(await classicLifetimeTask);
|
||||
|
||||
var form1 = (Views.MainWindow)classicLifetimeTask.Result.MainWindow;
|
||||
|
||||
form1.OnLibraryLoaded(await dbLibraryTask);
|
||||
|
||||
var assets = AvaloniaLocator.Current.GetService<IAssetLoader>();
|
||||
|
||||
classicLifetimeTask.Result.Start(null);
|
||||
}
|
||||
|
||||
@ -44,20 +53,15 @@ namespace LibationAvalonia
|
||||
.UsePlatformDetect()
|
||||
.LogToTrace()
|
||||
.UseReactiveUI();
|
||||
public static AppBuilder BuildAvaloniaAppBasic()
|
||||
=> AppBuilder.Configure<AppBasic>()
|
||||
.UsePlatformDetect()
|
||||
.LogToTrace();
|
||||
|
||||
private static bool RunDbMigrations()
|
||||
public static bool RunDbMigrations(Configuration config)
|
||||
{
|
||||
try
|
||||
{
|
||||
//***********************************************//
|
||||
// //
|
||||
// do not use Configuration before this line //
|
||||
// //
|
||||
//***********************************************//
|
||||
// Migrations which must occur before configuration is loaded for the first time. Usually ones which alter the Configuration
|
||||
var config = AppScaffolding.LibationScaffolding.RunPreConfigMigrations();
|
||||
AudibleUtilities.AudibleApiStorage.EnsureAccountsSettingsFileExists();
|
||||
|
||||
// most migrations go in here
|
||||
AppScaffolding.LibationScaffolding.RunPostConfigMigrations(config);
|
||||
AppScaffolding.LibationScaffolding.RunPostMigrationScaffolding(config);
|
||||
|
||||
@ -6,7 +6,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
<PropertyGroup>
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Any CPU</Platform>
|
||||
<PublishDir>..\bin\Release\publish\linux-x64\</PublishDir>
|
||||
<PublishDir>..\bin\publish\linux-x64\</PublishDir>
|
||||
<PublishProtocol>FileSystem</PublishProtocol>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
|
||||
|
||||
@ -345,7 +345,7 @@ namespace LibationFileManager
|
||||
#endregion
|
||||
|
||||
#region known directories
|
||||
public static string AppDir_Relative => $@".\{LIBATION_FILES_KEY}";
|
||||
public static string AppDir_Relative => $@".{Path.PathSeparator}{LIBATION_FILES_KEY}";
|
||||
public static string AppDir_Absolute => Path.GetFullPath(Path.Combine(Path.GetDirectoryName(Exe.FileLocationOnDisk), LIBATION_FILES_KEY));
|
||||
public static string MyDocs => Path.GetFullPath(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Libation"));
|
||||
public static string WinTemp => Path.GetFullPath(Path.Combine(Path.GetTempPath(), "Libation"));
|
||||
@ -522,8 +522,6 @@ namespace LibationFileManager
|
||||
|
||||
public void SetLibationFiles(string directory)
|
||||
{
|
||||
libationFilesPathCache = null;
|
||||
|
||||
// ensure exists
|
||||
if (!File.Exists(APPSETTINGS_JSON))
|
||||
{
|
||||
@ -532,6 +530,8 @@ namespace LibationFileManager
|
||||
System.Threading.Thread.Sleep(100);
|
||||
}
|
||||
|
||||
libationFilesPathCache = null;
|
||||
|
||||
var startingContents = File.ReadAllText(APPSETTINGS_JSON);
|
||||
var jObj = JObject.Parse(startingContents);
|
||||
|
||||
|
||||
@ -1,14 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using ApplicationServices;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.ReactiveUI;
|
||||
using Dinah.Core;
|
||||
using LibationFileManager;
|
||||
using LibationWinForms.Dialogs;
|
||||
using Serilog;
|
||||
|
||||
namespace LibationWinForms
|
||||
{
|
||||
@ -20,98 +18,6 @@ namespace LibationWinForms
|
||||
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
var sw = System.Diagnostics.Stopwatch.StartNew();
|
||||
var config = LoadLibationConfig();
|
||||
|
||||
if (config is null) return;
|
||||
|
||||
|
||||
var bmp = System.Drawing.SystemIcons.Error.ToBitmap();
|
||||
|
||||
/*
|
||||
Results below compare startup times when parallelizing startup tasks vs when
|
||||
running everything sequentially, from the entry point until after the call to
|
||||
OnLoadedLibrary() returns. Tests were run on a ReadyToRun enabled release build.
|
||||
|
||||
The first run is substantially slower than all subsequent runs for both serial
|
||||
and parallel. This is most likely due to file system caching speeding up
|
||||
subsequent runs, and it's significant because in the wild, most runs are "cold"
|
||||
and will not benefit from caching.
|
||||
|
||||
All times are in milliseconds.
|
||||
|
||||
Run Parallel Serial
|
||||
1 2837 5835
|
||||
2 1566 2774
|
||||
3 1562 2316
|
||||
4 1642 2388
|
||||
5 1596 2391
|
||||
6 1591 2358
|
||||
7 1492 2363
|
||||
8 1542 2335
|
||||
9 1600 2418
|
||||
10 1564 2359
|
||||
11 1567 2379
|
||||
|
||||
Min 1492 2316
|
||||
Q1 1562 2358
|
||||
Med 1567 2379
|
||||
Q3 1600 2418
|
||||
Max 2837 5835
|
||||
*/
|
||||
|
||||
void Form1_Load(object sender, EventArgs e)
|
||||
{
|
||||
sw.Stop();
|
||||
//MessageBox.Show(sw.ElapsedMilliseconds.ToString());
|
||||
}
|
||||
|
||||
//For debug purposes, always run AvaloniaUI.
|
||||
if (config.GetNonString<bool>("BetaOptIn"))
|
||||
{
|
||||
//Start as much work in parallel as possible.
|
||||
var runDbMigrationsTask = Task.Run(() => RunDbMigrations(config));
|
||||
var classicLifetimeTask = Task.Run(() => new ClassicDesktopStyleApplicationLifetime());
|
||||
var appBuilderTask = Task.Run(BuildAvaloniaApp);
|
||||
|
||||
if (!runDbMigrationsTask.GetAwaiter().GetResult())
|
||||
return;
|
||||
|
||||
var runOtherMigrationsTask = Task.Run(() => RunOtherMigrations(config));
|
||||
var dbLibraryTask = Task.Run(() => DbContexts.GetLibrary_Flat_NoTracking(includeParents: true));
|
||||
|
||||
appBuilderTask.GetAwaiter().GetResult().SetupWithLifetime(classicLifetimeTask.GetAwaiter().GetResult());
|
||||
|
||||
if (!runOtherMigrationsTask.GetAwaiter().GetResult())
|
||||
return;
|
||||
|
||||
var form1 = (AvaloniaUI.Views.MainWindow)classicLifetimeTask.Result.MainWindow;
|
||||
form1.Opened += Form1_Load;
|
||||
|
||||
form1.OnLibraryLoaded(dbLibraryTask.GetAwaiter().GetResult());
|
||||
|
||||
classicLifetimeTask.Result.Start(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!RunDbMigrations(config) || !RunOtherMigrations(config))
|
||||
return;
|
||||
|
||||
var form1 = new Form1();
|
||||
form1.Shown += Form1_Load;
|
||||
|
||||
System.Windows.Forms.Application.Run(form1);
|
||||
}
|
||||
}
|
||||
|
||||
public static AppBuilder BuildAvaloniaApp()
|
||||
=> AppBuilder.Configure<AvaloniaUI.App>()
|
||||
.UsePlatformDetect()
|
||||
.LogToTrace()
|
||||
.UseReactiveUI();
|
||||
|
||||
private static Configuration LoadLibationConfig()
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -120,7 +26,7 @@ namespace LibationWinForms
|
||||
//AllocConsole();
|
||||
|
||||
// run as early as possible. see notes in postLoggingGlobalExceptionHandling
|
||||
System.Windows.Forms.Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
|
||||
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
|
||||
|
||||
ApplicationConfiguration.Initialize();
|
||||
|
||||
@ -131,40 +37,13 @@ namespace LibationWinForms
|
||||
//***********************************************//
|
||||
// Migrations which must occur before configuration is loaded for the first time. Usually ones which alter the Configuration
|
||||
var config = AppScaffolding.LibationScaffolding.RunPreConfigMigrations();
|
||||
AudibleUtilities.AudibleApiStorage.EnsureAccountsSettingsFileExists();
|
||||
return config;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
DisplayStartupErrorMessage(ex);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool RunDbMigrations(Configuration config)
|
||||
{
|
||||
try
|
||||
{
|
||||
// do this as soon as possible (post-config)
|
||||
RunInstaller(config);
|
||||
|
||||
// most migrations go in here
|
||||
AppScaffolding.LibationScaffolding.RunPostConfigMigrations(config);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
DisplayStartupErrorMessage(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool RunOtherMigrations(Configuration config)
|
||||
{
|
||||
try
|
||||
{
|
||||
// migrations which require Forms or are long-running
|
||||
RunWindowsOnlyMigrations(config);
|
||||
|
||||
@ -175,27 +54,11 @@ namespace LibationWinForms
|
||||
#endif
|
||||
// logging is init'd here
|
||||
AppScaffolding.LibationScaffolding.RunPostMigrationScaffolding(config);
|
||||
|
||||
// global exception handling (ShowAdminAlert) attempts to use logging. only call it after logging has been init'd
|
||||
#if WINDOWS7_0_OR_GREATER
|
||||
postLoggingGlobalExceptionHandling();
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
DisplayStartupErrorMessage(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void DisplayStartupErrorMessage(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.";
|
||||
#if WINDOWS7_0_OR_GREATER
|
||||
try
|
||||
{
|
||||
MessageBoxLib.ShowAdminAlert(null, body, title, ex);
|
||||
@ -204,7 +67,13 @@ namespace LibationWinForms
|
||||
{
|
||||
MessageBox.Show($"{body}\r\n\r\n{ex.Message}\r\n\r\n{ex.StackTrace}", title, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
// global exception handling (ShowAdminAlert) attempts to use logging. only call it after logging has been init'd
|
||||
postLoggingGlobalExceptionHandling();
|
||||
|
||||
Application.Run(new Form1());
|
||||
}
|
||||
|
||||
private static void RunInstaller(Configuration config)
|
||||
@ -229,7 +98,7 @@ namespace LibationWinForms
|
||||
static void CancelInstallation()
|
||||
{
|
||||
MessageBox.Show("Initial set up cancelled.", "Cancelled", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
System.Windows.Forms.Application.Exit();
|
||||
Application.Exit();
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
@ -307,17 +176,13 @@ namespace LibationWinForms
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
#if WINDOWS7_0_OR_GREATER
|
||||
MessageBoxLib.ShowAdminAlert(null, "Error checking for update", "Error checking for update", ex);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if (upgradeProperties.ZipUrl is null)
|
||||
{
|
||||
#if WINDOWS7_0_OR_GREATER
|
||||
MessageBox.Show(upgradeProperties.HtmlUrl, "New version available");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
@ -330,7 +195,7 @@ namespace LibationWinForms
|
||||
AppDomain.CurrentDomain.UnhandledException += (_, e) => MessageBoxLib.ShowAdminAlert(null, "Libation has crashed due to an unhandled error.", "Application crash!", (Exception)e.ExceptionObject);
|
||||
|
||||
// these 2 lines makes it graceful. sync (eg in main form's ctor) and thread exceptions will still crash us, but event (sync, void async, Task async) will not
|
||||
System.Windows.Forms.Application.ThreadException += (_, e) => MessageBoxLib.ShowAdminAlert(null, "Libation has encountered an unexpected error.", "Unexpected error", e.Exception);
|
||||
Application.ThreadException += (_, e) => MessageBoxLib.ShowAdminAlert(null, "Libation has encountered an unexpected error.", "Unexpected error", e.Exception);
|
||||
// move to beginning of execution. crashes app if this is called post-RunInstaller: System.InvalidOperationException: 'Thread exception mode cannot be changed once any Controls are created on the thread.'
|
||||
//// I never found a case where including made a difference. I think this enum is default and including it will override app user config file
|
||||
//Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user