Several UI fixes

This commit is contained in:
Timothy Baldridge 2021-11-02 23:03:41 -06:00
parent 5d1bc5ff3b
commit 295b629169
9 changed files with 59 additions and 31 deletions

View File

@ -48,11 +48,13 @@ public class SettingsManager
try
{
if (path.FileExists())
{
await using (var s = path.Open(FileMode.Open))
{
return (await JsonSerializer.DeserializeAsync<T>(s, _dtos.Options))!;
}
}
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Loading settings {Key}", key);

View File

@ -20,6 +20,7 @@ using Wabbajack.App.Utilities;
using Wabbajack.App.ViewModels.SubViewModels;
using Wabbajack.Common;
using Wabbajack.Downloaders.GameFile;
using Wabbajack.Downloaders.Interfaces;
using Wabbajack.DTOs;
using Wabbajack.DTOs.DownloadStates;
using Wabbajack.DTOs.JsonConverters;
@ -64,7 +65,7 @@ public class StandardInstallationViewModel : ViewModelBase
this.WhenActivated(disposables =>
{
_updateTimer = new Timer(UpdateStatus, null, TimeSpan.FromMilliseconds(1), TimeSpan.FromMilliseconds(250));
_updateTimer = new Timer(UpdateStatus, null, TimeSpan.FromMilliseconds(1), TimeSpan.FromMilliseconds(100));
_updateTimer.DisposeWith(disposables);
_slideTimer = new Timer(_ =>
@ -108,7 +109,7 @@ public class StandardInstallationViewModel : ViewModelBase
[Reactive] public Percent StepProgress { get; set; } = Percent.Zero;
// Not Reactive, so we don't end up spamming the UI threads with events
public StatusUpdate _latestStatus { get; set; } = new("", Percent.Zero, Percent.Zero);
public StatusUpdate _latestStatus = new("", Percent.Zero, Percent.Zero);
public void Receive(StartInstallation msg)
{
@ -117,11 +118,12 @@ public class StandardInstallationViewModel : ViewModelBase
private void UpdateStatus(object? state)
{
Dispatcher.UIThread.Post(() => {
Dispatcher.UIThread.Post(() =>
{
StepsProgress = _latestStatus.StepsProgress;
StepProgress = _latestStatus.StepProgress;
StatusText = _latestStatus.StatusText;
});
}, DispatcherPriority.Render);
}
private void NextSlide(int direction)

View File

@ -27,11 +27,13 @@ public class InstallConfigurationViewModel : ViewModelBase, IActivatableViewMode
{
private readonly DTOSerializer _dtos;
private readonly InstallationStateManager _stateManager;
private readonly SettingsManager _settingsManager;
public InstallConfigurationViewModel(DTOSerializer dtos, InstallationStateManager stateManager)
public InstallConfigurationViewModel(DTOSerializer dtos, InstallationStateManager stateManager, SettingsManager settingsManager)
{
_stateManager = stateManager;
_settingsManager = settingsManager;
_dtos = dtos;
Activator = new ViewModelActivator();
@ -76,9 +78,20 @@ public class InstallConfigurationViewModel : ViewModelBase, IActivatableViewMode
settings.Select(s => s!.Downloads)
.BindTo(this, vm => vm.Download)
.DisposeWith(disposables);
LoadSettings().FireAndForget();
});
}
private async Task LoadSettings()
{
var path = await _settingsManager.Load<AbsolutePath>("last-install-path");
if (path != default && path.FileExists())
ModListPath = path;
}
[Reactive] public AbsolutePath ModListPath { get; set; }
[Reactive] public AbsolutePath Install { get; set; }
@ -107,13 +120,15 @@ public class InstallConfigurationViewModel : ViewModelBase, IActivatableViewMode
if (metadataPath.FileExists())
metadata = _dtos.Deserialize<ModlistMetadata>(await metadataPath.ReadAllTextAsync());
_stateManager.SetLastState(new InstallationConfigurationSetting
await _stateManager.SetLastState(new InstallationConfigurationSetting
{
ModList = ModListPath,
Downloads = Download,
Install = Install,
Metadata = metadata
}).FireAndForget();
});
await _settingsManager.Save("last-install-path", ModListPath);
MessageBus.Current.SendMessage(new NavigateTo(typeof(StandardInstallationViewModel)));
MessageBus.Current.SendMessage(new StartInstallation(ModListPath, Install, Download, metadata));

View File

@ -59,8 +59,8 @@ public class MainWindowViewModel : ReactiveValidationObject, IActivatableViewMod
this.WhenActivated(disposables =>
{
BackButton = ReactiveCommand.Create(() => { Receive(new NavigateBack()); },
this.ObservableForProperty(vm => vm.BreadCrumbs)
.Select(bc => bc.Value.Count() > 1))
this.WhenAnyValue(vm => vm.BreadCrumbs)
.Select(bc => bc.Count() > 1))
.DisposeWith(disposables);
SettingsButton = ReactiveCommand.Create(() => { Receive(new NavigateTo(typeof(SettingsViewModel))); })

View File

@ -321,6 +321,6 @@ public class FileExtractor
await using var stream = await factory.GetStream();
await abs.WriteAllAsync(stream, token);
return 0;
}, token);
}, token, progressFunction: updateProgress);
}
}

