-
+ @if (ModlistImage is not null)
+ {
+
+ }
@if (Modlist is not null)
diff --git a/Wabbajack.App.Blazor/Pages/Install/Installing.razor.cs b/Wabbajack.App.Blazor/Pages/Install/Installing.razor.cs
index d53727ea..11108d2e 100644
--- a/Wabbajack.App.Blazor/Pages/Install/Installing.razor.cs
+++ b/Wabbajack.App.Blazor/Pages/Install/Installing.razor.cs
@@ -29,36 +29,31 @@ public partial class Installing
[Inject] private IJSRuntime JSRuntime { get; set; } = default!;
private ModList? Modlist => StateContainer.Modlist;
- private string ModlistImage => StateContainer.ModlistImage;
+ private string? ModlistImage => StateContainer.ModlistImage;
private AbsolutePath ModlistPath => StateContainer.ModlistPath;
private AbsolutePath InstallPath => StateContainer.InstallPath;
private AbsolutePath DownloadPath => StateContainer.DownloadPath;
- public string StatusCategory { get; set; }
-
- private string LastStatus { get; set; }
-
- public List
StatusStep { get; set; } = new();
-
- private InstallState InstallState => StateContainer.InstallState;
-
+ private string? StatusCategory { get; set; }
+ private string? LastStatus { get; set; }
+ private List StatusStep { get; set; } = new();
+
private const string InstallSettingsPrefix = "install-settings-";
private bool _shouldRender;
protected override bool ShouldRender() => _shouldRender;
-
- protected override void OnInitialized()
+
+ protected override async Task OnInitializedAsync()
{
- Install();
_shouldRender = true;
+ await Task.Run(Install);
}
private async Task Install()
{
if (Modlist is null) return;
-
StateContainer.InstallState = InstallState.Installing;
- await Task.Run(() => BeginInstall(Modlist));
+ await BeginInstall(Modlist);
}
private async Task BeginInstall(ModList modlist)
diff --git a/Wabbajack.App.Blazor/Shared/MainLayout.razor b/Wabbajack.App.Blazor/Shared/MainLayout.razor
index fc248bcd..b2ee7cb4 100644
--- a/Wabbajack.App.Blazor/Shared/MainLayout.razor
+++ b/Wabbajack.App.Blazor/Shared/MainLayout.razor
@@ -2,6 +2,7 @@
@inherits LayoutComponentBase
@namespace Wabbajack.App.Blazor.Shared
+
diff --git a/Wabbajack.App.Blazor/Utility/JsInterop.cs b/Wabbajack.App.Blazor/Utility/JsInterop.cs
new file mode 100644
index 00000000..751bcdce
--- /dev/null
+++ b/Wabbajack.App.Blazor/Utility/JsInterop.cs
@@ -0,0 +1,12 @@
+using Microsoft.JSInterop;
+
+namespace Wabbajack.App.Blazor.Utility;
+
+public static class JsInterop
+{
+ ///
+ /// Converts a into a blob URL. Useful for streaming images.
+ /// async function getBlobUrlFromStream(imageStream: DotNetStreamReference)
+ ///
+ public const string GetBlobUrlFromStream = "getBlobUrlFromStream";
+}
diff --git a/Wabbajack.App.Blazor/Utility/UILoggerTarget.cs b/Wabbajack.App.Blazor/Utility/UILoggerTarget.cs
new file mode 100644
index 00000000..db948321
--- /dev/null
+++ b/Wabbajack.App.Blazor/Utility/UILoggerTarget.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Reactive.Subjects;
+using NLog;
+using NLog.Targets;
+
+namespace Wabbajack.App.Blazor.Utility;
+
+public class UiLoggerTarget : TargetWithLayout
+{
+ private readonly Subject _logs = new();
+ public IObservable Logs => _logs;
+
+ protected override void Write(LogEventInfo logEvent)
+ {
+ _logs.OnNext(RenderLogEvent(Layout, logEvent));
+ }
+}
diff --git a/Wabbajack.App.Blazor/Utility/UIUtils.cs b/Wabbajack.App.Blazor/Utility/UIUtils.cs
index 652cb2b6..e870992b 100644
--- a/Wabbajack.App.Blazor/Utility/UIUtils.cs
+++ b/Wabbajack.App.Blazor/Utility/UIUtils.cs
@@ -1,16 +1,10 @@
using System;
using System.Diagnostics;
-using System.IO;
-using System.Net.Http;
-using System.Text;
-using System.Threading.Tasks;
using System.Windows.Forms;
-using System.Windows.Media.Imaging;
-using Wabbajack.Hashing.xxHash64;
using Wabbajack.Paths;
using Wabbajack.Paths.IO;
-namespace Wabbajack
+namespace Wabbajack.App.Blazor.Utility
{
public static class UIUtils
{
@@ -31,11 +25,11 @@ namespace Wabbajack
}
- public static AbsolutePath OpenFileDialog(string filter, string initialDirectory = null)
+ public static AbsolutePath OpenFileDialog(string filter, string? initialDirectory = null)
{
- OpenFileDialog ofd = new OpenFileDialog();
+ var ofd = new OpenFileDialog();
ofd.Filter = filter;
- ofd.InitialDirectory = initialDirectory;
+ ofd.InitialDirectory = initialDirectory ?? string.Empty;
if (ofd.ShowDialog() == DialogResult.OK)
return (AbsolutePath)ofd.FileName;
return default;
diff --git a/Wabbajack.Installer/AInstaller.cs b/Wabbajack.Installer/AInstaller.cs
index bc962a22..02e38ab1 100644
--- a/Wabbajack.Installer/AInstaller.cs
+++ b/Wabbajack.Installer/AInstaller.cs
@@ -120,7 +120,7 @@ public abstract class AInstaller
ExtractedModlistFolder = _manager.CreateFolder();
await using var stream = _configuration.ModlistArchive.Open(FileMode.Open, FileAccess.Read, FileShare.Read);
using var archive = new ZipArchive(stream, ZipArchiveMode.Read);
- NextStep("Preparing","Extracting Modlist", archive.Entries.Count);
+ NextStep(Consts.StepPreparing,"Extracting Modlist", archive.Entries.Count);
foreach (var entry in archive.Entries)
{
var path = entry.FullName.ToRelativePath().RelativeTo(ExtractedModlistFolder);
@@ -182,7 +182,7 @@ public abstract class AInstaller
///
protected async Task PrimeVFS()
{
- NextStep("Preparing","Priming VFS", 0);
+ NextStep(Consts.StepPreparing,"Priming VFS", 0);
_vfs.AddKnown(_configuration.ModList.Directives.OfType().Select(d => d.ArchiveHashPath),
HashedArchives);
await _vfs.BackfillMissing();
@@ -190,7 +190,7 @@ public abstract class AInstaller
public async Task BuildFolderStructure()
{
- NextStep("Preparing", "Building Folder Structure", 0);
+ NextStep(Consts.StepPreparing, "Building Folder Structure", 0);
_logger.LogInformation("Building Folder Structure");
ModList.Directives
.Where(d => d.To.Depth > 1)
@@ -201,7 +201,7 @@ public abstract class AInstaller
public async Task InstallArchives(CancellationToken token)
{
- NextStep("Installing", "Installing files", ModList.Directives.Sum(d => d.Size));
+ NextStep(Consts.StepInstalling, "Installing files", ModList.Directives.Sum(d => d.Size));
var grouped = ModList.Directives
.OfType()
.Select(a => new {VF = _vfs.Index.FileForArchiveHashPath(a.ArchiveHashPath), Directive = a})
@@ -302,15 +302,15 @@ public abstract class AInstaller
}
}
- _logger.LogInformation("Downloading {count} archives", missing.Count);
- NextStep("Downloading", "Downloading files", missing.Count);
+ _logger.LogInformation("Downloading {Count} archives", missing.Count.ToString());
+ NextStep(Consts.StepDownloading, "Downloading files", missing.Count);
await missing
.OrderBy(a => a.Size)
.Where(a => a.State is not Manual)
.PDoAll(async archive =>
{
- _logger.LogInformation("Downloading {archive}", archive.Name);
+ _logger.LogInformation("Downloading {Archive}", archive.Name);
var outputPath = _configuration.Downloads.Combine(archive.Name);
if (download)
@@ -364,7 +364,7 @@ public abstract class AInstaller
public async Task HashArchives(CancellationToken token)
{
- NextStep("Hashing", "Hashing Archives", 0);
+ NextStep(Consts.StepHashing, "Hashing Archives", 0);
_logger.LogInformation("Looking for files to hash");
var allFiles = _configuration.Downloads.EnumerateFiles()
@@ -415,7 +415,7 @@ public abstract class AInstaller
var savePath = (RelativePath) "saves";
var existingFiles = _configuration.Install.EnumerateFiles().ToList();
- NextStep("Preparing", "Looking for files to delete", existingFiles.Count);
+ NextStep(Consts.StepPreparing, "Looking for files to delete", existingFiles.Count);
await existingFiles
.PDoAll(async f =>
{
@@ -434,7 +434,7 @@ public abstract class AInstaller
});
_logger.LogInformation("Cleaning empty folders");
- NextStep("Preparing", "Cleaning empty folders", indexed.Keys.Count);
+ NextStep(Consts.StepPreparing, "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
@@ -469,7 +469,7 @@ public abstract class AInstaller
var existingfiles = _configuration.Install.EnumerateFiles().ToHashSet();
- NextStep("Preparing", "Removing redundant directives", indexed.Count);
+ NextStep(Consts.StepPreparing, "Removing redundant directives", indexed.Count);
await indexed.Values.PMapAll(async d =>
{
// Bit backwards, but we want to return null for
@@ -488,7 +488,7 @@ public abstract class AInstaller
_logger.LogInformation("Optimized {optimized} directives to {indexed} required", ModList.Directives.Length,
indexed.Count);
- NextStep("Preparing", "Finalizing modlist optimization", 0);
+ NextStep(Consts.StepPreparing, "Finalizing modlist optimization", 0);
var requiredArchives = indexed.Values.OfType()
.GroupBy(d => d.ArchiveHashPath.Hash)
.Select(d => d.Key)
diff --git a/Wabbajack.Installer/Consts.cs b/Wabbajack.Installer/Consts.cs
index d3ef7b15..a4c81009 100644
--- a/Wabbajack.Installer/Consts.cs
+++ b/Wabbajack.Installer/Consts.cs
@@ -2,7 +2,7 @@ using Wabbajack.Paths;
namespace Wabbajack.Installer;
-public class Consts
+public static class Consts
{
public static string GAME_PATH_MAGIC_BACK = "{--||GAME_PATH_MAGIC_BACK||--}";
public static string GAME_PATH_MAGIC_DOUBLE_BACK = "{--||GAME_PATH_MAGIC_DOUBLE_BACK||--}";
@@ -20,4 +20,10 @@ public class Consts
public static RelativePath MO2ModFolderName = "mods".ToRelativePath();
public static RelativePath MO2ProfilesFolderName = "profiles".ToRelativePath();
+
+ public const string StepPreparing = "Preparing";
+ public const string StepInstalling = "Installing";
+ public const string StepDownloading = "Downloading";
+ public const string StepHashing = "Hashing";
+ public const string StepFinished = "Finished";
}
\ No newline at end of file
diff --git a/Wabbajack.Installer/StandardInstaller.cs b/Wabbajack.Installer/StandardInstaller.cs
index 13de952a..2be0601f 100644
--- a/Wabbajack.Installer/StandardInstaller.cs
+++ b/Wabbajack.Installer/StandardInstaller.cs
@@ -61,7 +61,7 @@ public class StandardInstaller : AInstaller
{
if (token.IsCancellationRequested) return false;
await _wjClient.SendMetric(MetricNames.BeginInstall, ModList.Name);
- NextStep("Preparing", "Configuring Installer", 0);
+ NextStep(Consts.StepPreparing, "Configuring Installer", 0);
_logger.LogInformation("Configuring Processor");
if (_configuration.GameFolder == default)
@@ -145,7 +145,7 @@ public class StandardInstaller : AInstaller
await ExtractedModlistFolder!.DisposeAsync();
await _wjClient.SendMetric(MetricNames.FinishInstall, ModList.Name);
- NextStep("Finished", "Finished", 1);
+ NextStep(Consts.StepFinished, "Finished", 1);
_logger.LogInformation("Finished Installation");
return true;
}
@@ -275,7 +275,7 @@ public class StandardInstaller : AInstaller
private async Task InstallIncludedFiles(CancellationToken token)
{
_logger.LogInformation("Writing inline files");
- NextStep("Installing", "Installing Included Files", ModList.Directives.OfType().Count());
+ NextStep(Consts.StepInstalling, "Installing Included Files", ModList.Directives.OfType().Count());
await ModList.Directives
.OfType()
.PDoAll(async directive =>