From c8b383cecc3576cca1b0381e4847a67e3e3186d0 Mon Sep 17 00:00:00 2001 From: Timothy Baldridge Date: Thu, 21 Oct 2021 06:57:02 -0600 Subject: [PATCH 1/2] Work on resource settings page --- Wabbajack.App/Controls/ResourceView.axaml | 16 +++++ Wabbajack.App/Controls/ResourceView.axaml.cs | 32 +++++++++ Wabbajack.App/Controls/ResourceViewModel.cs | 76 ++++++++++++++++++++ Wabbajack.App/Screens/SettingsView.axaml | 21 ++++++ Wabbajack.App/Screens/SettingsView.axaml.cs | 2 + Wabbajack.App/Screens/SettingsViewModel.cs | 10 ++- Wabbajack.RateLimiter/IResource.cs | 3 +- Wabbajack.RateLimiter/Resource.cs | 14 ++-- 8 files changed, 164 insertions(+), 10 deletions(-) create mode 100644 Wabbajack.App/Controls/ResourceView.axaml create mode 100644 Wabbajack.App/Controls/ResourceView.axaml.cs create mode 100644 Wabbajack.App/Controls/ResourceViewModel.cs diff --git a/Wabbajack.App/Controls/ResourceView.axaml b/Wabbajack.App/Controls/ResourceView.axaml new file mode 100644 index 00000000..7a3826a4 --- /dev/null +++ b/Wabbajack.App/Controls/ResourceView.axaml @@ -0,0 +1,16 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/Wabbajack.App/Controls/ResourceView.axaml.cs b/Wabbajack.App/Controls/ResourceView.axaml.cs new file mode 100644 index 00000000..e5cd57ae --- /dev/null +++ b/Wabbajack.App/Controls/ResourceView.axaml.cs @@ -0,0 +1,32 @@ + + +using System.Reactive.Disposables; +using Avalonia.ReactiveUI; +using FluentFTP.Helpers; +using ReactiveUI; + +namespace Wabbajack.App.Controls; + +public partial class ResourceView : ReactiveUserControl, IActivatableView +{ + public ResourceView() + { + InitializeComponent(); + this.WhenActivated(disposables => + { + this.OneWayBind(ViewModel, vm => vm.Name, view => view.ResourceName.Text) + .DisposeWith(disposables); + + this.Bind(ViewModel, vm => vm.MaxTasks, view => view.MaxTasks.Text) + .DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.MaxThroughput, view => view.MaxThroughput.Text) + .DisposeWith(disposables); + + this.OneWayBind(ViewModel, vm => vm.CurrentThroughput, view => view.CurrentThrougput.Text, + val => val.FileSizeToString()) + .DisposeWith(disposables); + + }); + } + +} \ No newline at end of file diff --git a/Wabbajack.App/Controls/ResourceViewModel.cs b/Wabbajack.App/Controls/ResourceViewModel.cs new file mode 100644 index 00000000..f46999f6 --- /dev/null +++ b/Wabbajack.App/Controls/ResourceViewModel.cs @@ -0,0 +1,76 @@ +using System; +using System.Reactive.Disposables; +using System.Reactive.Linq; +using System.Timers;using ReactiveUI; +using ReactiveUI.Fody.Helpers; +using Wabbajack.App.ViewModels; +using Wabbajack.RateLimiter; + +namespace Wabbajack.App.Controls; + +public class ResourceViewModel : ViewModelBase, IActivatableViewModel, IDisposable +{ + private readonly IResource _resource; + private readonly Timer _timer; + + [Reactive] + public int MaxTasks { get; set; } + + [Reactive] + public long MaxThroughput { get; set; } + + [Reactive] + public long CurrentThroughput { get; set; } + + [Reactive] + public string Name { get; set; } + + public ResourceViewModel(IResource resource) + { + Activator = new ViewModelActivator(); + _resource = resource; + _timer = new Timer(1.0); + + Name = resource.Name; + + this.WhenActivated(disposables => + { + _timer.Elapsed += TimerElapsed; + _timer.Start(); + + Disposable.Create(() => + { + _timer.Stop(); + _timer.Elapsed -= TimerElapsed; + }).DisposeWith(disposables); + + this.WhenAnyValue(vm => vm.MaxThroughput) + .Skip(1) + .Subscribe(v => + { + _resource.MaxThroughput = MaxThroughput; + }).DisposeWith(disposables); + + this.WhenAnyValue(vm => vm.MaxTasks) + .Skip(1) + .Subscribe(v => + { + _resource.MaxTasks = MaxTasks; + }).DisposeWith(disposables); + + }); + } + + private void TimerElapsed(object? sender, ElapsedEventArgs e) + { + MaxTasks = _resource.MaxTasks; + MaxThroughput = _resource.MaxThroughput; + CurrentThroughput = _resource.StatusReport.Transferred; + } + + + public void Dispose() + { + _timer.Dispose(); + } +} \ No newline at end of file diff --git a/Wabbajack.App/Screens/SettingsView.axaml b/Wabbajack.App/Screens/SettingsView.axaml index 52651b07..8a3f0167 100644 --- a/Wabbajack.App/Screens/SettingsView.axaml +++ b/Wabbajack.App/Screens/SettingsView.axaml @@ -2,6 +2,7 @@ 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:controls="clr-namespace:Wabbajack.App.Controls" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="Wabbajack.App.Screens.SettingsView"> @@ -26,5 +27,25 @@ + + + + Resource Limits + + + + + + + + + + + + + + + + diff --git a/Wabbajack.App/Screens/SettingsView.axaml.cs b/Wabbajack.App/Screens/SettingsView.axaml.cs index 385a2dd7..66257c94 100644 --- a/Wabbajack.App/Screens/SettingsView.axaml.cs +++ b/Wabbajack.App/Screens/SettingsView.axaml.cs @@ -16,6 +16,8 @@ namespace Wabbajack.App.Screens .DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.NexusLogout, view => view.NexusLogOut) .DisposeWith(disposables); + this.OneWayBind(ViewModel, vm => vm.Resources, view => view.ResourceList.Items) + .DisposeWith(disposables); }); } diff --git a/Wabbajack.App/Screens/SettingsViewModel.cs b/Wabbajack.App/Screens/SettingsViewModel.cs index fc3dbb6d..7c4c8add 100644 --- a/Wabbajack.App/Screens/SettingsViewModel.cs +++ b/Wabbajack.App/Screens/SettingsViewModel.cs @@ -1,14 +1,18 @@ +using System.Collections.Generic; using System.IO; +using System.Linq; using System.Reactive; using System.Reactive.Disposables; using System.Reactive.Linq; using System.Reactive.Subjects; using Microsoft.Extensions.Logging; using ReactiveUI; +using Wabbajack.App.Controls; using Wabbajack.App.Messages; using Wabbajack.App.ViewModels; using Wabbajack.Paths; using Wabbajack.Paths.IO; +using Wabbajack.RateLimiter; using Wabbajack.Services.OSIntegrated.TokenProviders; namespace Wabbajack.App.Screens @@ -23,10 +27,12 @@ namespace Wabbajack.App.Screens public FileSystemWatcher Watcher { get; set; } private readonly Subject _fileSystemEvents = new(); - - public SettingsViewModel(ILogger logger, Configuration configuration, NexusApiTokenProvider nexusProvider) + public readonly IEnumerable Resources; + + public SettingsViewModel(ILogger logger, Configuration configuration, NexusApiTokenProvider nexusProvider, IEnumerable resources) { _logger = logger; + Resources = resources.Select(r => new ResourceViewModel(r)).ToArray(); Activator = new ViewModelActivator(); this.WhenActivated(disposables => diff --git a/Wabbajack.RateLimiter/IResource.cs b/Wabbajack.RateLimiter/IResource.cs index 22475bd3..051e7106 100644 --- a/Wabbajack.RateLimiter/IResource.cs +++ b/Wabbajack.RateLimiter/IResource.cs @@ -7,7 +7,8 @@ namespace Wabbajack.RateLimiter { StatusReport StatusReport { get; } string Name { get; } - + int MaxTasks { get; set; } + long MaxThroughput { get; set; } } public interface IResource : IResource { diff --git a/Wabbajack.RateLimiter/Resource.cs b/Wabbajack.RateLimiter/Resource.cs index e503ebc5..c9b9530d 100644 --- a/Wabbajack.RateLimiter/Resource.cs +++ b/Wabbajack.RateLimiter/Resource.cs @@ -16,8 +16,8 @@ namespace Wabbajack.RateLimiter private readonly ConcurrentDictionary> _tasks; private ulong _nextId = 0; private long _totalUsed = 0; - private readonly int _maxTasks; - private readonly long _maxThroughput; + public int MaxTasks { get; set; } + public long MaxThroughput { get; set; } private readonly string _humanName; public string Name => _humanName; @@ -26,10 +26,10 @@ namespace Wabbajack.RateLimiter public Resource(string? humanName = null, int? maxTasks = 0, long maxThroughput = long.MaxValue) { _humanName = humanName ?? ""; - _maxTasks = maxTasks ?? Environment.ProcessorCount; - _maxThroughput = maxThroughput; + MaxTasks = maxTasks ?? Environment.ProcessorCount; + MaxThroughput = maxThroughput; - _semaphore = new SemaphoreSlim(_maxTasks); + _semaphore = new SemaphoreSlim(MaxTasks); _channel = Channel.CreateBounded(10); _tasks = new (); @@ -44,14 +44,14 @@ namespace Wabbajack.RateLimiter await foreach (var item in _channel.Reader.ReadAllAsync(token)) { Interlocked.Add(ref _totalUsed, item.Size); - if (_maxThroughput == long.MaxValue) + if (MaxThroughput == long.MaxValue) { item.Result.TrySetResult(); sw.Restart(); continue; } - var span = TimeSpan.FromSeconds((double)item.Size / _maxThroughput); + var span = TimeSpan.FromSeconds((double)item.Size / MaxThroughput); await Task.Delay(span, token); From fa8606732c09b599bd204731d5520ef7ab1eb250 Mon Sep 17 00:00:00 2001 From: Timothy Baldridge Date: Thu, 21 Oct 2021 22:28:52 -0600 Subject: [PATCH 2/2] List select views --- .../Controls/InstalledListView.axaml | 15 +++++++ .../Controls/InstalledListView.axaml.cs | 28 +++++++++++++ .../Controls/InstalledListViewModel.cs | 32 +++++++++++++++ Wabbajack.App/Screens/PlaySelectView.axaml | 22 ++++++++++ Wabbajack.App/Screens/PlaySelectView.axaml.cs | 19 +++++++++ Wabbajack.App/Screens/PlaySelectViewModel.cs | 40 +++++++++++++++++++ Wabbajack.App/ServiceExtensions.cs | 6 ++- Wabbajack.App/Views/ModeSelectionView.axaml | 2 +- .../Views/ModeSelectionView.axaml.cs | 5 +++ 9 files changed, 166 insertions(+), 3 deletions(-) create mode 100644 Wabbajack.App/Controls/InstalledListView.axaml create mode 100644 Wabbajack.App/Controls/InstalledListView.axaml.cs create mode 100644 Wabbajack.App/Controls/InstalledListViewModel.cs create mode 100644 Wabbajack.App/Screens/PlaySelectView.axaml create mode 100644 Wabbajack.App/Screens/PlaySelectView.axaml.cs create mode 100644 Wabbajack.App/Screens/PlaySelectViewModel.cs diff --git a/Wabbajack.App/Controls/InstalledListView.axaml b/Wabbajack.App/Controls/InstalledListView.axaml new file mode 100644 index 00000000..14e90f4f --- /dev/null +++ b/Wabbajack.App/Controls/InstalledListView.axaml @@ -0,0 +1,15 @@ + + + + + + + \ No newline at end of file diff --git a/Wabbajack.App/Controls/InstalledListView.axaml.cs b/Wabbajack.App/Controls/InstalledListView.axaml.cs new file mode 100644 index 00000000..778f44da --- /dev/null +++ b/Wabbajack.App/Controls/InstalledListView.axaml.cs @@ -0,0 +1,28 @@ +using Avalonia.Controls.Mixins; +using Avalonia.ReactiveUI; +using ReactiveUI; +using Wabbajack.App.Utilities; + +namespace Wabbajack.App.Controls; + +public partial class InstalledListView : ReactiveUserControl, IActivatableView +{ + public InstalledListView() + { + InitializeComponent(); + + this.WhenActivated(disposables => + { + this.OneWayBind(ViewModel, vm => vm.Name, view => view.Title.Text) + .DisposeWith(disposables); + + this.OneWayBind(ViewModel, vm => vm.InstallPath, view => view.Title.Text, + p => p.ToString()) + .DisposeWith(disposables); + + this.BindCommand(ViewModel, vm => vm.Play, view => view.PlayButton) + .DisposeWith(disposables); + }); + } + +} \ No newline at end of file diff --git a/Wabbajack.App/Controls/InstalledListViewModel.cs b/Wabbajack.App/Controls/InstalledListViewModel.cs new file mode 100644 index 00000000..1b3b43c3 --- /dev/null +++ b/Wabbajack.App/Controls/InstalledListViewModel.cs @@ -0,0 +1,32 @@ +using System.Reactive; +using ReactiveUI; +using ReactiveUI.Fody.Helpers; +using Wabbajack.App.Messages; +using Wabbajack.App.Screens; +using Wabbajack.App.ViewModels; +using Wabbajack.DTOs.SavedSettings; +using Wabbajack.Paths; + +namespace Wabbajack.App.Controls; + +public class InstalledListViewModel : ViewModelBase +{ + private readonly InstallationConfigurationSetting _setting; + public AbsolutePath InstallPath => _setting.Install; + + public string Name => _setting.Metadata?.Title ?? ""; + public ReactiveCommand Play { get; } + + public InstalledListViewModel(InstallationConfigurationSetting setting) + { + Activator = new ViewModelActivator(); + _setting = setting; + + Play = ReactiveCommand.Create(() => + { + MessageBus.Instance.Send(new ConfigureLauncher(InstallPath)); + MessageBus.Instance.Send(new NavigateTo(typeof(LauncherViewModel))); + }); + } + +} \ No newline at end of file diff --git a/Wabbajack.App/Screens/PlaySelectView.axaml b/Wabbajack.App/Screens/PlaySelectView.axaml new file mode 100644 index 00000000..93881c42 --- /dev/null +++ b/Wabbajack.App/Screens/PlaySelectView.axaml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Wabbajack.App/Screens/PlaySelectView.axaml.cs b/Wabbajack.App/Screens/PlaySelectView.axaml.cs new file mode 100644 index 00000000..c35e220a --- /dev/null +++ b/Wabbajack.App/Screens/PlaySelectView.axaml.cs @@ -0,0 +1,19 @@ +using Avalonia.Controls.Mixins; +using ReactiveUI; +using Wabbajack.App.Views; + +namespace Wabbajack.App.Screens; + +public partial class PlaySelectView : ScreenBase +{ + public PlaySelectView() + { + InitializeComponent(); + this.WhenActivated(disposables => + { + this.OneWayBind(ViewModel, vm => vm.Items, view => view.Lists.Items) + .DisposeWith(disposables); + }); + } + +} \ No newline at end of file diff --git a/Wabbajack.App/Screens/PlaySelectViewModel.cs b/Wabbajack.App/Screens/PlaySelectViewModel.cs new file mode 100644 index 00000000..3a3e5d1b --- /dev/null +++ b/Wabbajack.App/Screens/PlaySelectViewModel.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Disposables; +using System.Threading.Tasks; +using ReactiveUI; +using ReactiveUI.Fody.Helpers; +using Wabbajack.App.Controls; +using Wabbajack.App.Models; +using Wabbajack.App.ViewModels; +using Wabbajack.Common; +using Wabbajack.DTOs.SavedSettings; + +namespace Wabbajack.App.Screens; + +public class PlaySelectViewModel : ViewModelBase, IActivatableViewModel +{ + private readonly InstallationStateManager _manager; + + [Reactive] + public IEnumerable Items { get; set; } + + public PlaySelectViewModel(InstallationStateManager manager) + { + _manager = manager; + Activator = new ViewModelActivator(); + + this.WhenActivated(disposables => + { + LoadAndSetItems().FireAndForget(); + Disposable.Empty.DisposeWith(disposables); + }); + } + + public async Task LoadAndSetItems() + { + var items = await _manager.GetAll(); + Items = items.Settings.Select(a => new InstalledListViewModel(a)).ToArray(); + } + +} \ No newline at end of file diff --git a/Wabbajack.App/ServiceExtensions.cs b/Wabbajack.App/ServiceExtensions.cs index 2282272d..8d67e9bb 100644 --- a/Wabbajack.App/ServiceExtensions.cs +++ b/Wabbajack.App/ServiceExtensions.cs @@ -36,11 +36,11 @@ namespace Wabbajack.App services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - services.AddTransient(); - services.AddTransient(); + services.AddTransient(); + services.AddDTOConverters(); services.AddDTOSerializer(); services.AddSingleton(); @@ -55,11 +55,13 @@ namespace Wabbajack.App services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); services.AddAllSingleton(); services.AddAllSingleton(); services.AddAllSingleton(); diff --git a/Wabbajack.App/Views/ModeSelectionView.axaml b/Wabbajack.App/Views/ModeSelectionView.axaml index 7aa76b37..cb00693e 100644 --- a/Wabbajack.App/Views/ModeSelectionView.axaml +++ b/Wabbajack.App/Views/ModeSelectionView.axaml @@ -18,7 +18,7 @@ - + diff --git a/Wabbajack.App/Views/ModeSelectionView.axaml.cs b/Wabbajack.App/Views/ModeSelectionView.axaml.cs index fb045f85..4dff3ffb 100644 --- a/Wabbajack.App/Views/ModeSelectionView.axaml.cs +++ b/Wabbajack.App/Views/ModeSelectionView.axaml.cs @@ -29,6 +29,11 @@ namespace Wabbajack.App.Views { MessageBus.Instance.Send(new NavigateTo(typeof(CompilerConfigurationViewModel))); }).DisposeWith(disposables); + + LaunchButton.Button.Command = ReactiveCommand.Create(() => + { + MessageBus.Instance.Send(new NavigateTo(typeof(PlaySelectViewModel))); + }); }); }