View File

@ -87,7 +87,7 @@ public abstract class AInstaller<T>
public ModList ModList => _configuration.ModList;
public async Task NextStep(string statusText, long maxStepProgress)
public void NextStep(string statusText, long maxStepProgress)
{
_updateStopWatch.Restart();
MaxStepProgress = maxStepProgress;
@ -109,11 +109,20 @@ public abstract class AInstaller<T>
public abstract Task<bool> Begin(CancellationToken token);
public async Task ExtractModlist(CancellationToken token)
protected async Task ExtractModlist(CancellationToken token)
{
await NextStep("Extracting Modlist", 100);
ExtractedModlistFolder = _manager.CreateFolder();
await _extractor.ExtractAll(_configuration.ModlistArchive, ExtractedModlistFolder, token, updateProgress: p => UpdateProgress((long)(p.Value * 100)));
await using var stream = _configuration.ModlistArchive.Open(FileMode.Open, FileAccess.Read, FileShare.Read);
using var archive = new ZipArchive(stream, ZipArchiveMode.Read);
NextStep("Extracting Modlist", archive.Entries.Count);
foreach (var entry in archive.Entries)
{
var path = entry.FullName.ToRelativePath().RelativeTo(ExtractedModlistFolder);
path.Parent.CreateDirectory();
await using var of = path.Open(FileMode.Create, FileAccess.Write, FileShare.None);
await entry.Open().CopyToAsync(of, token);
UpdateProgress(1);
}
}
public async Task<byte[]> LoadBytesFromPath(RelativePath path)
@ -157,7 +166,7 @@ public abstract class AInstaller<T>
/// </summary>
protected async Task PrimeVFS()
{
await NextStep("Priming VFS", 0);
NextStep("Priming VFS", 0);
_vfs.AddKnown(_configuration.ModList.Directives.OfType<FromArchive>().Select(d => d.ArchiveHashPath),
HashedArchives);
await _vfs.BackfillMissing();
@ -165,7 +174,7 @@ public abstract class AInstaller<T>
public async Task BuildFolderStructure()
{
await NextStep("Building Folder Structure", 0);
NextStep("Building Folder Structure", 0);
_logger.LogInformation("Building Folder Structure");
ModList.Directives
.Where(d => d.To.Depth > 1)
@ -176,7 +185,7 @@ public abstract class AInstaller<T>
public async Task InstallArchives(CancellationToken token)
{
await NextStep("Installing files", ModList.Directives.Sum(d => d.Size));
NextStep("Installing files", ModList.Directives.Sum(d => d.Size));
var grouped = ModList.Directives
.OfType<FromArchive>()
.Select(a => new {VF = _vfs.Index.FileForArchiveHashPath(a.ArchiveHashPath), Directive = a})
@ -278,7 +287,7 @@ public abstract class AInstaller<T>
}
_logger.LogInformation("Downloading {count} archives", missing.Count);
await NextStep("Downloading files", missing.Count);
NextStep("Downloading files", missing.Count);
await missing
.OrderBy(a => a.Size)
@ -339,7 +348,7 @@ public abstract class AInstaller<T>
public async Task HashArchives(CancellationToken token)
{
await NextStep("Hashing Archives", 0);
NextStep("Hashing Archives", 0);
_logger.LogInformation("Looking for files to hash");
var allFiles = _configuration.Downloads.EnumerateFiles()
@ -390,7 +399,7 @@ public abstract class AInstaller<T>
var savePath = (RelativePath) "saves";
var existingFiles = _configuration.Install.EnumerateFiles().ToList();
await NextStep("Optimizing Modlist: Looking for files to delete", existingFiles.Count);
NextStep("Optimizing Modlist: Looking for files to delete", existingFiles.Count);
await existingFiles
.PDoAll(async f =>
{
@ -409,7 +418,7 @@ public abstract class AInstaller<T>
});
_logger.LogInformation("Cleaning empty folders");
await NextStep("Optimizing Modlist: Cleaning empty folders", indexed.Keys.Count);
NextStep("Optimizing Modlist: Cleaning empty folders", indexed.Keys.Count);
var expectedFolders = (indexed.Keys
.Select(f => f.RelativeTo(_configuration.Install))
// We ignore the last part of the path, so we need a dummy file name
@ -444,10 +453,9 @@ public abstract class AInstaller<T>
var existingfiles = _configuration.Install.EnumerateFiles().ToHashSet();
await NextStep("Optimizing Modlist: Removing redundant directives", indexed.Count);
NextStep("Optimizing Modlist: Removing redundant directives", indexed.Count);
await indexed.Values.PMapAll<Directive, Directive?>(async d =>
{
UpdateProgress(1);
// Bit backwards, but we want to return null for
// all files we *want* installed. We return the files
// to remove from the install list.
@ -458,12 +466,13 @@ public abstract class AInstaller<T>
})
.Do(d =>
{
UpdateProgress(1);
if (d != null) indexed.Remove(d.To);
});
_logger.LogInformation("Optimized {optimized} directives to {indexed} required", ModList.Directives.Length,
indexed.Count);
await NextStep("Finalizing modlist optimization", 0);
NextStep("Finalizing modlist optimization", 0);
var requiredArchives = indexed.Values.OfType<FromArchive>()
.GroupBy(d => d.ArchiveHashPath.Hash)
.Select(d => d.Key)

View File

@ -38,14 +38,14 @@ public class StandardInstaller : AInstaller<StandardInstaller>
base(logger, config, gameLocator, extractor, jsonSerializer, vfs, fileHashCache, downloadDispatcher,
parallelOptions, wjClient)
{
MaxSteps = 13;
MaxSteps = 14;
}
public override async Task<bool> Begin(CancellationToken token)
{
if (token.IsCancellationRequested) return false;
await _wjClient.SendMetric(MetricNames.BeginInstall, ModList.Name);
await NextStep("Configuring Installer", 0);
NextStep("Configuring Installer", 0);
_logger.LogInformation("Configuring Processor");
if (_configuration.GameFolder == default)
@ -106,7 +106,7 @@ public class StandardInstaller : AInstaller<StandardInstaller>
await PrimeVFS();
BuildFolderStructure();
await BuildFolderStructure();
await InstallArchives(token);
@ -129,7 +129,7 @@ public class StandardInstaller : AInstaller<StandardInstaller>
await ExtractedModlistFolder!.DisposeAsync();
await _wjClient.SendMetric(MetricNames.FinishInstall, ModList.Name);
await NextStep("Finished", 1);
NextStep("Finished", 1);
_logger.LogInformation("Finished Installation");
return true;
}
@ -259,7 +259,7 @@ public class StandardInstaller : AInstaller<StandardInstaller>
private async Task InstallIncludedFiles(CancellationToken token)
{
_logger.LogInformation("Writing inline files");
await NextStep("Installing Included Files", ModList.Directives.OfType<InlineFile>().Count());
NextStep("Installing Included Files", ModList.Directives.OfType<InlineFile>().Count());
await ModList.Directives
.OfType<InlineFile>()
.PDoAll(async directive =>

View File

@ -30,7 +30,7 @@ public readonly struct Percent : IComparable, IEquatable<Percent>
public static bool InRange(double d)
{
return d >= 0 || d <= 1;
return d is >= 0 or <= 1;
}
public static Percent operator +(Percent c1, Percent c2)

View File

@ -172,7 +172,7 @@ public class Context
_knownArchives.TryAdd(key, value);
}
public async Task BackfillMissing()
public async ValueTask BackfillMissing()
{
var newFiles = _knownArchives.ToDictionary(kv => kv.Key,
kv => new VirtualFile