From c3b4f5afadf0f4e1f450cdd821ae5556a4506ae2 Mon Sep 17 00:00:00 2001 From: Ionite Date: Thu, 21 Sep 2023 17:08:23 -0400 Subject: [PATCH] Add confirm dialog for model downloads --- StabilityMatrix.Avalonia/App.axaml.cs | 3 + .../DesignData/DesignData.cs | 8 ++ .../Services/InferenceClientManager.cs | 20 +++-- .../Dialogs/DownloadResourceViewModel.cs | 78 +++++++++++++++++++ .../Inference/UpscalerCardViewModel.cs | 17 +++- .../Dialogs/DownloadResourceDialog.axaml | 50 ++++++++++++ .../Dialogs/DownloadResourceDialog.axaml.cs | 27 +++++++ StabilityMatrix.Core/Models/IContextAction.cs | 1 + .../Models/ModelPostDownloadContextAction.cs | 17 ++++ 9 files changed, 209 insertions(+), 12 deletions(-) create mode 100644 StabilityMatrix.Avalonia/ViewModels/Dialogs/DownloadResourceViewModel.cs create mode 100644 StabilityMatrix.Avalonia/Views/Dialogs/DownloadResourceDialog.axaml create mode 100644 StabilityMatrix.Avalonia/Views/Dialogs/DownloadResourceDialog.axaml.cs create mode 100644 StabilityMatrix.Core/Models/ModelPostDownloadContextAction.cs diff --git a/StabilityMatrix.Avalonia/App.axaml.cs b/StabilityMatrix.Avalonia/App.axaml.cs index a89d8f4f..cb4a3e37 100644 --- a/StabilityMatrix.Avalonia/App.axaml.cs +++ b/StabilityMatrix.Avalonia/App.axaml.cs @@ -296,6 +296,7 @@ public sealed class App : Application services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); // Dialog view models (singleton) services.AddSingleton(); @@ -365,6 +366,7 @@ public sealed class App : Application .Register(provider.GetRequiredService) .Register(provider.GetRequiredService) .Register(provider.GetRequiredService) + .Register(provider.GetRequiredService) ); } @@ -408,6 +410,7 @@ public sealed class App : Application services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); // Controls services.AddTransient(); diff --git a/StabilityMatrix.Avalonia/DesignData/DesignData.cs b/StabilityMatrix.Avalonia/DesignData/DesignData.cs index 33b31e63..eac6cf7a 100644 --- a/StabilityMatrix.Avalonia/DesignData/DesignData.cs +++ b/StabilityMatrix.Avalonia/DesignData/DesignData.cs @@ -614,6 +614,14 @@ The gallery images are often inpainted, but you will get something very similar vm.ImageSizeText = "1280 x 1792"; }); + public static DownloadResourceViewModel DownloadResourceViewModel => + DialogFactory.Get(vm => + { + vm.FileName = ComfyUpscaler.DefaultDownloadableModels[0].Name; + vm.FileSize = Convert.ToInt64(2 * Size.GiB); + vm.Resource = ComfyUpscaler.DefaultDownloadableModels[0].DownloadableResource!.Value; + }); + public static SharpenCardViewModel SharpenCardViewModel => DialogFactory.Get(); diff --git a/StabilityMatrix.Avalonia/Services/InferenceClientManager.cs b/StabilityMatrix.Avalonia/Services/InferenceClientManager.cs index eaf3ba15..1c14cc09 100644 --- a/StabilityMatrix.Avalonia/Services/InferenceClientManager.cs +++ b/StabilityMatrix.Avalonia/Services/InferenceClientManager.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Reactive.Linq; using System.Threading; using System.Threading.Tasks; using AsyncAwaitBestPractices; @@ -98,12 +99,7 @@ public partial class InferenceClientManager : ObservableObject, IInferenceClient vaeModelsDefaults.AddOrUpdate(HybridModelFile.Default); - vaeModelsDefaults - .Connect() - .Or(vaeModelsSource.Connect()) - .DeferUntilLoaded() - .Bind(VaeModels) - .Subscribe(); + vaeModelsDefaults.Connect().Or(vaeModelsSource.Connect()).Bind(VaeModels).Subscribe(); samplersSource.Connect().DeferUntilLoaded().Bind(Samplers).Subscribe(); @@ -111,7 +107,11 @@ public partial class InferenceClientManager : ObservableObject, IInferenceClient .Connect() .Or(modelUpscalersSource.Connect()) .Or(downloadableUpscalersSource.Connect()) - .DeferUntilLoaded() + .Sort( + SortExpressionComparer + .Ascending(f => f.Type) + .ThenByAscending(f => f.Name) + ) .Bind(Upscalers) .Subscribe(); @@ -129,6 +129,8 @@ public partial class InferenceClientManager : ObservableObject, IInferenceClient if (!settingsManager.IsLibraryDirSet) return; + ResetSharedProperties(); + if (IsConnected) { LoadSharedPropertiesAsync() @@ -136,10 +138,6 @@ public partial class InferenceClientManager : ObservableObject, IInferenceClient onException: ex => logger.LogError(ex, "Error loading shared properties") ); } - else - { - ResetSharedProperties(); - } }; } diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/DownloadResourceViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/DownloadResourceViewModel.cs new file mode 100644 index 00000000..aaf2aaa0 --- /dev/null +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/DownloadResourceViewModel.cs @@ -0,0 +1,78 @@ +using System.Threading.Tasks; +using Avalonia.Controls; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using FluentAvalonia.UI.Controls; +using StabilityMatrix.Avalonia.Controls; +using StabilityMatrix.Avalonia.Languages; +using StabilityMatrix.Avalonia.ViewModels.Base; +using StabilityMatrix.Avalonia.Views.Dialogs; +using StabilityMatrix.Core.Attributes; +using StabilityMatrix.Core.Helper; +using StabilityMatrix.Core.Models; +using StabilityMatrix.Core.Processes; +using StabilityMatrix.Core.Services; + +namespace StabilityMatrix.Avalonia.ViewModels.Dialogs; + +[View(typeof(DownloadResourceDialog))] +public partial class DownloadResourceViewModel : ContentDialogViewModelBase +{ + private readonly IDownloadService downloadService; + + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(FileNameWithHash))] + private string? fileName; + + public string FileNameWithHash => $"{FileName} [{Resource.HashSha256.ToLowerInvariant()[..7]}]"; + + [ObservableProperty] + [NotifyPropertyChangedFor(nameof(FileSizeText))] + private long fileSize; + + [ObservableProperty] + private RemoteResource resource; + + public string? FileSizeText => FileSize == 0 ? null : Size.FormatBase10Bytes(FileSize); + + public string ShortHash => Resource.HashSha256.ToLowerInvariant(); + + public DownloadResourceViewModel(IDownloadService downloadService) + { + this.downloadService = downloadService; + } + + /// + public override async Task OnLoadedAsync() + { + await base.OnLoadedAsync(); + + // Get download size + if (!Design.IsDesignMode && Resource.Url is { } url) + { + FileSize = await downloadService.GetFileSizeAsync(url.ToString()); + } + } + + [RelayCommand] + private void OpenInfoUrl() + { + if (Resource.InfoUrl is { } url) + { + ProcessRunner.OpenUrl(url); + } + } + + public BetterContentDialog GetDialog() + { + return new BetterContentDialog + { + MinDialogWidth = 400, + Title = "Download Model", + Content = new DownloadResourceDialog { DataContext = this }, + PrimaryButtonText = Resources.Action_Continue, + CloseButtonText = Resources.Action_Cancel, + DefaultButton = ContentDialogButton.Primary + }; + } +} diff --git a/StabilityMatrix.Avalonia/ViewModels/Inference/UpscalerCardViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Inference/UpscalerCardViewModel.cs index 65a32ffb..1975fb68 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Inference/UpscalerCardViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Inference/UpscalerCardViewModel.cs @@ -3,10 +3,12 @@ using System.Text.Json.Nodes; using System.Threading.Tasks; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; +using FluentAvalonia.UI.Controls; using StabilityMatrix.Avalonia.Controls; using StabilityMatrix.Avalonia.Models.Inference; using StabilityMatrix.Avalonia.Services; using StabilityMatrix.Avalonia.ViewModels.Base; +using StabilityMatrix.Avalonia.ViewModels.Dialogs; using StabilityMatrix.Core.Attributes; using StabilityMatrix.Core.Extensions; using StabilityMatrix.Core.Helper; @@ -23,6 +25,7 @@ public partial class UpscalerCardViewModel : LoadableViewModelBase private readonly INotificationService notificationService; private readonly ITrackedDownloadService trackedDownloadService; private readonly ISettingsManager settingsManager; + private readonly ServiceManager vmFactory; [ObservableProperty] private double scale = 1; @@ -36,12 +39,14 @@ public partial class UpscalerCardViewModel : LoadableViewModelBase IInferenceClientManager clientManager, INotificationService notificationService, ITrackedDownloadService trackedDownloadService, - ISettingsManager settingsManager + ISettingsManager settingsManager, + ServiceManager vmFactory ) { this.notificationService = notificationService; this.trackedDownloadService = trackedDownloadService; this.settingsManager = settingsManager; + this.vmFactory = vmFactory; ClientManager = clientManager; } @@ -56,6 +61,15 @@ public partial class UpscalerCardViewModel : LoadableViewModelBase resource.ContextType as SharedFolderType? ?? throw new InvalidOperationException("ContextType is not SharedFolderType"); + var confirmDialog = vmFactory.Get(); + confirmDialog.Resource = resource; + confirmDialog.FileName = upscaler.Value.Name; + + if (await confirmDialog.GetDialog().ShowAsync() != ContentDialogResult.Primary) + { + return; + } + var modelsDir = new DirectoryPath(settingsManager.ModelsDirectory).JoinDir( sharedFolderType.GetStringValue() ); @@ -64,6 +78,7 @@ public partial class UpscalerCardViewModel : LoadableViewModelBase resource.Url, modelsDir.JoinFile(upscaler.Value.Name) ); + download.ContextAction = new ModelPostDownloadContextAction(); download.Start(); EventManager.Instance.OnToggleProgressFlyout(); diff --git a/StabilityMatrix.Avalonia/Views/Dialogs/DownloadResourceDialog.axaml b/StabilityMatrix.Avalonia/Views/Dialogs/DownloadResourceDialog.axaml new file mode 100644 index 00000000..910ea333 --- /dev/null +++ b/StabilityMatrix.Avalonia/Views/Dialogs/DownloadResourceDialog.axaml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + diff --git a/StabilityMatrix.Avalonia/Views/Dialogs/DownloadResourceDialog.axaml.cs b/StabilityMatrix.Avalonia/Views/Dialogs/DownloadResourceDialog.axaml.cs new file mode 100644 index 00000000..22d34516 --- /dev/null +++ b/StabilityMatrix.Avalonia/Views/Dialogs/DownloadResourceDialog.axaml.cs @@ -0,0 +1,27 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Markup.Xaml; +using StabilityMatrix.Avalonia.ViewModels.Dialogs; +using StabilityMatrix.Core.Processes; + +namespace StabilityMatrix.Avalonia.Views.Dialogs; + +public partial class DownloadResourceDialog : UserControl +{ + public DownloadResourceDialog() + { + InitializeComponent(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + + private void LicenseButton_OnTapped(object? sender, TappedEventArgs e) + { + var url = ((DownloadResourceViewModel)DataContext!).Resource.LicenseUrl; + ProcessRunner.OpenUrl(url!.ToString()); + } +} diff --git a/StabilityMatrix.Core/Models/IContextAction.cs b/StabilityMatrix.Core/Models/IContextAction.cs index 805531be..7807b21b 100644 --- a/StabilityMatrix.Core/Models/IContextAction.cs +++ b/StabilityMatrix.Core/Models/IContextAction.cs @@ -3,6 +3,7 @@ namespace StabilityMatrix.Core.Models; [JsonDerivedType(typeof(CivitPostDownloadContextAction), "CivitPostDownload")] +[JsonDerivedType(typeof(ModelPostDownloadContextAction), "ModelPostDownload")] public interface IContextAction { object? Context { get; set; } diff --git a/StabilityMatrix.Core/Models/ModelPostDownloadContextAction.cs b/StabilityMatrix.Core/Models/ModelPostDownloadContextAction.cs new file mode 100644 index 00000000..96c898dc --- /dev/null +++ b/StabilityMatrix.Core/Models/ModelPostDownloadContextAction.cs @@ -0,0 +1,17 @@ +using System.Diagnostics.CodeAnalysis; +using StabilityMatrix.Core.Services; + +namespace StabilityMatrix.Core.Models; + +public class ModelPostDownloadContextAction : IContextAction +{ + /// + public object? Context { get; set; } + + [SuppressMessage("Performance", "CA1822:Mark members as static")] + public void Invoke(IModelIndexService modelIndexService) + { + // Request reindex + modelIndexService.BackgroundRefreshIndex(); + } +}