diff --git a/LightBulb/App.axaml.cs b/LightBulb/App.axaml.cs index 633df2c..a9dbce0 100644 --- a/LightBulb/App.axaml.cs +++ b/LightBulb/App.axaml.cs @@ -103,21 +103,11 @@ public override void OnFrameworkInitializationCompleted() ); } - private void ShowMainWindow() - { - if (ApplicationLifetime?.TryGetMainWindow() is { } window) - { - window.Show(); - window.Activate(); - window.Focus(); - } - } - - private void TrayIcon_OnClicked(object? sender, EventArgs args) => ShowMainWindow(); + private void TrayIcon_OnClicked(object? sender, EventArgs args) => this.TryFocusMainWindow(); private void ShowSettingsMenuItem_OnClick(object? sender, EventArgs args) { - ShowMainWindow(); + this.TryFocusMainWindow(); _mainViewModel.ShowSettingsCommand.Execute(null); } diff --git a/LightBulb/Services/UpdateService.cs b/LightBulb/Services/UpdateService.cs index caa4367..d23a0f8 100644 --- a/LightBulb/Services/UpdateService.cs +++ b/LightBulb/Services/UpdateService.cs @@ -1,8 +1,6 @@ using System; using System.Linq; using System.Threading.Tasks; -using Avalonia; -using LightBulb.Utils.Extensions; using Onova; using Onova.Exceptions; using Onova.Services; @@ -16,29 +14,47 @@ public class UpdateService(SettingsService settingsService) : IDisposable new ZipPackageExtractor() ); - private Version? TryGetLastPreparedUpdate() => _updateManager.GetPreparedUpdates().Max(); + public async Task CheckForUpdatesAsync() + { + if (!settingsService.IsAutoUpdateEnabled) + return null; + + var check = await _updateManager.CheckForUpdatesAsync(); + return check.CanUpdate ? check.LastVersion : null; + } + + public Version? TryGetLastPreparedUpdate() + { + var version = _updateManager.GetPreparedUpdates().Max(); + if (version <= _updateManager.Updatee.Version) + return null; - public async Task CheckPrepareUpdateAsync() + return version; + } + + public async Task PrepareUpdateAsync(Version version) { if (!settingsService.IsAutoUpdateEnabled) return; try { - var check = await _updateManager.CheckForUpdatesAsync(); - if (!check.CanUpdate || check.LastVersion is null) + if (version == TryGetLastPreparedUpdate()) return; - - if (check.LastVersion != TryGetLastPreparedUpdate()) - await _updateManager.PrepareUpdateAsync(check.LastVersion); + + await _updateManager.PrepareUpdateAsync(version); } - catch + catch (UpdaterAlreadyLaunchedException) { - // Failure to check for updates shouldn't crash the app + // Ignore race conditions + } + catch (LockFileNotAcquiredException) + { + // Ignore race conditions } } - public void FinalizePendingUpdates() + public void FinalizeUpdate(Version version) { if (!settingsService.IsAutoUpdateEnabled) return; @@ -49,17 +65,7 @@ public void FinalizePendingUpdates() try { - var lastPreparedUpdate = TryGetLastPreparedUpdate(); - if (lastPreparedUpdate is null) - return; - - if (lastPreparedUpdate <= _updateManager.Updatee.Version) - return; - - _updateManager.LaunchUpdater(lastPreparedUpdate); - - if (Application.Current?.ApplicationLifetime?.TryShutdown(2) != true) - Environment.Exit(2); + _updateManager.LaunchUpdater(version); } catch (UpdaterAlreadyLaunchedException) { diff --git a/LightBulb/Utils/Extensions/AvaloniaExtensions.cs b/LightBulb/Utils/Extensions/AvaloniaExtensions.cs index b417839..896e7b8 100644 --- a/LightBulb/Utils/Extensions/AvaloniaExtensions.cs +++ b/LightBulb/Utils/Extensions/AvaloniaExtensions.cs @@ -1,4 +1,5 @@ -using Avalonia.Controls; +using Avalonia; +using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; namespace LightBulb.Utils.Extensions; @@ -25,4 +26,14 @@ public static bool TryShutdown(this IApplicationLifetime lifetime, int exitCode return false; } + + public static void TryFocusMainWindow(this Application application) + { + if (application.ApplicationLifetime?.TryGetMainWindow() is { } window) + { + window.Show(); + window.Activate(); + window.Focus(); + } + } } diff --git a/LightBulb/ViewModels/MainViewModel.cs b/LightBulb/ViewModels/MainViewModel.cs index d16ab96..bee2845 100644 --- a/LightBulb/ViewModels/MainViewModel.cs +++ b/LightBulb/ViewModels/MainViewModel.cs @@ -1,10 +1,12 @@ using System; using System.Threading.Tasks; +using Avalonia; using CommunityToolkit.Mvvm.Input; using LightBulb.Framework; using LightBulb.PlatformInterop; using LightBulb.Services; using LightBulb.Utils; +using LightBulb.Utils.Extensions; using LightBulb.ViewModels.Components; using LightBulb.ViewModels.Components.Settings; @@ -18,7 +20,15 @@ UpdateService updateService ) : ViewModelBase { private readonly Timer _checkForUpdatesTimer = - new(TimeSpan.FromHours(3), async () => await updateService.CheckPrepareUpdateAsync()); + new( + TimeSpan.FromHours(3), + async () => + { + var updateVersion = await updateService.CheckForUpdatesAsync(); + if (updateVersion is not null) + await updateService.PrepareUpdateAsync(updateVersion); + } + ); public DashboardViewModel Dashboard { get; } = viewModelManager.CreateDashboardViewModel(); @@ -39,11 +49,11 @@ Press FIX to unlock the gamma range. Administrator privileges may be required. "CLOSE" ); - if (await dialogManager.ShowDialogAsync(dialog) == true) - { - settingsService.IsExtendedGammaRangeUnlocked = true; - settingsService.Save(); - } + if (await dialogManager.ShowDialogAsync(dialog) != true) + return; + + settingsService.IsExtendedGammaRangeUnlocked = true; + settingsService.Save(); } private async Task ShowFirstTimeExperienceMessageAsync() @@ -68,13 +78,13 @@ Press OK to open settings. settingsService.IsAutoStartEnabled = true; settingsService.Save(); - if (await dialogManager.ShowDialogAsync(dialog) == true) - { - var settingsDialog = viewModelManager.CreateSettingsViewModel(); - settingsDialog.ActivateTab(); + if (await dialogManager.ShowDialogAsync(dialog) != true) + return; - await dialogManager.ShowDialogAsync(settingsDialog); - } + var settingsDialog = viewModelManager.CreateSettingsViewModel(); + settingsDialog.ActivateTab(); + + await dialogManager.ShowDialogAsync(settingsDialog); } private async Task ShowUkraineSupportMessageAsync() @@ -97,22 +107,49 @@ Click LEARN MORE to find ways that you can help. settingsService.IsUkraineSupportMessageEnabled = false; settingsService.Save(); - if (await dialogManager.ShowDialogAsync(dialog) == true) - ProcessEx.StartShellExecute("https://tyrrrz.me/ukraine?source=lightbulb"); + if (await dialogManager.ShowDialogAsync(dialog) != true) + return; + + ProcessEx.StartShellExecute("https://tyrrrz.me/ukraine?source=lightbulb"); + } + + private async Task FinalizePendingUpdateAsync() + { + var updateVersion = updateService.TryGetLastPreparedUpdate(); + if (updateVersion is null) + return; + + var dialog = viewModelManager.CreateMessageBoxViewModel( + "Update available", + $""" + Update to {Program.Name} v{updateVersion} has been downloaded. + Do you want to install it now? + """, + "INSTALL", + "CANCEL" + ); + + Application.Current?.TryFocusMainWindow(); + + if (await dialogManager.ShowDialogAsync(dialog) != true) + return; + + updateService.FinalizeUpdate(updateVersion); + + if (Application.Current?.ApplicationLifetime?.TryShutdown(2) != true) + Environment.Exit(2); } [RelayCommand] private async Task InitializeAsync() { - // Finalize pending updates (and restart if necessary) - updateService.FinalizePendingUpdates(); - // Load settings settingsService.Load(); await ShowGammaRangePromptAsync(); await ShowFirstTimeExperienceMessageAsync(); await ShowUkraineSupportMessageAsync(); + await FinalizePendingUpdateAsync(); _checkForUpdatesTimer.Start(); } diff --git a/LightBulb/Views/Dialogs/MessageBoxView.axaml b/LightBulb/Views/Dialogs/MessageBoxView.axaml index 006a555..21e338f 100644 --- a/LightBulb/Views/Dialogs/MessageBoxView.axaml +++ b/LightBulb/Views/Dialogs/MessageBoxView.axaml @@ -41,7 +41,6 @@ Columns="{Binding ButtonsCount}">