Work on the launch page

This commit is contained in:
Timothy Baldridge 2021-09-29 22:03:43 -06:00
parent 07ec856ee4
commit 6bc1662399
17 changed files with 205 additions and 16 deletions

View File

@ -4,6 +4,7 @@ using System.Linq;
using System.Net.Http;
using System.Reactive;
using System.Reactive.Linq;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Media.Imaging;
@ -17,6 +18,7 @@ using Wabbajack.App.ViewModels;
using Wabbajack.Common;
using Wabbajack.Downloaders;
using Wabbajack.DTOs;
using Wabbajack.DTOs.JsonConverters;
using Wabbajack.Installer;
using Wabbajack.Paths;
using Wabbajack.Paths.IO;
@ -43,6 +45,7 @@ namespace Wabbajack.App.Controls
private readonly DownloadDispatcher _dispatcher;
private readonly ILogger _logger;
private readonly IResource<DownloadDispatcher> _downloadLimiter;
private readonly DTOSerializer _dtos;
public string Title => _metadata.ImageContainsTitle ? "" : _metadata.Title;
public string MachineURL => _metadata.Links.MachineURL;
@ -74,7 +77,7 @@ namespace Wabbajack.App.Controls
public BrowseItemViewModel(ModlistMetadata metadata, ModListSummary summary, HttpClient client, IResource<HttpClient> limiter,
FileHashCache hashCache, Configuration configuration, DownloadDispatcher dispatcher, IResource<DownloadDispatcher> downloadLimiter, GameLocator gameLocator,
ILogger logger)
DTOSerializer dtos, ILogger logger)
{
Activator = new ViewModelActivator();
_metadata = metadata;
@ -86,6 +89,8 @@ namespace Wabbajack.App.Controls
_dispatcher = dispatcher;
_downloadLimiter = downloadLimiter;
_logger = logger;
_dtos = dtos;
var haveGame = gameLocator.IsInstalled(_metadata.Game);
Tags = metadata.tags
.Select(t => new TagViewModel(t, "ModList"))
@ -150,6 +155,9 @@ namespace Wabbajack.App.Controls
_hashCache.FileHashWriteCache(ModListLocation, hash);
var metadataPath = ModListLocation.WithExtension(Ext.MetaData);
await metadataPath.WriteAllTextAsync(_dtos.Serialize(_metadata));
await UpdateState();
}

View File

@ -0,0 +1,9 @@
using Wabbajack.Paths;
namespace Wabbajack.App.Messages
{
public record ConfigureLauncher(AbsolutePath InstallFolder)
{
}
}

View File

@ -3,7 +3,7 @@ using Wabbajack.Paths;
namespace Wabbajack.App.Messages
{
public record StartInstallation(AbsolutePath ModListPath, AbsolutePath Install, AbsolutePath Download)
public record StartInstallation(AbsolutePath ModListPath, AbsolutePath Install, AbsolutePath Download, ModlistMetadata? Metadata)
{
}
}

View File

@ -73,5 +73,10 @@ namespace Wabbajack.App.Models
{
return (await GetAll()).Settings.FirstOrDefault(f => f.ModList == modListPath);
}
public async Task<InstallationConfigurationSetting?> GetByInstallFolder(AbsolutePath folder)
{
return (await GetAll()).Settings.FirstOrDefault(f => f.Install == folder);
}
}
}

View File

