diff --git a/StabilityMatrix.Avalonia/DesignData/DesignData.cs b/StabilityMatrix.Avalonia/DesignData/DesignData.cs index 8abeb2d0..8b0d3517 100644 --- a/StabilityMatrix.Avalonia/DesignData/DesignData.cs +++ b/StabilityMatrix.Avalonia/DesignData/DesignData.cs @@ -11,6 +11,7 @@ using StabilityMatrix.Avalonia.Models; using StabilityMatrix.Avalonia.Services; using StabilityMatrix.Avalonia.ViewModels; using StabilityMatrix.Avalonia.ViewModels.Dialogs; +using StabilityMatrix.Avalonia.ViewModels.PackageManager; using StabilityMatrix.Core.Api; using StabilityMatrix.Core.Database; using StabilityMatrix.Core.Helper; @@ -254,7 +255,11 @@ public static class DesignData { get { + var settings = Services.GetRequiredService(); var vm = Services.GetRequiredService(); + vm.Packages = new ObservableCollection( + settings.Settings.InstalledPackages.Select(p => + DialogFactory.Get(viewModel => viewModel.Package = p))); vm.Packages.First().IsUpdateAvailable = true; return vm; } diff --git a/StabilityMatrix.Avalonia/Program.cs b/StabilityMatrix.Avalonia/Program.cs index d37f84a6..c693b224 100644 --- a/StabilityMatrix.Avalonia/Program.cs +++ b/StabilityMatrix.Avalonia/Program.cs @@ -12,7 +12,6 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Threading; -using Microsoft.Extensions.DependencyInjection; using NLog; using Polly.Contrib.WaitAndRetry; using Projektanker.Icons.Avalonia; diff --git a/StabilityMatrix.Avalonia/Services/ServiceManager.cs b/StabilityMatrix.Avalonia/Services/ServiceManager.cs index dc7387cf..157f01fe 100644 --- a/StabilityMatrix.Avalonia/Services/ServiceManager.cs +++ b/StabilityMatrix.Avalonia/Services/ServiceManager.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; namespace StabilityMatrix.Avalonia.Services; @@ -129,4 +130,13 @@ public class ServiceManager initializer(instance); return instance; } + + /// + /// Get a view model instance with an initializer parameter, but asynchronously + /// + public async Task GetAsync(Func> initializer) where TService : T + { + var instance = Get(); + return await initializer(instance); + } } diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/UpdateViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/UpdateViewModel.cs index f85232bc..22a6b9ab 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/UpdateViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/UpdateViewModel.cs @@ -6,7 +6,6 @@ using System.Threading.Tasks; using AsyncAwaitBestPractices; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; -using Python.Runtime; using StabilityMatrix.Avalonia.Views.Dialogs; using StabilityMatrix.Core.Attributes; using StabilityMatrix.Core.Helper; @@ -14,7 +13,6 @@ using StabilityMatrix.Core.Models.Progress; using StabilityMatrix.Core.Models.Update; using StabilityMatrix.Core.Services; using StabilityMatrix.Core.Updater; -using Dispatcher = Avalonia.Threading.Dispatcher; namespace StabilityMatrix.Avalonia.ViewModels.Dialogs; diff --git a/StabilityMatrix.Avalonia/ViewModels/LaunchPageViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/LaunchPageViewModel.cs index fe4f1491..bba13dca 100644 --- a/StabilityMatrix.Avalonia/ViewModels/LaunchPageViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/LaunchPageViewModel.cs @@ -135,10 +135,8 @@ public partial class LaunchPageViewModel : PageViewModelBase, IDisposable, IAsyn NotificationType.Error); return; } - - OnLoaded(); - if (SelectedPackage is null) return; - + + SelectedPackage = InstalledPackages.FirstOrDefault(x => x.Id == e); LaunchAsync().SafeFireAndForget(); } diff --git a/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs index dd86595c..e4e79943 100644 --- a/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs @@ -1,7 +1,10 @@ using System; using System.IO; +using System.Linq; using System.Threading.Tasks; +using AsyncAwaitBestPractices; using Avalonia.Controls.Notifications; +using Avalonia.Threading; using CommunityToolkit.Mvvm.ComponentModel; using FluentAvalonia.UI.Controls; using NLog; @@ -25,6 +28,7 @@ public partial class PackageCardViewModel : ProgressViewModel [ObservableProperty] private InstalledPackage? package; [ObservableProperty] private Uri cardImage; [ObservableProperty] private bool isUpdateAvailable; + [ObservableProperty] private string installedVersion; public PackageCardViewModel(IPackageFactory packageFactory, INotificationService notificationService, ISettingsManager settingsManager) @@ -41,6 +45,12 @@ public partial class PackageCardViewModel : ProgressViewModel var basePackage = packageFactory[value.PackageName]; CardImage = basePackage?.PreviewImageUri ?? Assets.NoImage; + InstalledVersion = value.DisplayVersion ?? "Unknown"; + } + + public override async Task OnLoadedAsync() + { + IsUpdateAvailable = await HasUpdate(); } public void Launch() @@ -49,7 +59,6 @@ public partial class PackageCardViewModel : ProgressViewModel return; settingsManager.Transaction(s => s.ActiveInstalledPackageId = Package.Id); - EventManager.Instance.RequestPageChange(typeof(LaunchPageViewModel)); EventManager.Instance.OnPackageLaunchRequested(Package.Id); } @@ -100,7 +109,7 @@ public partial class PackageCardViewModel : ProgressViewModel public async Task Update() { if (Package == null) return; - var basePackage = packageFactory.FindPackageByName(Package.PackageName); + var basePackage = packageFactory[Package.PackageName!]; if (basePackage == null) { logger.Error("Could not find package {SelectedPackagePackageName}", @@ -108,7 +117,7 @@ public partial class PackageCardViewModel : ProgressViewModel return; } - Text = $"Updating {Package.DisplayName} to latest version..."; + Text = $"Updating {Package.DisplayName}"; basePackage.InstallLocation = Package.FullPath!; var progress = new Progress(progress => { @@ -116,7 +125,7 @@ public partial class PackageCardViewModel : ProgressViewModel Value = percent; IsIndeterminate = progress.IsIndeterminate; - Text = $"Updating {Package.DisplayName} to latest version... {percent:N0}%"; + Text = $"Updating {Package.DisplayName}"; EventManager.Instance.OnGlobalProgressChanged(percent); }); @@ -146,6 +155,10 @@ public partial class PackageCardViewModel : ProgressViewModel Package.UpdateAvailable = false; IsUpdateAvailable = false; + IsIndeterminate = false; + InstalledVersion = settingsManager.Settings.InstalledPackages + .First(x => x.Id == Package.Id).DisplayVersion ?? "Unknown"; + Value = 0; } /// @@ -232,19 +245,23 @@ public partial class PackageCardViewModel : ProgressViewModel var basePackage = packageFactory[Package.PackageName!]; if (basePackage == null) return false; - - var canCheckUpdate = Package.LastUpdateCheck == null || + + var canCheckUpdate = Package.LastUpdateCheck == null || Package.LastUpdateCheck < DateTime.Now.AddMinutes(-15); - - if (!canCheckUpdate) return false; - - var hasUpdate = await basePackage.CheckForUpdates(Package); - Package.UpdateAvailable = hasUpdate; - Package.LastUpdateCheck = DateTimeOffset.Now; - IsUpdateAvailable = true; - settingsManager.SetLastUpdateCheck(Package); - return hasUpdate; + if (!canCheckUpdate) return false; + try + { + var hasUpdate = await basePackage.CheckForUpdates(Package); + Package.UpdateAvailable = hasUpdate; + Package.LastUpdateCheck = DateTimeOffset.Now; + settingsManager.SetLastUpdateCheck(Package); + return hasUpdate; + } + catch (Exception e) + { + return false; + } } } diff --git a/StabilityMatrix.Avalonia/ViewModels/PackageManagerViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/PackageManagerViewModel.cs index c9b5c535..ede1e7f2 100644 --- a/StabilityMatrix.Avalonia/ViewModels/PackageManagerViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/PackageManagerViewModel.cs @@ -1,18 +1,12 @@ using System; using System.Collections.Immutable; using System.Collections.ObjectModel; -using System.IO; using System.Linq; using System.Threading.Tasks; using AsyncAwaitBestPractices; using Avalonia.Controls; -using Avalonia.Controls.Notifications; -using Avalonia.Threading; using CommunityToolkit.Mvvm.ComponentModel; -using CommunityToolkit.Mvvm.Input; using FluentAvalonia.UI.Controls; -using Microsoft.Extensions.Logging; -using Polly; using StabilityMatrix.Avalonia.Controls; using StabilityMatrix.Avalonia.Services; using StabilityMatrix.Avalonia.ViewModels.Dialogs; @@ -22,9 +16,6 @@ using StabilityMatrix.Avalonia.Views.Dialogs; using StabilityMatrix.Core.Attributes; using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Helper.Factory; -using StabilityMatrix.Core.Models; -using StabilityMatrix.Core.Models.Packages; -using StabilityMatrix.Core.Models.Progress; using StabilityMatrix.Core.Services; using Symbol = FluentIcons.Common.Symbol; using SymbolIconSource = FluentIcons.FluentAvalonia.SymbolIconSource; @@ -38,28 +29,16 @@ namespace StabilityMatrix.Avalonia.ViewModels; [View(typeof(PackageManagerPage))] public partial class PackageManagerViewModel : PageViewModelBase { - private readonly ILogger logger; private readonly ISettingsManager settingsManager; private readonly IPackageFactory packageFactory; - private readonly INotificationService notificationService; private readonly ServiceManager dialogFactory; - private const int MinutesToWaitForUpdateCheck = 60; - - public PackageManagerViewModel(ILogger logger, - ISettingsManager settingsManager, IPackageFactory packageFactory, - INotificationService notificationService, ServiceManager dialogFactory) + public PackageManagerViewModel(ISettingsManager settingsManager, IPackageFactory packageFactory, + ServiceManager dialogFactory) { - this.logger = logger; this.settingsManager = settingsManager; this.packageFactory = packageFactory; - this.notificationService = notificationService; this.dialogFactory = dialogFactory; - - Packages = - new ObservableCollection( - settingsManager.Settings.InstalledPackages.Select(p => - dialogFactory.Get(vm => vm.Package = p))); EventManager.Instance.InstalledPackagesChanged += OnInstalledPackagesChanged; } @@ -70,15 +49,25 @@ public partial class PackageManagerViewModel : PageViewModelBase public override bool CanNavigatePrevious { get; protected set; } public override string Title => "Packages"; public override IconSource IconSource => new SymbolIconSource { Symbol = Symbol.Box, IsFilled = true}; - - public override void OnLoaded() + + public override async Task OnLoadedAsync() { - Packages = - new ObservableCollection( - settingsManager.Settings.InstalledPackages.Select(p => - dialogFactory.Get(vm => vm.Package = p))); + if (Design.IsDesignMode) return; + + var installedPackages = settingsManager.Settings.InstalledPackages; + Packages = new ObservableCollection(installedPackages.Select( + package => dialogFactory.Get(vm => + { + vm.Package = package; + return vm; + }))); + + foreach (var package in Packages) + { + await package.OnLoadedAsync(); + } } - + public async Task ShowInstallDialog() { var viewModel = dialogFactory.Get(); @@ -100,8 +89,8 @@ public partial class PackageManagerViewModel : PageViewModelBase }; await dialog.ShowAsync(); - OnLoaded(); + await OnLoadedAsync(); } - - private void OnInstalledPackagesChanged(object? sender, EventArgs e) => OnLoaded(); + + private void OnInstalledPackagesChanged(object? sender, EventArgs e) =>OnLoaded(); } diff --git a/StabilityMatrix.Avalonia/Views/PackageManagerPage.axaml b/StabilityMatrix.Avalonia/Views/PackageManagerPage.axaml index 9b084b58..0716d8f9 100644 --- a/StabilityMatrix.Avalonia/Views/PackageManagerPage.axaml +++ b/StabilityMatrix.Avalonia/Views/PackageManagerPage.axaml @@ -29,15 +29,16 @@