diff --git a/WoWsShipBuilder.Desktop/App.axaml.cs b/WoWsShipBuilder.Desktop/App.axaml.cs index ee3314c10..d1bde2160 100644 --- a/WoWsShipBuilder.Desktop/App.axaml.cs +++ b/WoWsShipBuilder.Desktop/App.axaml.cs @@ -25,164 +25,163 @@ using WoWsShipBuilder.Infrastructure.Localization.Resources; using WoWsShipBuilder.Infrastructure.Utility; -namespace WoWsShipBuilder.Desktop +namespace WoWsShipBuilder.Desktop; + +[SuppressMessage("System.IO.Abstractions", "IO0003", Justification = "This class is never tested.")] +[SuppressMessage("System.IO.Abstractions", "IO0006", Justification = "This class is never tested.")] +public class App : Application { - [SuppressMessage("System.IO.Abstractions", "IO0003", Justification = "This class is never tested.")] - [SuppressMessage("System.IO.Abstractions", "IO0006", Justification = "This class is never tested.")] - public class App : Application - { - private readonly ILogger logger = NullLogger.Instance; - private readonly IServiceProvider services = default!; + private readonly ILogger logger = NullLogger.Instance; + private readonly IServiceProvider services = default!; - public App() - { - ModeDetector.OverrideModeDetector(new CustomModeDetector()); - } + public App() + { + ModeDetector.OverrideModeDetector(new CustomModeDetector()); + } - public IServiceProvider Services + public IServiceProvider Services + { + get => services; + init { - get => services; - init - { - services = value; - logger = services.GetRequiredService>(); - } + services = value; + logger = services.GetRequiredService>(); } + } - public static async Task ShowUpdateRestartDialog(Window? parent, ILocalizer localizer) - { - return await Dispatcher.UIThread.InvokeAsync(async () => await MessageBox.Show( - parent, - localizer.GetAppLocalization(nameof(Translation.UpdateMessageBox_Description)).Localization, - localizer.GetAppLocalization(nameof(Translation.UpdateMessageBox_Title)).Localization, - MessageBox.MessageBoxButtons.YesNo, - MessageBox.MessageBoxIcon.Question)); - } + public static async Task ShowUpdateRestartDialog(Window? parent, ILocalizer localizer) + { + return await Dispatcher.UIThread.InvokeAsync(async () => await MessageBox.Show( + parent, + localizer.GetAppLocalization(nameof(Translation.UpdateMessageBox_Description)).Localization, + localizer.GetAppLocalization(nameof(Translation.UpdateMessageBox_Title)).Localization, + MessageBox.MessageBoxButtons.YesNo, + MessageBox.MessageBoxIcon.Question)); + } - public override void Initialize() - { - AvaloniaXamlLoader.Load(this); - } + public override void Initialize() + { + AvaloniaXamlLoader.Load(this); + } - public override void OnFrameworkInitializationCompleted() + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { - if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - Logging.Initialize(Services.GetRequiredService()); - InitializeSettings(); - var settings = Services.GetRequiredService(); + Logging.Initialize(Services.GetRequiredService()); + InitializeSettings(); + var settings = Services.GetRequiredService(); - LogManager.ReconfigExistingLoggers(); + LogManager.ReconfigExistingLoggers(); - desktop.Exit += OnExit; - desktop.MainWindow = new SplashScreen(Services); - logger.LogInformation("AutoUpdate Enabled: {SettingsAutoUpdateEnabled}", settings.AutoUpdateEnabled); + desktop.Exit += OnExit; + desktop.MainWindow = new SplashScreen(Services); + logger.LogInformation("AutoUpdate Enabled: {SettingsAutoUpdateEnabled}", settings.AutoUpdateEnabled); - if (settings.AutoUpdateEnabled) + if (settings.AutoUpdateEnabled) + { + Task.Run(async () => { - Task.Run(async () => + if (OperatingSystem.IsWindows()) { - if (OperatingSystem.IsWindows()) - { - await UpdateCheck(Services.GetRequiredService()); - logger.LogInformation("Finished updatecheck"); - } - else - { - logger.LogInformation("Skipped updatecheck"); - } - }); - } + await UpdateCheck(Services.GetRequiredService()); + logger.LogInformation("Finished updatecheck"); + } + else + { + logger.LogInformation("Skipped updatecheck"); + } + }); } - - base.OnFrameworkInitializationCompleted(); } - private void InitializeSettings() - { - var settingsAccessor = (DesktopSettingsAccessor)services.GetRequiredService(); - var settings = settingsAccessor.LoadSettingsSync(); - settings ??= new(); - - logger.LogDebug("Updating app settings with settings read from file..."); - var appSettings = services.GetRequiredService(); - appSettings.UpdateFromSettings(settings); - AppData.IsInitialized = true; - Thread.CurrentThread.CurrentCulture = appSettings.SelectedLanguage.CultureInfo; - Thread.CurrentThread.CurrentUICulture = appSettings.SelectedLanguage.CultureInfo; - logger.LogDebug("Settings initialization complete"); - } + base.OnFrameworkInitializationCompleted(); + } + + private void InitializeSettings() + { + var settingsAccessor = (DesktopSettingsAccessor)services.GetRequiredService(); + var settings = settingsAccessor.LoadSettingsSync(); + settings ??= new(); + + logger.LogDebug("Updating app settings with settings read from file..."); + var appSettings = services.GetRequiredService(); + appSettings.UpdateFromSettings(settings); + AppData.IsInitialized = true; + Thread.CurrentThread.CurrentCulture = appSettings.SelectedLanguage.CultureInfo; + Thread.CurrentThread.CurrentUICulture = appSettings.SelectedLanguage.CultureInfo; + logger.LogDebug("Settings initialization complete"); + } + + private void OnExit(object? sender, ControlledApplicationLifetimeExitEventArgs e) + { + logger.LogInformation("Closing app, saving setting and builds"); + var settingsAccessor = (DesktopSettingsAccessor)Services.GetRequiredService(); + settingsAccessor.SaveSettingsSync(Services.GetRequiredService()); + logger.LogInformation("Exiting..."); + logger.LogInformation("------------------------------"); + } + + [SupportedOSPlatform("windows")] + private async Task UpdateCheck(AppNotificationService notificationService) + { + logger.LogInformation("Current version: {Version}", Assembly.GetExecutingAssembly().GetName().Version); - private void OnExit(object? sender, ControlledApplicationLifetimeExitEventArgs e) + using UpdateManager updateManager = new GithubUpdateManager("https://github.com/WoWs-Builder-Team/WoWs-ShipBuilder"); + if (!updateManager.IsInstalledApp) { - logger.LogInformation("Closing app, saving setting and builds"); - var settingsAccessor = (DesktopSettingsAccessor)Services.GetRequiredService(); - settingsAccessor.SaveSettingsSync(Services.GetRequiredService()); - logger.LogInformation("Exiting..."); - logger.LogInformation("------------------------------"); + logger.LogInformation("No update.exe found, aborting update check"); + return; } - [SupportedOSPlatform("windows")] - private async Task UpdateCheck(AppNotificationService notificationService) + logger.LogInformation("Update manager initialized"); + try { - logger.LogInformation("Current version: {Version}", Assembly.GetExecutingAssembly().GetName().Version); - - using UpdateManager updateManager = new GithubUpdateManager("https://github.com/WoWs-Builder-Team/WoWs-ShipBuilder"); - if (!updateManager.IsInstalledApp) + // Can throw a null-reference-exception, no idea why. + var updateInfo = await updateManager.CheckForUpdate(); + if (!updateInfo.ReleasesToApply.Any()) { - logger.LogInformation("No update.exe found, aborting update check"); + logger.LogInformation("No app update found"); return; } - logger.LogInformation("Update manager initialized"); - try + await notificationService.NotifyAppUpdateStart(); + var release = await updateManager.UpdateApp(); + if (release == null) { - // Can throw a null-reference-exception, no idea why. - var updateInfo = await updateManager.CheckForUpdate(); - if (!updateInfo.ReleasesToApply.Any()) - { - logger.LogInformation("No app update found"); - return; - } + logger.LogInformation("No app update found"); + return; + } - await notificationService.NotifyAppUpdateStart(); - var release = await updateManager.UpdateApp(); - if (release == null) + logger.LogInformation("App updated to version {ReleaseVersion}", release.Version); + await notificationService.NotifyAppUpdateComplete(); + var result = await ShowUpdateRestartDialog((ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.MainWindow, Services.GetRequiredService()); + if (result.Equals(MessageBox.MessageBoxResult.Yes)) + { + logger.LogInformation("User decided to restart after update"); + if (OperatingSystem.IsWindows()) { - logger.LogInformation("No app update found"); - return; + UpdateManager.RestartApp(); } - - logger.LogInformation("App updated to version {ReleaseVersion}", release.Version); - await notificationService.NotifyAppUpdateComplete(); - var result = await ShowUpdateRestartDialog((ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.MainWindow, Services.GetRequiredService()); - if (result.Equals(MessageBox.MessageBoxResult.Yes)) - { - logger.LogInformation("User decided to restart after update"); - if (OperatingSystem.IsWindows()) - { - UpdateManager.RestartApp(); - } - } - } - catch (NullReferenceException) - { - logger.LogDebug("NullReferenceException during app update"); } - catch (Exception e) - { + } + catch (NullReferenceException) + { + logger.LogDebug("NullReferenceException during app update"); + } + catch (Exception e) + { #if DEBUG - logger.LogWarning(e, "Exception during app update"); + logger.LogWarning(e, "Exception during app update"); #else logger.LogError(e, "Exception during app update"); #endif - await notificationService.NotifyAppUpdateError(nameof(Translation.NotificationService_ErrorMessage)); - } + await notificationService.NotifyAppUpdateError(nameof(Translation.NotificationService_ErrorMessage)); } + } - private sealed class CustomModeDetector : IModeDetector - { - public bool? InUnitTestRunner() => false; - } + private sealed class CustomModeDetector : IModeDetector + { + public bool? InUnitTestRunner() => false; } } diff --git a/WoWsShipBuilder.Desktop/Common/AppHeader.axaml b/WoWsShipBuilder.Desktop/Common/AppHeader.axaml index d4e5d37e1..939408fe6 100644 --- a/WoWsShipBuilder.Desktop/Common/AppHeader.axaml +++ b/WoWsShipBuilder.Desktop/Common/AppHeader.axaml @@ -28,7 +28,7 @@ @@ -52,7 +52,7 @@ @@ -70,11 +70,8 @@ - - ShowTitleProperty = + AvaloniaProperty.Register(nameof(ShowTitle), true); + + public static readonly StyledProperty TitleProperty = + AvaloniaProperty.Register(nameof(Title), "WoWs Ship Builder"); + + public static readonly StyledProperty ShowMinimizeButtonProperty = + AvaloniaProperty.Register(nameof(ShowMinimizeButton), true); + + public static readonly StyledProperty ShowMaximizeButtonProperty = + AvaloniaProperty.Register(nameof(ShowMaximizeButton), false); + + public static readonly StyledProperty ShowCloseButtonProperty = + AvaloniaProperty.Register(nameof(ShowCloseButton), true); + + public AppHeader() { - public static readonly StyledProperty ShowTitleProperty = - AvaloniaProperty.Register(nameof(ShowTitle), true); + InitializeComponent(); + } - public static readonly StyledProperty TitleProperty = - AvaloniaProperty.Register(nameof(Title), "WoWs Ship Builder"); + public bool ShowTitle + { + get => GetValue(ShowTitleProperty); + set => SetValue(ShowTitleProperty, value); + } - public static readonly StyledProperty ShowMinimizeButtonProperty = - AvaloniaProperty.Register(nameof(ShowMinimizeButton), true); + public string Title + { + get => GetValue(TitleProperty); + set => SetValue(TitleProperty, value); + } - public static readonly StyledProperty ShowMaximizeButtonProperty = - AvaloniaProperty.Register(nameof(ShowMaximizeButton), false); + public bool ShowMinimizeButton + { + get => GetValue(ShowMinimizeButtonProperty); + set => SetValue(ShowMinimizeButtonProperty, value); + } - public static readonly StyledProperty ShowCloseButtonProperty = - AvaloniaProperty.Register(nameof(ShowCloseButton), true); + public bool ShowMaximizeButton + { + get => GetValue(ShowMaximizeButtonProperty); + set => SetValue(ShowMaximizeButtonProperty, value); + } - public AppHeader() - { - InitializeComponent(); + public bool ShowCloseButton + { + get => GetValue(ShowCloseButtonProperty); + set => SetValue(ShowCloseButtonProperty, value); + } - MinimizeButton.Click += MinimizeWindow; - MaximizeButton.Click += MaximizeWindow; - CloseButton.Click += CloseWindow; + protected override void OnLoaded(RoutedEventArgs e) + { + base.OnLoaded(e); + MinimizeButton.Click += MinimizeWindow; + MaximizeButton.Click += MaximizeWindow; + CloseButton.Click += CloseWindow; - SubscribeToWindowState(); - } + SubscribeToWindowState(); + } - public bool ShowTitle + private void CloseWindow(object? sender, RoutedEventArgs e) + { + if (Design.IsDesignMode) { - get => GetValue(ShowTitleProperty); - set => SetValue(ShowTitleProperty, value); + return; } - public string Title - { - get => GetValue(TitleProperty); - set => SetValue(TitleProperty, value); - } + Window hostWindow = (Window)VisualRoot!; + hostWindow.Close(); + } - public bool ShowMinimizeButton + private void MaximizeWindow(object? sender, RoutedEventArgs e) + { + if (Design.IsDesignMode) { - get => GetValue(ShowMinimizeButtonProperty); - set => SetValue(ShowMinimizeButtonProperty, value); + return; } - public bool ShowMaximizeButton - { - get => GetValue(ShowMaximizeButtonProperty); - set => SetValue(ShowMaximizeButtonProperty, value); - } + Window hostWindow = (Window)VisualRoot!; - public bool ShowCloseButton + if (hostWindow.WindowState == WindowState.Normal) { - get => GetValue(ShowCloseButtonProperty); - set => SetValue(ShowCloseButtonProperty, value); + hostWindow.WindowState = WindowState.Maximized; } - - private void InitializeComponent() + else { - AvaloniaXamlLoader.Load(this); + hostWindow.WindowState = WindowState.Normal; } + } - private void CloseWindow(object? sender, Avalonia.Interactivity.RoutedEventArgs e) + private void MinimizeWindow(object? sender, RoutedEventArgs e) + { + if (Design.IsDesignMode) { - if (Design.IsDesignMode) - { - return; - } - - Window hostWindow = (Window)VisualRoot!; - hostWindow.Close(); + return; } - private void MaximizeWindow(object? sender, Avalonia.Interactivity.RoutedEventArgs e) - { - if (Design.IsDesignMode) - { - return; - } - - Window hostWindow = (Window)VisualRoot!; + Window hostWindow = (Window)VisualRoot!; + hostWindow.WindowState = WindowState.Minimized; + } - if (hostWindow.WindowState == WindowState.Normal) - { - hostWindow.WindowState = WindowState.Maximized; - } - else - { - hostWindow.WindowState = WindowState.Normal; - } - } + private async void SubscribeToWindowState() + { + var hostWindow = (Window?)VisualRoot; - private void MinimizeWindow(object? sender, Avalonia.Interactivity.RoutedEventArgs e) + while (hostWindow == null) { - if (Design.IsDesignMode) - { - return; - } - - Window hostWindow = (Window)VisualRoot!; - hostWindow.WindowState = WindowState.Minimized; + hostWindow = (Window)VisualRoot!; + await Task.Delay(50); } - private async void SubscribeToWindowState() + hostWindow.GetObservable(Window.WindowStateProperty).Subscribe(s => { - Window hostWindow = (Window)VisualRoot!; - - while (hostWindow == null) + if (s != WindowState.Maximized) { - hostWindow = (Window)VisualRoot!; - await Task.Delay(50); + MaximizeIcon.Data = Avalonia.Media.Geometry.Parse("M2048 2048v-2048h-2048v2048h2048zM1843 1843h-1638v-1638h1638v1638z"); + hostWindow.Padding = new Thickness(0, 0, 0, 0); } - hostWindow.GetObservable(Window.WindowStateProperty).Subscribe(s => + if (s == WindowState.Maximized) { - if (s != WindowState.Maximized) - { - MaximizeIcon.Data = Avalonia.Media.Geometry.Parse("M2048 2048v-2048h-2048v2048h2048zM1843 1843h-1638v-1638h1638v1638z"); - hostWindow.Padding = new Thickness(0, 0, 0, 0); - } - - if (s == WindowState.Maximized) - { - MaximizeIcon.Data = Avalonia.Media.Geometry.Parse("M2048 1638h-410v410h-1638v-1638h410v-410h1638v1638zm-614-1024h-1229v1229h1229v-1229zm409-409h-1229v205h1024v1024h205v-1229z"); - - // This should be a more universal approach in both cases, but I found it to be less reliable, when for example double-clicking the title bar. - hostWindow.Padding = new Thickness( - hostWindow.OffScreenMargin.Left, - hostWindow.OffScreenMargin.Top, - hostWindow.OffScreenMargin.Right, - hostWindow.OffScreenMargin.Bottom); - } - }); - } + MaximizeIcon.Data = Avalonia.Media.Geometry.Parse("M2048 1638h-410v410h-1638v-1638h410v-410h1638v1638zm-614-1024h-1229v1229h1229v-1229zm409-409h-1229v205h1024v1024h205v-1229z"); + + // This should be a more universal approach in both cases, but I found it to be less reliable, when for example double-clicking the title bar. + hostWindow.Padding = new Thickness( + hostWindow.OffScreenMargin.Left, + hostWindow.OffScreenMargin.Top, + hostWindow.OffScreenMargin.Right, + hostWindow.OffScreenMargin.Bottom); + } + }); } } diff --git a/WoWsShipBuilder.Desktop/Features/MessageBox/MessageBox.axaml b/WoWsShipBuilder.Desktop/Features/MessageBox/MessageBox.axaml index 2f0fb6e9e..8265872bb 100644 --- a/WoWsShipBuilder.Desktop/Features/MessageBox/MessageBox.axaml +++ b/WoWsShipBuilder.Desktop/Features/MessageBox/MessageBox.axaml @@ -2,25 +2,27 @@ 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" - xmlns:infrastructure="clr-namespace:WoWsShipBuilder.Desktop.Infrastructure" xmlns:common="clr-namespace:WoWsShipBuilder.Desktop.Common" mc:Ignorable="d" d:DesignWidth="300" d:DesignHeight="150" x:Class="WoWsShipBuilder.Desktop.Features.MessageBox.MessageBox" WindowStartupLocation="CenterOwner" ExtendClientAreaChromeHints="NoChrome"> - - - - - - - - - - + + + + + + + + + + + + - + diff --git a/WoWsShipBuilder.Desktop/Features/MessageBox/MessageBox.axaml.cs b/WoWsShipBuilder.Desktop/Features/MessageBox/MessageBox.axaml.cs index 43fcdd0b9..00a0a5e9d 100644 --- a/WoWsShipBuilder.Desktop/Features/MessageBox/MessageBox.axaml.cs +++ b/WoWsShipBuilder.Desktop/Features/MessageBox/MessageBox.axaml.cs @@ -1,11 +1,8 @@ using System; using System.Threading.Tasks; -using Avalonia; using Avalonia.Controls; -using Avalonia.Markup.Xaml; using Avalonia.Media.Imaging; using Avalonia.Platform; -using WoWsShipBuilder.Desktop.Infrastructure; using WoWsShipBuilder.Infrastructure.Localization.Resources; namespace WoWsShipBuilder.Desktop.Features.MessageBox; @@ -14,7 +11,7 @@ public partial class MessageBox : Window { public MessageBox() { - AvaloniaXamlLoader.Load(this); + InitializeComponent(); } public enum MessageBoxButtons diff --git a/WoWsShipBuilder.Desktop/Program.cs b/WoWsShipBuilder.Desktop/Program.cs index f6de95ee1..0fb444666 100644 --- a/WoWsShipBuilder.Desktop/Program.cs +++ b/WoWsShipBuilder.Desktop/Program.cs @@ -7,7 +7,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using NLog; +using Microsoft.Extensions.Logging.Abstractions; using NLog.Extensions.Logging; using Sentry; using Squirrel; @@ -15,12 +15,13 @@ using WoWsShipBuilder.Desktop.Infrastructure.StaticConfiguration; using WoWsShipBuilder.Infrastructure.ApplicationData; using WoWsShipBuilder.Infrastructure.Localization; -using LogLevel = Microsoft.Extensions.Logging.LogLevel; namespace WoWsShipBuilder.Desktop; internal sealed class Program { + private static ILogger logger = NullLogger.Instance; + [STAThread] public static void Main(string[] args) => RunProgram(args).GetAwaiter().GetResult(); @@ -39,7 +40,7 @@ private static async Task RunProgram(string[] args) AppData.WebMode = false; await app.StartAsync(); - var logger = app.Services.GetRequiredService>(); + logger = app.Services.GetRequiredService>(); LocalizeConverter.InitializeLocalizer(app.Services.GetRequiredService()); var avaloniaApp = BuildAvaloniaApp(app.Services); @@ -88,29 +89,33 @@ public static AppBuilder BuildAvaloniaApp(IServiceProvider services) .UseSkia() .UseReactiveUI(); - public static AppBuilder BuildAvaloniaApp() => BuildAvaloniaApp(null!); + public static AppBuilder BuildAvaloniaApp() => BuildAvaloniaApp(CreatePreviewServiceProvider()); [SupportedOSPlatform("windows")] private static void OnAppUninstall(SemanticVersion version, IAppTools tools) { - LogManager.GetCurrentClassLogger().Info("App is uninstalling..."); + logger.LogInformation("App is uninstalling..."); tools.RemoveShortcutForThisExe(); } [SupportedOSPlatform("windows")] private static void OnInitialInstall(SemanticVersion version, IAppTools tools) { - var logger = LogManager.GetCurrentClassLogger(); - logger.Info("App has been installed, creating shortcuts..."); + logger.LogInformation("App has been installed, creating shortcuts..."); try { tools.CreateShortcutForThisExe(); - logger.Info("Shortcuts have been created."); + logger.LogInformation("Shortcuts have been created."); } catch (Exception e) { - logger.Error(e); + logger.LogError(e, "Error during installation"); throw; } } + + private static IServiceProvider CreatePreviewServiceProvider() + { + return new ServiceCollection().AddLogging(builder => builder.ClearProviders()).BuildServiceProvider(); + } }