@ -24,6 +24,7 @@ using Wabbajack.Networking.WabbajackClientApi;
using DynamicData.Binding;
using Microsoft.Extensions.DependencyInjection;
using Wabbajack.Downloaders;
using Wabbajack.DTOs.JsonConverters;
using Wabbajack.Installer;
using Wabbajack.Paths;
using Wabbajack.Paths.IO;
@ -52,6 +53,7 @@ namespace Wabbajack.App.Screens
private SourceCache<GameSelectorItemViewModel, string> _gamesList = new(x => x.Name);
public readonly ReadOnlyObservableCollection<GameSelectorItemViewModel> _filteredGamesList;
private readonly GameLocator _gameLocator;
private readonly DTOSerializer _dtos;
public ReadOnlyObservableCollection<GameSelectorItemViewModel> GamesList => _filteredGamesList;
[Reactive]
@ -67,7 +69,7 @@ namespace Wabbajack.App.Screens
[Reactive] public bool ShowNSFW { get; set; } = false;
public BrowseViewModel(ILogger<BrowseViewModel> logger, Client wjClient, HttpClient httpClient, IResource<HttpClient> limiter, FileHashCache hashCache,
IResource<DownloadDispatcher> dispatcherLimiter, DownloadDispatcher dispatcher, GameLocator gameLocator, Configuration configuration)
IResource<DownloadDispatcher> dispatcherLimiter, DownloadDispatcher dispatcher, GameLocator gameLocator, DTOSerializer dtos, Configuration configuration)
{
Activator = new ViewModelActivator();
_wjClient = wjClient;
@ -79,6 +81,7 @@ namespace Wabbajack.App.Screens
_dispatcher = dispatcher;
_dispatcherLimiter = dispatcherLimiter;
_gameLocator = gameLocator;
_dtos = dtos;
IObservable<Func<BrowseItemViewModel, bool>> searchTextPredicates = this.ObservableForProperty(vm => vm.SearchText)
@ -198,7 +201,7 @@ namespace Wabbajack.App.Screens
summary = new ModListSummary();
}
return new BrowseItemViewModel(m, summary, _httpClient, _limiter, _hashCache, _configuration, _dispatcher, _dispatcherLimiter, _gameLocator, _logger);
return new BrowseItemViewModel(m, summary, _httpClient, _limiter, _hashCache, _configuration, _dispatcher, _dispatcherLimiter, _gameLocator, _dtos, _logger);
});
_modLists.Edit(lsts =>

View File

@ -0,0 +1,15 @@
<UserControl xmlns="https://github.com/avaloniaui"
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"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Wabbajack.App.Screens.LauncherScreen">
<Grid RowDefinitions="40, *, Auto">
<TextBlock Grid.Row="0" x:Name="StatusText" FontSize="20" FontWeight="Bold">[20/30] Installing Files</TextBlock>
<Viewbox Grid.Row="1" HorizontalAlignment="Center"
VerticalAlignment="Center"
Stretch="Uniform">
<Image x:Name="SlideImage"></Image>
</Viewbox>
</Grid>
</UserControl>

View File

@ -0,0 +1,23 @@
using System.Reactive.Disposables;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using ReactiveUI;
using Wabbajack.App.Views;
namespace Wabbajack.App.Screens
{
public partial class LauncherScreen : ScreenBase<LauncherViewModel>
{
public LauncherScreen()
{
InitializeComponent();
this.WhenActivated(disposables =>
{
this.OneWayBind(ViewModel, vm => vm.Image, view => view.SlideImage.Source)
.DisposeWith(disposables);
});
}
}
}

View File

@ -0,0 +1,52 @@
using System.Reactive.Disposables;
using System.Reactive.Linq;
using Avalonia.Media.Imaging;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using Wabbajack.App.Extensions;
using Wabbajack.App.Messages;
using Wabbajack.App.Models;
using Wabbajack.App.ViewModels;
using Wabbajack.DTOs.SavedSettings;
using Wabbajack.Paths;
namespace Wabbajack.App.Screens
{
public class LauncherViewModel : ViewModelBase, IActivatableViewModel, IReceiver<ConfigureLauncher>
{
[Reactive]
public AbsolutePath InstallFolder { get; set; }
[Reactive]
public IBitmap Image { get; set; }
[Reactive]
public InstallationConfigurationSetting? Setting { get; set; }
public LauncherViewModel(InstallationStateManager manager)
{
Activator = new ViewModelActivator();
this.WhenActivated(disposables =>
{
this.WhenAnyValue(v => v.InstallFolder)
.SelectAsync(disposables, async folder => await manager.GetByInstallFolder(folder))
.Where(v => v != null)
.BindTo(this, vm => vm.Setting)
.DisposeWith(disposables);
this.WhenAnyValue(v => v.Setting)
.Where(v => v != default)
.Select(v => new Bitmap((v!.Image).ToString()))
.BindTo(this, vm => vm.Image)
.DisposeWith(disposables);
});
}
public void Receive(ConfigureLauncher val)
{
InstallFolder = val.InstallFolder;
}
}
}

View File

@ -12,7 +12,7 @@
<Viewbox Grid.Row="3" HorizontalAlignment="Center"
VerticalAlignment="Center"
Stretch="Uniform">
<Image x:Name="SlideImage" Source="C:\tmp\modlist-image.png"></Image>
<Image x:Name="SlideImage"></Image>
</Viewbox>
<Grid Grid.Row="4" HorizontalAlignment="Center" ColumnDefinitions="40, 40, 40, 40">
<Button Grid.Column="0" x:Name="PrevSlide"><i:MaterialIcon Kind="ArrowLeft"></i:MaterialIcon></Button>

View File

@ -1,4 +1,5 @@
using System;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Reactive;
@ -13,12 +14,17 @@ using Microsoft.Extensions.Logging;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using Wabbajack.App.Messages;
using Wabbajack.App.Models;
using Wabbajack.App.Screens;
using Wabbajack.App.Utilities;
using Wabbajack.App.ViewModels.SubViewModels;
using Wabbajack.Common;
using Wabbajack.DTOs;
using Wabbajack.DTOs.DownloadStates;
using Wabbajack.DTOs.JsonConverters;
using Wabbajack.DTOs.SavedSettings;
using Wabbajack.Installer;
using Wabbajack.Paths.IO;
using Wabbajack.RateLimiter;
namespace Wabbajack.App.ViewModels
@ -36,6 +42,7 @@ namespace Wabbajack.App.ViewModels
private readonly HttpClient _httpClient;
private Timer _slideTimer;
private int _currentSlideIndex;
private readonly InstallationStateManager _installStateManager;
[Reactive]
public SlideViewModel Slide { get; set; }
@ -58,13 +65,15 @@ namespace Wabbajack.App.ViewModels
[Reactive] public Percent StepsProgress { get; set; } = Percent.Zero;
[Reactive] public Percent StepProgress { get; set; } = Percent.Zero;
public StandardInstallationViewModel(ILogger<StandardInstallationViewModel> logger, IServiceProvider provider, GameLocator locator, DTOSerializer dtos, HttpClient httpClient)
public StandardInstallationViewModel(ILogger<StandardInstallationViewModel> logger, IServiceProvider provider, GameLocator locator, DTOSerializer dtos,
HttpClient httpClient, InstallationStateManager manager)
{
_provider = provider;
_locator = locator;
_logger = logger;
_dtos = dtos;
_httpClient = httpClient;
_installStateManager = manager;
Activator = new ViewModelActivator();
this.WhenActivated(disposables => {
@ -142,6 +151,7 @@ namespace Wabbajack.App.ViewModels
_config.Downloads = msg.Download;
_config.Install = msg.Install;
_config.ModlistArchive = msg.ModListPath;
_config.Metadata = msg.Metadata;
_logger.LogInformation("Loading ModList Data");
_config.ModList = await StandardInstaller.LoadFromFile(_dtos, msg.ModListPath);
@ -180,7 +190,34 @@ namespace Wabbajack.App.ViewModels
};
_logger.LogInformation("Installer created, starting the installation process");
await _installer.Begin(CancellationToken.None);
var result = await _installer.Begin(CancellationToken.None);
if (result)
{
await SaveConfigAndContinue(_config);
}
}
private async Task SaveConfigAndContinue(InstallerConfiguration config)
{
var path = config.Install.Combine("modlist-image.png");
{
var image = await ModListUtilities.GetModListImageStream(config.ModlistArchive);
await using var os = path.Open(FileMode.Create, FileAccess.Write);
await image.CopyToAsync(os);
}
await _installStateManager.SetLastState(new InstallationConfigurationSetting
{
Downloads = config.Downloads,
Install = config.Install,
Metadata = config.Metadata,
ModList = config.ModlistArchive,
Image = path
});
MessageBus.Instance.Send(new ConfigureLauncher(config.Install));
MessageBus.Instance.Send(new NavigateTo(typeof(LauncherViewModel)));
}
}
}

