diff --git a/Wabbajack.App.Wpf/App.xaml b/Wabbajack.App.Wpf/App.xaml index fa7f54dc..ce244afa 100644 --- a/Wabbajack.App.Wpf/App.xaml +++ b/Wabbajack.App.Wpf/App.xaml @@ -4,7 +4,8 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:Wabbajack" ShutdownMode="OnExplicitShutdown" - Startup="OnStartup"> + Startup="OnStartup" + Exit="OnExit"> diff --git a/Wabbajack.App.Wpf/App.xaml.cs b/Wabbajack.App.Wpf/App.xaml.cs index c006b8c9..75181335 100644 --- a/Wabbajack.App.Wpf/App.xaml.cs +++ b/Wabbajack.App.Wpf/App.xaml.cs @@ -1,8 +1,14 @@ using System; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; using System.Windows; +using System.Windows.Threading; +using CefSharp.DevTools.Debugger; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using ReactiveUI; +using Splat; using Wabbajack.Common; using Wabbajack; using Wabbajack.Services.OSIntegrated; @@ -16,18 +22,27 @@ namespace Wabbajack public partial class App { private readonly IServiceProvider _serviceProvider; + private readonly IHost _host; + public App() { - var services = new ServiceCollection(); + _host = Host.CreateDefaultBuilder(Array.Empty()) + .ConfigureLogging(c => + { + c.ClearProviders(); + }) + .ConfigureServices((host, services) => + { + ConfigureServices(services); + }) + .Build(); - var host = Host.CreateDefaultBuilder(Array.Empty()) - //.ConfigureLogging(c => { c.ClearProviders(); }) - .ConfigureServices((host, services) => { ConfigureServices(services); }).Build(); - - _serviceProvider = host.Services; + _serviceProvider = _host.Services; } private IServiceCollection ConfigureServices(IServiceCollection services) { + RxApp.MainThreadScheduler = new DispatcherScheduler(Dispatcher.CurrentDispatcher); + services.AddOSIntegrated(); services.AddTransient(); services.AddTransient(); @@ -40,13 +55,25 @@ namespace Wabbajack services.AddTransient(); services.AddTransient(); - return services; } private void OnStartup(object sender, StartupEventArgs e) { - var mainWindow = _serviceProvider.GetRequiredService(); - mainWindow!.Show(); + RxApp.MainThreadScheduler.Schedule(0, (_, _) => + { + var mainWindow = _serviceProvider.GetRequiredService(); + mainWindow!.Show(); + return Disposable.Empty; + }); + } + + private void OnExit(object sender, ExitEventArgs e) + { + using (_host) + { + _host.StopAsync(); + } + base.OnExit(e); } } } diff --git a/Wabbajack.App.Wpf/Extensions/ReactiveUIExt.cs b/Wabbajack.App.Wpf/Extensions/ReactiveUIExt.cs index cdef3164..570c8eb8 100644 --- a/Wabbajack.App.Wpf/Extensions/ReactiveUIExt.cs +++ b/Wabbajack.App.Wpf/Extensions/ReactiveUIExt.cs @@ -6,6 +6,7 @@ using System.Reactive.Disposables; using System.Reactive.Linq; using System.Reactive.Subjects; using System.Threading.Tasks; +using System.Windows.Threading; using DynamicData; using DynamicData.Kernel; using ReactiveUI; diff --git a/Wabbajack.App.Wpf/View Models/Gallery/ModListMetadataVM.cs b/Wabbajack.App.Wpf/View Models/Gallery/ModListMetadataVM.cs index eede9ac1..7ad16509 100644 --- a/Wabbajack.App.Wpf/View Models/Gallery/ModListMetadataVM.cs +++ b/Wabbajack.App.Wpf/View Models/Gallery/ModListMetadataVM.cs @@ -63,6 +63,9 @@ namespace Wabbajack [Reactive] public bool IsBroken { get; private set; } + [Reactive] + public ModListStatus Status { get; set; } + [Reactive] public bool IsDownloading { get; private set; } @@ -99,6 +102,8 @@ namespace Wabbajack _wjClient = wjClient; Location = LauncherUpdater.CommonFolder.Value.Combine("downloaded_mod_lists", Metadata.Links.MachineURL).WithExtension(Ext.Wabbajack); ModListTagList = new List(); + + UpdateStatus().FireAndForget(); Metadata.Tags.ForEach(tag => { @@ -224,6 +229,9 @@ namespace Wabbajack private async Task Download() { + Status = ModListStatus.Downloading; + + using var ll = LoadingLock.WithLoading(); var (progress, task) = _maintainer.DownloadModlist(Metadata); var dispose = progress .BindToStrict(this, vm => vm.ProgressPercent); @@ -231,7 +239,25 @@ namespace Wabbajack await task; await _wjClient.SendMetric("downloading", Metadata.Title); + await UpdateStatus(); dispose.Dispose(); } + + private async Task UpdateStatus() + { + if (await _maintainer.HaveModList(Metadata)) + Status = ModListStatus.Downloaded; + else if (LoadingLock.IsLoading) + Status = ModListStatus.Downloading; + else + Status = ModListStatus.NotDownloaded; + } + + public enum ModListStatus + { + NotDownloaded, + Downloading, + Downloaded + } } } diff --git a/Wabbajack.App.Wpf/Views/ModListTileView.xaml b/Wabbajack.App.Wpf/Views/ModListTileView.xaml index 51e68a6a..a86212d8 100644 --- a/Wabbajack.App.Wpf/Views/ModListTileView.xaml +++ b/Wabbajack.App.Wpf/Views/ModListTileView.xaml @@ -356,51 +356,13 @@ Height="40" Margin="5,0" VerticalAlignment="Center"> - - - + + + diff --git a/Wabbajack.App.Wpf/Views/ModListTileView.xaml.cs b/Wabbajack.App.Wpf/Views/ModListTileView.xaml.cs index fcd2143a..b6ded5e2 100644 --- a/Wabbajack.App.Wpf/Views/ModListTileView.xaml.cs +++ b/Wabbajack.App.Wpf/Views/ModListTileView.xaml.cs @@ -1,6 +1,9 @@ -using System.Reactive.Disposables; +using System; +using System.Reactive.Disposables; using System.Reactive.Linq; using System.Windows; +using System.Windows.Media.Media3D; +using MahApps.Metro.IconPacks; using ReactiveUI; namespace Wabbajack @@ -65,6 +68,26 @@ namespace Wabbajack .BindTo(this, x => x.DownloadProgressBar.Value) .DisposeWith(disposables); + ViewModel.WhenAnyValue(x => x.Status) + .ObserveOnGuiThread() + .Subscribe(x => + { + IconContainer.Children.Clear(); + IconContainer.Children.Add(new PackIconMaterial + { + Width = 20, + Height = 20, + Kind = x switch + { + ModListMetadataVM.ModListStatus.Downloaded => PackIconMaterialKind.Play, + ModListMetadataVM.ModListStatus.Downloading => PackIconMaterialKind.Network, + ModListMetadataVM.ModListStatus.NotDownloaded => PackIconMaterialKind.Download, + _ => throw new ArgumentOutOfRangeException(nameof(x), x, null) + } + }); + }) + .DisposeWith(disposables); + /* this.MarkAsNeeded(this.ViewModel, x => x.IsBroken); this.MarkAsNeeded(this.ViewModel, x => x.Exists);