mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
Implement installer UI error messaging
This commit is contained in:
parent
3edf568ef7
commit
165760e082
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@ -85,6 +86,12 @@ public class InstallerVM : BackNavigatingVM, IBackNavigatingVM, ICpuStatusVM
|
||||
[Reactive]
|
||||
public InstallState InstallState { get; set; }
|
||||
|
||||
[Reactive]
|
||||
protected ErrorResponse[] Errors { get; private set; }
|
||||
|
||||
[Reactive]
|
||||
public ErrorResponse Error { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Slideshow Data
|
||||
/// </summary>
|
||||
@ -114,6 +121,9 @@ public class InstallerVM : BackNavigatingVM, IBackNavigatingVM, ICpuStatusVM
|
||||
[Reactive]
|
||||
public bool Installing { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public ErrorResponse ErrorState { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool ShowNSFWSlides { get; set; }
|
||||
|
||||
@ -221,10 +231,45 @@ public class InstallerVM : BackNavigatingVM, IBackNavigatingVM, ICpuStatusVM
|
||||
BeginSlideShow(token.Token).FireAndForget();
|
||||
Disposable.Create(() => token.Cancel())
|
||||
.DisposeWith(disposables);
|
||||
|
||||
this.WhenAny(vm => vm.ModListLocation.ErrorState)
|
||||
.CombineLatest(this.WhenAny(vm => vm.Installer.DownloadLocation.ErrorState),
|
||||
this.WhenAny(vm => vm.Installer.Location.ErrorState),
|
||||
this.WhenAny(vm => vm.ModListLocation.TargetPath),
|
||||
this.WhenAny(vm => vm.Installer.Location.TargetPath),
|
||||
this.WhenAny(vm => vm.Installer.DownloadLocation.TargetPath))
|
||||
.Select(t =>
|
||||
{
|
||||
var errors = new[] {t.First, t.Second, t.Third}
|
||||
.Where(t => t.Failed)
|
||||
.Concat(Validate())
|
||||
.ToArray();
|
||||
if (!errors.Any()) return ErrorResponse.Success;
|
||||
return ErrorResponse.Fail(string.Join("\n", errors.Select(e => e.Reason)));
|
||||
})
|
||||
.BindTo(this, vm => vm.ErrorState)
|
||||
.DisposeWith(disposables);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private IEnumerable<ErrorResponse> Validate()
|
||||
{
|
||||
if (!ModListLocation.TargetPath.FileExists())
|
||||
yield return ErrorResponse.Fail("Mod list source does not exist");
|
||||
|
||||
var downloadPath = Installer.DownloadLocation.TargetPath;
|
||||
if (downloadPath.Depth <= 1)
|
||||
yield return ErrorResponse.Fail("Download path isn't set to a folder");
|
||||
|
||||
var installPath = Installer.Location.TargetPath;
|
||||
if (installPath.Depth <= 1)
|
||||
yield return ErrorResponse.Fail("Install path isn't set to a folder");
|
||||
if (installPath.InFolder(KnownFolders.Windows))
|
||||
yield return ErrorResponse.Fail("Don't install modlists into your Windows folder");
|
||||
}
|
||||
|
||||
|
||||
private async Task BeginSlideShow(CancellationToken token)
|
||||
{
|
||||
while (!token.IsCancellationRequested)
|
||||
|
@ -39,19 +39,27 @@ namespace Wabbajack
|
||||
this.WhenAny(x => x.ViewModel.BeginCommand)
|
||||
.BindToStrict(this, x => x.BeginButton.Command)
|
||||
.DisposeWith(dispose);
|
||||
|
||||
// Error icon display
|
||||
var vis = this.WhenAny(x => x.ViewModel.Installer.CanInstall)
|
||||
.Select(err => err.Failed ? Visibility.Visible : Visibility.Hidden)
|
||||
.Replay(1)
|
||||
.RefCount();
|
||||
vis.BindToStrict(this, x => x.ErrorSummaryIconGlow.Visibility)
|
||||
|
||||
// Error handling
|
||||
|
||||
this.WhenAnyValue(x => x.ViewModel.ErrorState)
|
||||
.Select(v => !v.Failed)
|
||||
.BindToStrict(this, view => view.BeginButton.IsEnabled)
|
||||
.DisposeWith(dispose);
|
||||
vis.BindToStrict(this, x => x.ErrorSummaryIcon.Visibility)
|
||||
|
||||
this.WhenAnyValue(x => x.ViewModel.ErrorState)
|
||||
.Select(v => v.Failed ? Visibility.Visible : Visibility.Hidden)
|
||||
.BindToStrict(this, view => view.ErrorSummaryIcon.Visibility)
|
||||
.DisposeWith(dispose);
|
||||
this.WhenAny(x => x.ViewModel.Installer.CanInstall)
|
||||
.Select(x => x.Reason)
|
||||
.BindToStrict(this, x => x.ErrorSummaryIcon.ToolTip)
|
||||
|
||||
this.WhenAnyValue(x => x.ViewModel.ErrorState)
|
||||
.Select(v => v.Failed ? Visibility.Visible : Visibility.Hidden)
|
||||
.BindToStrict(this, view => view.ErrorSummaryIconGlow.Visibility)
|
||||
.DisposeWith(dispose);
|
||||
|
||||
this.WhenAnyValue(x => x.ViewModel.ErrorState)
|
||||
.Select(v => v.Reason)
|
||||
.BindToStrict(this, view => view.ErrorSummaryIcon.ToolTip)
|
||||
.DisposeWith(dispose);
|
||||
});
|
||||
}
|
||||
|
@ -78,6 +78,7 @@ namespace Wabbajack
|
||||
.BindToStrict(this, view => view.TopProgressBar.ProgressPercent)
|
||||
.DisposeWith(disposables);
|
||||
|
||||
|
||||
// Slideshow
|
||||
ViewModel.WhenAnyValue(vm => vm.SlideShowTitle)
|
||||
.Select(f => f)
|
||||
|
@ -40,4 +40,6 @@ public class NativeFileStreamFactory : IStreamFactory
|
||||
}
|
||||
|
||||
public IPath Name { get; }
|
||||
|
||||
public AbsolutePath FullPath => (AbsolutePath) Name;
|
||||
}
|
@ -3,9 +3,13 @@ using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.DTOs.Streams;
|
||||
using Wabbajack.DTOs.Vfs;
|
||||
using Wabbajack.Hashing.xxHash64;
|
||||
using Wabbajack.Networking.Http;
|
||||
using Wabbajack.Paths;
|
||||
using Wabbajack.Paths.IO;
|
||||
using Wabbajack.VFS.Interfaces;
|
||||
|
||||
namespace Wabbajack.Networking.WabbajackClientApi;
|
||||
@ -14,6 +18,7 @@ public class CesiVFSCache : IVfsCache
|
||||
{
|
||||
private readonly Client _client;
|
||||
private readonly ILogger<CesiVFSCache> _logger;
|
||||
private const int Threshold = 1024 * 1024 * 128;
|
||||
|
||||
public CesiVFSCache(ILogger<CesiVFSCache> logger, Client client)
|
||||
{
|
||||
@ -21,8 +26,12 @@ public class CesiVFSCache : IVfsCache
|
||||
_client = client;
|
||||
}
|
||||
|
||||
public async Task<IndexedVirtualFile?> Get(Hash hash, CancellationToken token)
|
||||
public async Task<IndexedVirtualFile?> Get(Hash hash, IStreamFactory sf, CancellationToken token)
|
||||
{
|
||||
if (sf is not NativeFileStreamFactory nf)
|
||||
return null;
|
||||
if (nf.FullPath.Size() < Threshold) return null;
|
||||
|
||||
try
|
||||
{
|
||||
var result = await _client.GetCesiVfsEntry(hash, token);
|
||||
|
@ -281,6 +281,7 @@ public static class AbsolutePathExtensions
|
||||
|
||||
public static IEnumerable<AbsolutePath> EnumerateDirectories(this AbsolutePath path, bool recursive = true)
|
||||
{
|
||||
if (!path.DirectoryExists()) return Array.Empty<AbsolutePath>();
|
||||
return Directory.EnumerateDirectories(path.ToString(), "*",
|
||||
recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly)
|
||||
.Select(p => (AbsolutePath) p);
|
||||
|
@ -67,7 +67,7 @@ public struct AbsolutePath : IPath, IComparable<AbsolutePath>, IEquatable<Absolu
|
||||
}
|
||||
}
|
||||
|
||||
public int Depth => Parts.Length;
|
||||
public int Depth => Parts?.Length ?? 0;
|
||||
|
||||
public AbsolutePath ReplaceExtension(Extension newExtension)
|
||||
{
|
||||
|
@ -6,8 +6,8 @@ public static class ArrayExtensions
|
||||
{
|
||||
public static bool AreEqual<T>(T[] a, int startA, T[] b, int startB, int length)
|
||||
{
|
||||
if (startA + length > a.Length) return false;
|
||||
if (startB + length > b.Length) return false;
|
||||
if (startA + length > (a?.Length ?? 0)) return false;
|
||||
if (startB + length > (b?.Length ?? 0)) return false;
|
||||
|
||||
for (var i = 0; i < length; i++)
|
||||
if (!a[startA + i]!.Equals(b[startB + i]))
|
||||
|
@ -1,10 +1,12 @@
|
||||
using Wabbajack.DTOs.Vfs;
|
||||
using Wabbajack.DTOs.Streams;
|
||||
using Wabbajack.DTOs.Vfs;
|
||||
using Wabbajack.Hashing.xxHash64;
|
||||
using Wabbajack.Paths;
|
||||
|
||||
namespace Wabbajack.VFS.Interfaces;
|
||||
|
||||
public interface IVfsCache
|
||||
{
|
||||
public Task<IndexedVirtualFile?> Get(Hash hash, CancellationToken token);
|
||||
public Task<IndexedVirtualFile?> Get(Hash hash, IStreamFactory sf, CancellationToken token);
|
||||
public Task Put(IndexedVirtualFile file, CancellationToken token);
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Wabbajack.DTOs.Streams;
|
||||
using Wabbajack.DTOs.Vfs;
|
||||
using Wabbajack.Hashing.xxHash64;
|
||||
using Wabbajack.VFS.Interfaces;
|
||||
@ -15,14 +16,14 @@ public class FallthroughVFSCache : IVfsCache
|
||||
_caches = caches;
|
||||
}
|
||||
|
||||
public async Task<IndexedVirtualFile?> Get(Hash hash, CancellationToken token)
|
||||
public async Task<IndexedVirtualFile?> Get(Hash hash, IStreamFactory sf, CancellationToken token)
|
||||
{
|
||||
IndexedVirtualFile? result = null;
|
||||
foreach (var cache in _caches)
|
||||
{
|
||||
if (result == null)
|
||||
{
|
||||
result = await cache.Get(hash, token);
|
||||
result = await cache.Get(hash, sf, token);
|
||||
if (result == null) continue;
|
||||
foreach (var upperCache in _caches)
|
||||
{
|
||||
|
@ -41,7 +41,7 @@ public class VFSDiskCache : IVfsCache
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
public async Task<IndexedVirtualFile?> Get(Hash hash, CancellationToken token)
|
||||
public async Task<IndexedVirtualFile?> Get(Hash hash, IStreamFactory sfn, CancellationToken token)
|
||||
{
|
||||
if (hash == default)
|
||||
throw new ArgumentException("Cannot cache default hashes");
|
||||
|
@ -177,7 +177,7 @@ public class VirtualFile
|
||||
hash = await hstream.HashingCopy(Stream.Null, token, job);
|
||||
}
|
||||
|
||||
var found = await context.VfsCache.Get(hash, token);
|
||||
var found = await context.VfsCache.Get(hash, extractedFile, token);
|
||||
if (found != null)
|
||||
{
|
||||
var file = ConvertFromIndexedFile(context, found!, relPath, parent!, extractedFile);
|
||||
|
Loading…
Reference in New Issue
Block a user