View File

@ -0,0 +1,21 @@
using System.IO;
using System.IO.Compression;
using System.Threading.Tasks;
using Wabbajack.Common;
using Wabbajack.Paths;
using Wabbajack.Paths.IO;
namespace Wabbajack.App.Utilities
{
public class ModListUtilities
{
public static async Task<MemoryStream> GetModListImageStream(AbsolutePath modList)
{
await using var fs = modList.Open(FileMode.Open, FileAccess.Read, FileShare.Read);
using var ar = new ZipArchive(fs, ZipArchiveMode.Read);
var entry = ar.GetEntry("modlist-image.png");
await using var stream = entry!.Open();
return new MemoryStream(await stream.ReadAllAsync());
}
}
}

View File

@ -12,6 +12,7 @@ using ReactiveUI.Validation.Extensions;
using Wabbajack.App.Extensions;
using Wabbajack.App.Messages;
using Wabbajack.App.Models;
using Wabbajack.App.Utilities;
using Wabbajack.Common;
using Wabbajack.DTOs;
using Wabbajack.DTOs.JsonConverters;
@ -63,7 +64,7 @@ namespace Wabbajack.App.ViewModels
this.ValidationRule(x => x.Install, p => p.DirectoryExists(), "Install folder file must exist");
this.ValidationRule(x => x.Download, p => p != default, "Download folder must be set");
BeginCommand = ReactiveCommand.Create(StartInstall, this.IsValid());
BeginCommand = ReactiveCommand.Create(() => {StartInstall().FireAndForget();}, this.IsValid());
this.WhenAnyValue(t => t.ModListPath)
@ -97,26 +98,30 @@ namespace Wabbajack.App.ViewModels
}
private void StartInstall()
private async Task StartInstall()
{
ModlistMetadata? metadata = null;
var metadataPath = ModListPath.WithExtension(Ext.MetaData);
if (metadataPath.FileExists())
{
metadata = _dtos.Deserialize<ModlistMetadata>(await metadataPath.ReadAllTextAsync());
}
_stateManager.SetLastState(new InstallationConfigurationSetting
{
ModList = ModListPath,
Downloads = Download,
Install = Install
Install = Install,
Metadata = metadata
}).FireAndForget();
MessageBus.Instance.Send(new NavigateTo(typeof(StandardInstallationViewModel)));
MessageBus.Instance.Send(new StartInstallation(ModListPath, Install, Download));
MessageBus.Instance.Send(new StartInstallation(ModListPath, Install, Download, metadata));
}
private async Task<IBitmap> LoadModListImage(AbsolutePath path)
{
await using var fs = path.Open(FileMode.Open, FileAccess.Read, FileShare.Read);
using var ar = new ZipArchive(fs, ZipArchiveMode.Read);
var entry = ar.GetEntry("modlist-image.png");
await using var stream = entry!.Open();
return new Bitmap(new MemoryStream(await stream.ReadAllAsync()));
return new Bitmap(await ModListUtilities.GetModListImageStream(path));
}
private async Task<ModList> LoadModList(AbsolutePath modlist)

View File

@ -41,6 +41,10 @@
<DependentUpon>SettingsView.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Update="Screens\StandardInstallationView.axaml.cs">
<DependentUpon>StandardInstallationView.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<Target Name="AferBuild" AfterTargets="Build">

View File

@ -19,5 +19,6 @@ namespace Wabbajack.Common
public static Extension Dds = new(".dds");
public static Extension Json = new(".json");
public static Extension Md = new(".md");
public static Extension MetaData = new(".metadata");
}
}

View File

@ -16,5 +16,9 @@ namespace Wabbajack.DTOs.SavedSettings
public AbsolutePath ModList { get; set; }
public AbsolutePath Install { get; set; }
public AbsolutePath Downloads { get; set; }
public ModlistMetadata? Metadata { get; set; }
public AbsolutePath Image { get; set; }
}
}

View File

@ -12,5 +12,7 @@ namespace Wabbajack.Installer
public SystemParameters? SystemParameters { get; set; }
public Game Game { get; set; }
public AbsolutePath GameFolder { get; set; }
public ModlistMetadata? Metadata { get; set; }
}
}