From 67a4672c66341a78fbc4119ccebe2aee250085be Mon Sep 17 00:00:00 2001 From: Robert Date: Sun, 26 Feb 2023 13:41:40 +0100 Subject: [PATCH] Implemented Windows updating --- .../Services/MainWindow/IMainWindowService.cs | 6 + .../Services/MainWindow/MainWindowService.cs | 9 +- .../DryIoc/ContainerExtensions.cs | 2 + .../WindowsUpdateNotificationProvider.cs | 146 ++++++++++++++++++ src/Artemis.UI/DryIoc/ContainerExtensions.cs | 2 +- src/Artemis.UI/Screens/Root/RootViewModel.cs | 8 +- .../Screens/Settings/SettingsView.axaml | 6 +- .../Screens/Settings/SettingsViewModel.cs | 8 + .../Settings/Tabs/ReleasesTabViewModel.cs | 3 +- .../Settings/Updating/ReleaseViewModel.cs | 21 ++- .../Screens/Sidebar/SidebarViewModel.cs | 4 + .../Updating/IUpdateNotificationProvider.cs | 2 +- .../Services/Updating/IUpdateService.cs | 2 +- .../InAppUpdateNotificationProvider.cs | 66 ++++++++ .../SimpleUpdateNotificationProvider.cs | 12 -- .../Services/Updating/UpdateService.cs | 63 ++++---- 16 files changed, 291 insertions(+), 69 deletions(-) create mode 100644 src/Artemis.UI.Windows/Providers/WindowsUpdateNotificationProvider.cs create mode 100644 src/Artemis.UI/Services/Updating/InAppUpdateNotificationProvider.cs delete mode 100644 src/Artemis.UI/Services/Updating/SimpleUpdateNotificationProvider.cs diff --git a/src/Artemis.UI.Shared/Services/MainWindow/IMainWindowService.cs b/src/Artemis.UI.Shared/Services/MainWindow/IMainWindowService.cs index bf7716f0b..e42d644dc 100644 --- a/src/Artemis.UI.Shared/Services/MainWindow/IMainWindowService.cs +++ b/src/Artemis.UI.Shared/Services/MainWindow/IMainWindowService.cs @@ -1,4 +1,5 @@ using System; +using ReactiveUI; namespace Artemis.UI.Shared.Services.MainWindow; @@ -12,6 +13,11 @@ public interface IMainWindowService : IArtemisSharedUIService /// bool IsMainWindowOpen { get; } + /// + /// Gets or sets the host screen contained in the main window + /// + IScreen? HostScreen { get; set; } + /// /// Sets up the main window provider that controls the state of the main window /// diff --git a/src/Artemis.UI.Shared/Services/MainWindow/MainWindowService.cs b/src/Artemis.UI.Shared/Services/MainWindow/MainWindowService.cs index fbdda52e1..98a6cba19 100644 --- a/src/Artemis.UI.Shared/Services/MainWindow/MainWindowService.cs +++ b/src/Artemis.UI.Shared/Services/MainWindow/MainWindowService.cs @@ -1,4 +1,5 @@ using System; +using ReactiveUI; namespace Artemis.UI.Shared.Services.MainWindow; @@ -6,6 +7,12 @@ internal class MainWindowService : IMainWindowService { private IMainWindowProvider? _mainWindowManager; + /// + public bool IsMainWindowOpen { get; private set; } + + /// + public IScreen? HostScreen { get; set; } + protected virtual void OnMainWindowOpened() { MainWindowOpened?.Invoke(this, EventArgs.Empty); @@ -64,8 +71,6 @@ internal class MainWindowService : IMainWindowService OnMainWindowUnfocused(); } - public bool IsMainWindowOpen { get; private set; } - public void ConfigureMainWindowProvider(IMainWindowProvider mainWindowProvider) { if (mainWindowProvider == null) throw new ArgumentNullException(nameof(mainWindowProvider)); diff --git a/src/Artemis.UI.Windows/DryIoc/ContainerExtensions.cs b/src/Artemis.UI.Windows/DryIoc/ContainerExtensions.cs index 4b3bf7fda..337e21744 100644 --- a/src/Artemis.UI.Windows/DryIoc/ContainerExtensions.cs +++ b/src/Artemis.UI.Windows/DryIoc/ContainerExtensions.cs @@ -1,5 +1,6 @@ using Artemis.Core.Providers; using Artemis.Core.Services; +using Artemis.UI.Services.Updating; using Artemis.UI.Shared.Providers; using Artemis.UI.Windows.Providers; using Artemis.UI.Windows.Providers.Input; @@ -22,5 +23,6 @@ public static class UIContainerExtensions container.Register(Reuse.Singleton); container.Register(); container.Register(serviceKey: WindowsInputProvider.Id); + container.Register(); } } \ No newline at end of file diff --git a/src/Artemis.UI.Windows/Providers/WindowsUpdateNotificationProvider.cs b/src/Artemis.UI.Windows/Providers/WindowsUpdateNotificationProvider.cs new file mode 100644 index 000000000..3cc68f83f --- /dev/null +++ b/src/Artemis.UI.Windows/Providers/WindowsUpdateNotificationProvider.cs @@ -0,0 +1,146 @@ +using System; +using System.ComponentModel; +using System.Globalization; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Windows.UI.Notifications; +using Artemis.UI.Screens.Settings; +using Artemis.UI.Services.Updating; +using Artemis.UI.Shared.Services.MainWindow; +using Avalonia.Threading; +using Microsoft.Toolkit.Uwp.Notifications; +using ReactiveUI; + +namespace Artemis.UI.Windows.Providers; + +public class WindowsUpdateNotificationProvider : IUpdateNotificationProvider +{ + private readonly Func _getReleaseInstaller; + private readonly Func _getSettingsViewModel; + private readonly IMainWindowService _mainWindowService; + private readonly IUpdateService _updateService; + + public WindowsUpdateNotificationProvider(IMainWindowService mainWindowService, + IUpdateService updateService, + Func getSettingsViewModel, + Func getReleaseInstaller) + { + _mainWindowService = mainWindowService; + _updateService = updateService; + _getSettingsViewModel = getSettingsViewModel; + _getReleaseInstaller = getReleaseInstaller; + ToastNotificationManagerCompat.OnActivated += ToastNotificationManagerCompatOnOnActivated; + } + + private async void ToastNotificationManagerCompatOnOnActivated(ToastNotificationActivatedEventArgsCompat e) + { + ToastArguments args = ToastArguments.Parse(e.Argument); + string releaseId = args.Get("releaseId"); + string releaseVersion = args.Get("releaseVersion"); + string action = "view-changes"; + if (args.Contains("action")) + action = args.Get("action"); + + if (action == "install") + await InstallRelease(releaseId, releaseVersion); + else if (action == "view-changes") + ViewRelease(releaseId); + else if (action == "restart-for-update") + _updateService.RestartForUpdate(false); + } + + public async Task ShowNotification(string releaseId, string releaseVersion) + { + new ToastContentBuilder() + .AddArgument("releaseId", releaseId) + .AddArgument("releaseVersion", releaseVersion) + .AddText("Update available") + .AddText($"Artemis version {releaseVersion} has been released") + .AddButton(new ToastButton() + .SetContent("Install") + .AddArgument("action", "install").SetAfterActivationBehavior(ToastAfterActivationBehavior.PendingUpdate)) + .AddButton(new ToastButton().SetContent("View changes").AddArgument("action", "view-changes")) + .Show(t => t.Tag = releaseId); + } + + private void ViewRelease(string releaseId) + { + Dispatcher.UIThread.Post(() => + { + _mainWindowService.OpenMainWindow(); + + // TODO: When proper routing has been implemented, use that here + // Create a settings VM to navigate to + SettingsViewModel settingsViewModel = _getSettingsViewModel(_mainWindowService.HostScreen); + // Get the release tab + ReleasesTabViewModel releaseTabViewModel = (ReleasesTabViewModel) settingsViewModel.SettingTabs.First(t => t is ReleasesTabViewModel); + + // Navigate to the settings VM + _mainWindowService.HostScreen.Router.Navigate.Execute(settingsViewModel); + // Navigate to the release tab + releaseTabViewModel.PreselectId = releaseId; + settingsViewModel.SelectedTab = releaseTabViewModel; + }); + } + + private async Task InstallRelease(string releaseId, string releaseVersion) + { + ReleaseInstaller installer = _getReleaseInstaller(releaseId); + void InstallerOnPropertyChanged(object? sender, PropertyChangedEventArgs e) => UpdateInstallProgress(releaseId, installer); + + new ToastContentBuilder() + .AddArgument("releaseId", releaseId) + .AddArgument("releaseVersion", releaseVersion) + .AddAudio(new ToastAudio {Silent = true}) + .AddText("Installing Artemis update") + .AddVisualChild(new AdaptiveProgressBar() + { + Title = releaseVersion, + Value = new BindableProgressBarValue("progressValue"), + Status = new BindableString("progressStatus") + }) + .AddButton(new ToastButton().SetContent("Cancel").AddArgument("action", "cancel")) + .Show(t => + { + t.Tag = releaseId; + t.Data = GetInstallerNotificationData(installer); + }); + + await Task.Delay(2000); + installer.PropertyChanged += InstallerOnPropertyChanged; + await installer.InstallAsync(CancellationToken.None); + installer.PropertyChanged -= InstallerOnPropertyChanged; + + _updateService.QueueUpdate(); + + new ToastContentBuilder() + .AddArgument("releaseId", releaseId) + .AddArgument("releaseVersion", releaseVersion) + .AddAudio(new ToastAudio {Silent = true}) + .AddText("Update ready") + .AddText($"Artemis version {releaseVersion} is ready to be applied") + .AddButton(new ToastButton().SetContent("Restart Artemis").AddArgument("action", "restart-for-update")) + .AddButton(new ToastButton().SetContent("Later").AddArgument("action", "postpone-update")) + .Show(t => t.Tag = releaseId); + } + + private void UpdateInstallProgress(string releaseId, ReleaseInstaller installer) + { + ToastNotificationManagerCompat.CreateToastNotifier().Update(GetInstallerNotificationData(installer), releaseId); + } + + private NotificationData GetInstallerNotificationData(ReleaseInstaller installer) + { + NotificationData data = new() + { + Values = + { + ["progressValue"] = (installer.Progress / 100f).ToString(CultureInfo.InvariantCulture), + ["progressStatus"] = installer.Status + } + }; + + return data; + } +} \ No newline at end of file diff --git a/src/Artemis.UI/DryIoc/ContainerExtensions.cs b/src/Artemis.UI/DryIoc/ContainerExtensions.cs index da1dcd815..0f2ae6862 100644 --- a/src/Artemis.UI/DryIoc/ContainerExtensions.cs +++ b/src/Artemis.UI/DryIoc/ContainerExtensions.cs @@ -36,7 +36,7 @@ public static class UIContainerExtensions container.Register(Reuse.Singleton); container.Register(Reuse.Singleton); - container.Register(); + container.Register(); container.RegisterMany(thisAssembly, type => type.IsAssignableTo(), Reuse.Singleton); } diff --git a/src/Artemis.UI/Screens/Root/RootViewModel.cs b/src/Artemis.UI/Screens/Root/RootViewModel.cs index fbd81e943..2037d0b1a 100644 --- a/src/Artemis.UI/Screens/Root/RootViewModel.cs +++ b/src/Artemis.UI/Screens/Root/RootViewModel.cs @@ -58,7 +58,8 @@ public class RootViewModel : ActivatableViewModelBase, IScreen, IMainWindowProvi _lifeTime = (IClassicDesktopStyleApplicationLifetime) Application.Current!.ApplicationLifetime!; mainWindowService.ConfigureMainWindowProvider(this); - + mainWindowService.HostScreen = this; + DisplayAccordingToSettings(); Router.CurrentViewModel.Subscribe(UpdateTitleBarViewModel); Task.Run(() => @@ -230,11 +231,6 @@ public class RootViewModel : ActivatableViewModelBase, IScreen, IMainWindowProvi } #endregion - - public void SaveWindowBounds(int x, int y, int width, int height) - { - throw new NotImplementedException(); - } } internal class EmptyViewModel : MainScreenViewModel diff --git a/src/Artemis.UI/Screens/Settings/SettingsView.axaml b/src/Artemis.UI/Screens/Settings/SettingsView.axaml index 96433edb6..78ac70be4 100644 --- a/src/Artemis.UI/Screens/Settings/SettingsView.axaml +++ b/src/Artemis.UI/Screens/Settings/SettingsView.axaml @@ -2,10 +2,12 @@ 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:settings="clr-namespace:Artemis.UI.Screens.Settings" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" - x:Class="Artemis.UI.Screens.Settings.SettingsView"> + x:Class="Artemis.UI.Screens.Settings.SettingsView" + x:DataType="settings:SettingsViewModel"> - + diff --git a/src/Artemis.UI/Screens/Settings/SettingsViewModel.cs b/src/Artemis.UI/Screens/Settings/SettingsViewModel.cs index 773656c1f..c91b33f8f 100644 --- a/src/Artemis.UI/Screens/Settings/SettingsViewModel.cs +++ b/src/Artemis.UI/Screens/Settings/SettingsViewModel.cs @@ -6,6 +6,8 @@ namespace Artemis.UI.Screens.Settings; public class SettingsViewModel : MainScreenViewModel { + private ActivatableViewModelBase _selectedTab; + public SettingsViewModel(IScreen hostScreen, GeneralTabViewModel generalTabViewModel, PluginsTabViewModel pluginsTabViewModel, @@ -24,4 +26,10 @@ public class SettingsViewModel : MainScreenViewModel } public ObservableCollection SettingTabs { get; } + + public ActivatableViewModelBase SelectedTab + { + get => _selectedTab; + set => RaiseAndSetIfChanged(ref _selectedTab, value); + } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Settings/Tabs/ReleasesTabViewModel.cs b/src/Artemis.UI/Screens/Settings/Tabs/ReleasesTabViewModel.cs index 61da057c8..d7e3a6517 100644 --- a/src/Artemis.UI/Screens/Settings/Tabs/ReleasesTabViewModel.cs +++ b/src/Artemis.UI/Screens/Settings/Tabs/ReleasesTabViewModel.cs @@ -51,11 +51,12 @@ public class ReleasesTabViewModel : ActivatableViewModelBase { await updateService.CacheLatestRelease(); await GetMoreReleases(d.AsCancellationToken()); - SelectedReleaseViewModel = ReleaseViewModels.FirstOrDefault(); + SelectedReleaseViewModel = ReleaseViewModels.FirstOrDefault(r => r.ReleaseId == PreselectId) ?? ReleaseViewModels.FirstOrDefault(); }); } public ReadOnlyObservableCollection ReleaseViewModels { get; } + public string? PreselectId { get; set; } public ReleaseViewModel? SelectedReleaseViewModel { diff --git a/src/Artemis.UI/Screens/Settings/Updating/ReleaseViewModel.cs b/src/Artemis.UI/Screens/Settings/Updating/ReleaseViewModel.cs index 8201ecccf..bf965746a 100644 --- a/src/Artemis.UI/Screens/Settings/Updating/ReleaseViewModel.cs +++ b/src/Artemis.UI/Screens/Settings/Updating/ReleaseViewModel.cs @@ -22,7 +22,7 @@ public class ReleaseViewModel : ActivatableViewModelBase { private readonly ILogger _logger; private readonly INotificationService _notificationService; - private readonly string _releaseId; + private readonly IUpdateService _updateService; private readonly Platform _updatePlatform; private readonly IUpdatingClient _updatingClient; private readonly IWindowService _windowService; @@ -46,10 +46,10 @@ public class ReleaseViewModel : ActivatableViewModelBase IUpdateService updateService, IWindowService windowService) { - _releaseId = releaseId; _logger = logger; _updatingClient = updatingClient; _notificationService = notificationService; + _updateService = updateService; _windowService = windowService; if (OperatingSystem.IsWindows()) @@ -61,12 +61,13 @@ public class ReleaseViewModel : ActivatableViewModelBase else throw new PlatformNotSupportedException("Cannot auto update on the current platform"); + ReleaseId = releaseId; Version = version; CreatedAt = createdAt; - ReleaseInstaller = updateService.GetReleaseInstaller(_releaseId); + ReleaseInstaller = updateService.GetReleaseInstaller(ReleaseId); Install = ReactiveCommand.CreateFromTask(ExecuteInstall); - Restart = ReactiveCommand.Create(() => Utilities.ApplyUpdate(false)); + Restart = ReactiveCommand.Create(ExecuteRestart); CancelInstall = ReactiveCommand.Create(() => _installerCts?.Cancel()); this.WhenActivated(d => @@ -74,12 +75,19 @@ public class ReleaseViewModel : ActivatableViewModelBase // There's no point in running anything but the latest version of the current channel. // Perhaps later that won't be true anymore, then we could consider allowing to install // older versions with compatible database versions. - InstallationAvailable = updateService.CachedLatestRelease?.Id == _releaseId; + InstallationAvailable = _updateService.CachedLatestRelease?.Id == ReleaseId; RetrieveDetails(d.AsCancellationToken()).ToObservable(); Disposable.Create(_installerCts, cts => cts?.Cancel()).DisposeWith(d); }); } + public string ReleaseId { get; } + + private void ExecuteRestart() + { + _updateService.RestartForUpdate(false); + } + public ReactiveCommand Restart { get; set; } public ReactiveCommand Install { get; } public ReactiveCommand CancelInstall { get; } @@ -148,6 +156,7 @@ public class ReleaseViewModel : ActivatableViewModelBase { InstallationInProgress = true; await ReleaseInstaller.InstallAsync(_installerCts.Token); + _updateService.QueueUpdate(); InstallationFinished = true; } catch (TaskCanceledException) @@ -173,7 +182,7 @@ public class ReleaseViewModel : ActivatableViewModelBase { Loading = true; - IOperationResult result = await _updatingClient.GetReleaseById.ExecuteAsync(_releaseId, cancellationToken); + IOperationResult result = await _updatingClient.GetReleaseById.ExecuteAsync(ReleaseId, cancellationToken); IGetReleaseById_PublishedRelease? release = result.Data?.PublishedRelease; if (release == null) return; diff --git a/src/Artemis.UI/Screens/Sidebar/SidebarViewModel.cs b/src/Artemis.UI/Screens/Sidebar/SidebarViewModel.cs index 4c3b971ad..7e1756384 100644 --- a/src/Artemis.UI/Screens/Sidebar/SidebarViewModel.cs +++ b/src/Artemis.UI/Screens/Sidebar/SidebarViewModel.cs @@ -137,6 +137,10 @@ public class SidebarViewModel : ActivatableViewModelBase private void NavigateToScreen(SidebarScreenViewModel sidebarScreenViewModel) { + // If the current screen changed through external means and already matches, don't navigate again + if (_hostScreen.Router.GetCurrentViewModel()?.GetType() == sidebarScreenViewModel.ScreenType) + return; + _hostScreen.Router.Navigate.Execute(sidebarScreenViewModel.CreateInstance(_container, _hostScreen)); _profileEditorService.ChangeCurrentProfileConfiguration(null); } diff --git a/src/Artemis.UI/Services/Updating/IUpdateNotificationProvider.cs b/src/Artemis.UI/Services/Updating/IUpdateNotificationProvider.cs index fcf306bb4..0b7722609 100644 --- a/src/Artemis.UI/Services/Updating/IUpdateNotificationProvider.cs +++ b/src/Artemis.UI/Services/Updating/IUpdateNotificationProvider.cs @@ -4,5 +4,5 @@ namespace Artemis.UI.Services.Updating; public interface IUpdateNotificationProvider { - Task ShowNotification(string releaseId); + Task ShowNotification(string releaseId, string releaseVersion); } \ No newline at end of file diff --git a/src/Artemis.UI/Services/Updating/IUpdateService.cs b/src/Artemis.UI/Services/Updating/IUpdateService.cs index 7b619900c..db716859e 100644 --- a/src/Artemis.UI/Services/Updating/IUpdateService.cs +++ b/src/Artemis.UI/Services/Updating/IUpdateService.cs @@ -11,8 +11,8 @@ public interface IUpdateService : IArtemisUIService Task CacheLatestRelease(); Task CheckForUpdate(); - Task InstallRelease(string releaseId); void QueueUpdate(); ReleaseInstaller GetReleaseInstaller(string releaseId); + void RestartForUpdate(bool silent); } \ No newline at end of file diff --git a/src/Artemis.UI/Services/Updating/InAppUpdateNotificationProvider.cs b/src/Artemis.UI/Services/Updating/InAppUpdateNotificationProvider.cs new file mode 100644 index 000000000..be08d2644 --- /dev/null +++ b/src/Artemis.UI/Services/Updating/InAppUpdateNotificationProvider.cs @@ -0,0 +1,66 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Artemis.UI.Screens.Settings; +using Artemis.UI.Shared.Services; +using Artemis.UI.Shared.Services.Builders; +using Artemis.UI.Shared.Services.MainWindow; +using ReactiveUI; + +namespace Artemis.UI.Services.Updating; + +public class InAppUpdateNotificationProvider : IUpdateNotificationProvider +{ + private readonly Func _getSettingsViewModel; + private readonly IMainWindowService _mainWindowService; + private readonly INotificationService _notificationService; + private Action? _notification; + + public InAppUpdateNotificationProvider(INotificationService notificationService, IMainWindowService mainWindowService, Func getSettingsViewModel) + { + _notificationService = notificationService; + _mainWindowService = mainWindowService; + _getSettingsViewModel = getSettingsViewModel; + } + + private void ShowInAppNotification(string releaseId, string releaseVersion) + { + _notification?.Invoke(); + _notification = _notificationService.CreateNotification() + .WithTitle("Update available") + .WithMessage($"Artemis version {releaseVersion} has been released") + .WithSeverity(NotificationSeverity.Success) + .WithTimeout(TimeSpan.FromSeconds(15)) + .HavingButton(b => b.WithText("View release").WithAction(() => ViewRelease(releaseId))) + .Show(); + } + + private void ViewRelease(string releaseId) + { + _notification?.Invoke(); + + if (_mainWindowService.HostScreen == null) + return; + + // TODO: When proper routing has been implemented, use that here + // Create a settings VM to navigate to + SettingsViewModel settingsViewModel = _getSettingsViewModel(_mainWindowService.HostScreen); + // Get the release tab + ReleasesTabViewModel releaseTabViewModel = (ReleasesTabViewModel) settingsViewModel.SettingTabs.First(t => t is ReleasesTabViewModel); + + // Navigate to the settings VM + _mainWindowService.HostScreen.Router.Navigate.Execute(settingsViewModel); + // Navigate to the release tab + releaseTabViewModel.PreselectId = releaseId; + settingsViewModel.SelectedTab = releaseTabViewModel; + } + + /// + public async Task ShowNotification(string releaseId, string releaseVersion) + { + if (_mainWindowService.IsMainWindowOpen) + ShowInAppNotification(releaseId, releaseVersion); + else + _mainWindowService.MainWindowOpened += (_, _) => ShowInAppNotification(releaseId, releaseVersion); + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Services/Updating/SimpleUpdateNotificationProvider.cs b/src/Artemis.UI/Services/Updating/SimpleUpdateNotificationProvider.cs deleted file mode 100644 index 86f8f7a91..000000000 --- a/src/Artemis.UI/Services/Updating/SimpleUpdateNotificationProvider.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Threading.Tasks; - -namespace Artemis.UI.Services.Updating; - -public class SimpleUpdateNotificationProvider : IUpdateNotificationProvider -{ - /// - public async Task ShowNotification(string releaseId) - { - throw new System.NotImplementedException(); - } -} \ No newline at end of file diff --git a/src/Artemis.UI/Services/Updating/UpdateService.cs b/src/Artemis.UI/Services/Updating/UpdateService.cs index fb387a48f..2b97df2de 100644 --- a/src/Artemis.UI/Services/Updating/UpdateService.cs +++ b/src/Artemis.UI/Services/Updating/UpdateService.cs @@ -6,8 +6,6 @@ using Artemis.Core; using Artemis.Core.Services; using Artemis.Storage.Entities.General; using Artemis.Storage.Repositories.Interfaces; -using Artemis.UI.Screens.Settings.Updating; -using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services.MainWindow; using Artemis.WebClient.Updating; using Avalonia.Threading; @@ -69,7 +67,7 @@ public class UpdateService : IUpdateService _channel.Value = "feature/gh-actions"; _channel.Save(); - + InstallQueuedUpdate(); } @@ -79,30 +77,21 @@ public class UpdateService : IUpdateService if (!_queuedActionRepository.IsTypeQueued("InstallUpdate")) return; + // Remove the queued action, in case something goes wrong then at least we don't end up in a loop _queuedActionRepository.ClearByType("InstallUpdate"); + _logger.Information("Installing queued update"); Utilities.ApplyUpdate(false); } - private async Task ShowUpdateDialog(string nextReleaseId) + private async Task ShowUpdateNotification(IGetNextRelease_NextPublishedRelease release) { - - - await Dispatcher.UIThread.InvokeAsync(async () => - { - // Main window is probably already open but this will bring it into focus - _mainWindowService.OpenMainWindow(); - }); + await _updateNotificationProvider.Value.ShowNotification(release.Id, release.Version); } - private async Task ShowUpdateNotification(string nextReleaseId) + private async Task AutoInstallUpdate(IGetNextRelease_NextPublishedRelease release) { - await _updateNotificationProvider.Value.ShowNotification(nextReleaseId); - } - - private async Task AutoInstallUpdate(string nextReleaseId) - { - ReleaseInstaller installer = _getReleaseInstaller(nextReleaseId); + ReleaseInstaller installer = _getReleaseInstaller(release.Id); await installer.InstallAsync(CancellationToken.None); Utilities.ApplyUpdate(true); } @@ -121,7 +110,7 @@ public class UpdateService : IUpdateService _logger.Warning(ex, "Auto update failed"); } } - + public IGetNextRelease_NextPublishedRelease? CachedLatestRelease { get; private set; } public string? CurrentVersion @@ -139,12 +128,12 @@ public class UpdateService : IUpdateService IOperationResult result = await _updatingClient.GetNextRelease.ExecuteAsync(CurrentVersion, _channel.Value, _updatePlatform); CachedLatestRelease = result.Data?.NextPublishedRelease; } - + public async Task CheckForUpdate() { IOperationResult result = await _updatingClient.GetNextRelease.ExecuteAsync(CurrentVersion, _channel.Value, _updatePlatform); result.EnsureNoErrors(); - + // Update cache CachedLatestRelease = result.Data?.NextPublishedRelease; @@ -156,27 +145,14 @@ public class UpdateService : IUpdateService _suspendAutoCheck = true; // If the window is open show the changelog, don't auto-update while the user is busy - if (_mainWindowService.IsMainWindowOpen) - await ShowUpdateDialog(CachedLatestRelease.Id); - else if (!_autoInstall.Value) - await ShowUpdateNotification(CachedLatestRelease.Id); + if (!_autoInstall.Value) + await ShowUpdateNotification(CachedLatestRelease); else - await AutoInstallUpdate(CachedLatestRelease.Id); + await AutoInstallUpdate(CachedLatestRelease); return true; } - /// - public async Task InstallRelease(string releaseId) - { - ReleaseInstaller installer = _getReleaseInstaller(releaseId); - await Dispatcher.UIThread.InvokeAsync(() => - { - // Main window is probably already open but this will bring it into focus - _mainWindowService.OpenMainWindow(); - }); - } - /// public void QueueUpdate() { @@ -184,9 +160,22 @@ public class UpdateService : IUpdateService _queuedActionRepository.Add(new QueuedActionEntity {Type = "InstallUpdate"}); } + /// + public void DequeueUpdate() + { + _queuedActionRepository.ClearByType("InstallUpdate"); + } + /// public ReleaseInstaller GetReleaseInstaller(string releaseId) { return _getReleaseInstaller(releaseId); } + + /// + public void RestartForUpdate(bool silent) + { + DequeueUpdate(); + Utilities.ApplyUpdate(silent); + } } \ No newline at end of file