mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
Update
This commit is contained in:
parent
8782aeca94
commit
92a2195ccf
@ -6,18 +6,9 @@ insert_final_newline = true
|
||||
|
||||
# C# and Razor files
|
||||
[*.{cs,razor}]
|
||||
|
||||
# CS8602: Dereference of a possibly null reference.
|
||||
# CS8618: Non-nullable member is uninitialized.
|
||||
# Reason: The compiler/IDE doesn't quite understand Dependency Injection yet.
|
||||
dotnet_diagnostic.CS8602.severity = none
|
||||
dotnet_diagnostic.CS8618.severity = none
|
||||
|
||||
# RZ10012: Markup element with unexpected name.
|
||||
# Reason: The component namespace is added to the global _Imports.razor file.
|
||||
dotnet_diagnostic.RZ10012.severity = none
|
||||
|
||||
dotnet_sort_system_directives_first = true
|
||||
|
||||
[*.scss]
|
||||
indent_size = 2
|
||||
|
@ -14,16 +14,17 @@ namespace Wabbajack.App.Blazor;
|
||||
public partial class App
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IHost _host;
|
||||
|
||||
public App()
|
||||
{
|
||||
_host = Host.CreateDefaultBuilder(Array.Empty<string>())
|
||||
.ConfigureLogging(c => { c.ClearProviders(); })
|
||||
.ConfigureServices((host, services) => { ConfigureServices(services); })
|
||||
.Build();
|
||||
|
||||
_serviceProvider = _host.Services;
|
||||
_serviceProvider = Host.CreateDefaultBuilder(Array.Empty<string>())
|
||||
.ConfigureLogging(loggingBuilder =>
|
||||
{
|
||||
loggingBuilder.ClearProviders();
|
||||
})
|
||||
.ConfigureServices(services => ConfigureServices(services))
|
||||
.Build()
|
||||
.Services;
|
||||
}
|
||||
|
||||
private static IServiceCollection ConfigureServices(IServiceCollection services)
|
||||
@ -33,24 +34,18 @@ public partial class App
|
||||
services.AddAllSingleton<ILoggerProvider, LoggerProvider>();
|
||||
services.AddTransient<MainWindow>();
|
||||
services.AddSingleton<SystemParametersConstructor>();
|
||||
services.AddSingleton<GlobalState>();
|
||||
services.AddSingleton(typeof(IStateContainer), typeof(StateContainer));
|
||||
return services;
|
||||
}
|
||||
|
||||
private void OnStartup(object sender, StartupEventArgs e)
|
||||
{
|
||||
var mainWindow = _serviceProvider.GetRequiredService<MainWindow>();
|
||||
mainWindow!.Show();
|
||||
mainWindow.Show();
|
||||
}
|
||||
|
||||
private void OnExit(object sender, ExitEventArgs e)
|
||||
{
|
||||
Current.Shutdown();
|
||||
// using (_host)
|
||||
// {
|
||||
// _host.StopAsync();
|
||||
// }
|
||||
//
|
||||
// base.OnExit(e);
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +0,0 @@
|
||||
@using Wabbajack.DTOs
|
||||
|
||||
@namespace Wabbajack.App.Blazor.Components
|
||||
|
||||
<div class="item">
|
||||
<div class="display">
|
||||
<img src="@Metadata.Links.ImageUri" loading="lazy" class="image" alt="@Metadata.Title">
|
||||
<div class="interaction">
|
||||
@ChildContent
|
||||
</div>
|
||||
</div>
|
||||
<div class="info">
|
||||
<div class="title">@Metadata.Title</div>
|
||||
<div class="author">@Metadata.Author</div>
|
||||
<div class="description">@Metadata.Description</div>
|
||||
</div>
|
||||
<div class="tags"></div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
|
||||
[Parameter]
|
||||
public ModlistMetadata Metadata { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment ChildContent { get; set; }
|
||||
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
@import "../Shared/Globals.scss";
|
||||
|
||||
$display-height: 225px;
|
||||
$hover-icon-size: 75px;
|
||||
|
||||
.item {
|
||||
width: 400px;
|
||||
height: 450px;
|
||||
overflow: hidden;
|
||||
margin: 0.5rem;
|
||||
padding: 1rem;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
|
||||
backdrop-filter: blur(7px);
|
||||
-webkit-backdrop-filter: blur(7px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.31);
|
||||
|
||||
&:hover .display .image {
|
||||
filter: blur(2px) brightness(70%);
|
||||
}
|
||||
|
||||
&:hover .display .interaction {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.display {
|
||||
position: relative;
|
||||
height: $display-height;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.image {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
transition: all 250ms ease-in-out;
|
||||
}
|
||||
|
||||
.interaction {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
transition: all 250ms ease-in-out;
|
||||
|
||||
::deep img {
|
||||
width: $hover-icon-size;
|
||||
height: $hover-icon-size;
|
||||
margin: 0;
|
||||
transition: all 150ms ease-in-out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info {
|
||||
padding-bottom: 1rem;
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
|
||||
.title {
|
||||
color: white;
|
||||
font-weight: 100;
|
||||
font-size: 2rem;
|
||||
line-height: 2.5rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.author {
|
||||
color: lightgray;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: grey;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
|
||||
.tags {
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@
|
||||
@* TODO: [Low] Replace logo with SVG? *@
|
||||
<img class="logo" src="images/Logo_Dark_Transparent.png" alt="Wabbajack Logo">
|
||||
<div class="socials">
|
||||
@* TODO: wrap social icons in a button and make it clickable *@
|
||||
<img src="images/icons/patreon.svg" alt="">
|
||||
<img src="images/icons/github.svg" alt="">
|
||||
<img src="images/icons/discord.svg" alt="">
|
||||
|
@ -1,42 +1,36 @@
|
||||
@using Wabbajack.App.Blazor.Pages
|
||||
@using Wabbajack.App.Blazor.Shared
|
||||
@using Wabbajack.App.Blazor.State
|
||||
@inject NavigationManager _navigationManager
|
||||
@inject IStateContainer _stateContainer
|
||||
|
||||
@namespace Wabbajack.App.Blazor.Components
|
||||
|
||||
@* TODO: [Low] Clean this up a bit. *@
|
||||
<header id="top-bar">
|
||||
<nav class="@(GlobalState.NavigationAllowed ? "" : "disallow")">
|
||||
<nav class="@(_stateContainer.NavigationAllowed ? "" : "disallow")">
|
||||
<ul>
|
||||
<li>
|
||||
<div class='item @CurrentPage("")' @onclick='() => Navigate("")'>Play</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class='item @CurrentPage("Gallery")' @onclick='() => Navigate("Gallery")'>Gallery</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class='item @CurrentPage("Install")' @onclick='() => Navigate("Install")'>Install</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class='item @CurrentPage("Create")' @onclick='() => Navigate("Create")'>Create</div>
|
||||
</li>
|
||||
@foreach (var (name, route) in Pages)
|
||||
{
|
||||
<li>
|
||||
<div class="item @CurrentPage(route)" @onclick="@(() => Navigate(route))">@name</div>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</nav>
|
||||
<div class="settings">
|
||||
<InteractionIcon Icon="images/icons/adjust.svg" Label="Settings" Size="100%" OnClick="@(() => Navigate("Settings"))"/>
|
||||
<InteractionIcon Icon="images/icons/adjust.svg" Label="Settings" Size="100%" OnClick="@(() => Navigate(Settings.Route))"/>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@code {
|
||||
|
||||
[Inject]
|
||||
NavigationManager _navigationManager { get; set; }
|
||||
|
||||
[Inject]
|
||||
GlobalState GlobalState { get; set; }
|
||||
|
||||
[CascadingParameter]
|
||||
protected MainLayout _mainLayout { get; set; }
|
||||
private static readonly Dictionary<string, string> Pages = new()
|
||||
{
|
||||
{"Play", Play.Route},
|
||||
{"Gallery", Gallery.Route},
|
||||
{"Install", Install.Route},
|
||||
{"Create", Create.Route}
|
||||
};
|
||||
|
||||
private void Navigate(string page)
|
||||
{
|
||||
@ -45,14 +39,14 @@
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_navigationManager.LocationChanged += (o, args) => StateHasChanged();
|
||||
GlobalState.OnNavigationStateChange += StateHasChanged;
|
||||
// TODO(erri120): update this
|
||||
// _navigationManager.LocationChanged += (_, _) => StateHasChanged();
|
||||
// _globalState.OnNavigationStateChange += StateHasChanged;
|
||||
}
|
||||
|
||||
private string CurrentPage(string page)
|
||||
{
|
||||
string relativePath = _navigationManager.ToBaseRelativePath(_navigationManager.Uri).ToLower();
|
||||
return page.ToLower() == relativePath ? "active" : "";
|
||||
var relativePath = _navigationManager.ToBaseRelativePath(_navigationManager.Uri);
|
||||
return page.Equals(relativePath, StringComparison.OrdinalIgnoreCase) ? "active" : string.Empty;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Wabbajack.App.Blazor.Models;
|
||||
using Wabbajack.App.Blazor.State;
|
||||
@ -11,20 +12,20 @@ namespace Wabbajack.App.Blazor;
|
||||
|
||||
public partial class MainWindow
|
||||
{
|
||||
private readonly ILogger<MainWindow> _logger;
|
||||
private readonly LoggerProvider _loggerProvider;
|
||||
private readonly ILogger<MainWindow> _logger;
|
||||
private readonly LoggerProvider _loggerProvider;
|
||||
private readonly SystemParametersConstructor _systemParams;
|
||||
private readonly GlobalState _globalState;
|
||||
private readonly IStateContainer _stateContainer;
|
||||
|
||||
public MainWindow(ILogger<MainWindow> logger, IServiceProvider serviceProvider, LoggerProvider loggerProvider,
|
||||
SystemParametersConstructor systemParams, GlobalState globalState)
|
||||
SystemParametersConstructor systemParams, IStateContainer stateContainer)
|
||||
{
|
||||
_logger = logger;
|
||||
_logger = logger;
|
||||
_loggerProvider = loggerProvider;
|
||||
_systemParams = systemParams;
|
||||
_globalState = globalState;
|
||||
_systemParams = systemParams;
|
||||
_stateContainer = stateContainer;
|
||||
|
||||
_globalState.OnTaskBarStateChange += state =>
|
||||
_stateContainer.TaskBarStateObservable.Subscribe(state =>
|
||||
{
|
||||
Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
@ -32,7 +33,7 @@ public partial class MainWindow
|
||||
TaskBarItem.ProgressState = state.State;
|
||||
TaskBarItem.ProgressValue = state.ProgressValue;
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
InitializeComponent();
|
||||
BlazorWebView.Services = serviceProvider;
|
||||
|
@ -71,7 +71,7 @@ public class LoggerProvider : ILoggerProvider
|
||||
|
||||
private void LogToFile(ILogMessage logMessage)
|
||||
{
|
||||
string? line = $"[{logMessage.TimeStamp - _startupTime}] {logMessage.LongMessage}";
|
||||
var line = $"[{logMessage.TimeStamp - _startupTime}] {logMessage.LongMessage}";
|
||||
lock (_logStream)
|
||||
{
|
||||
_logStream.Write(line);
|
||||
|
@ -1,52 +1,50 @@
|
||||
@page "/Configure"
|
||||
|
||||
@page "/configure"
|
||||
@using Wabbajack.App.Blazor.State
|
||||
|
||||
@namespace Wabbajack.App.Blazor.Pages
|
||||
|
||||
<div id="content">
|
||||
<div class="install-background">
|
||||
<img src="@Image" alt="">
|
||||
<img id="background-image" src="" alt=""/>
|
||||
</div>
|
||||
<div class="list">
|
||||
@* TODO: [High] Find a cleaner way to show/hide components based on state. *@
|
||||
@* TODO: [Low] Split each "side" into their own components? *@
|
||||
<div class="left-side">
|
||||
@if (!string.IsNullOrEmpty(ModList.Name))
|
||||
{
|
||||
if (InstallState != GlobalState.InstallStateEnum.Installing)
|
||||
@if (Modlist is not null)
|
||||
{
|
||||
<div class="left-side">
|
||||
@if (InstallState != InstallState.Installing)
|
||||
{
|
||||
<InfoBlock Title="@ModList.Name" Subtitle="@ModList.Author" Comment="@ModList.Version.ToString()" Description="@ModList.Description"/>
|
||||
<InfoBlock Title="@Modlist.Name" Subtitle="@Modlist.Author" Comment="@Modlist.Version.ToString()" Description="@Modlist.Description"/>
|
||||
}
|
||||
else if (InstallState == GlobalState.InstallStateEnum.Installing)
|
||||
else
|
||||
{
|
||||
<InfoBlock Supertitle="Installing..." Title="@ModList.Name" Subtitle="@StatusText"/>
|
||||
// TODO: [Low] Step logging.
|
||||
<InfoBlock Supertitle="Installing..." Title="@Modlist.Name" Subtitle="@StatusText"/>
|
||||
// TODO: [Low] Step logging
|
||||
}
|
||||
}
|
||||
</div>
|
||||
<div class="right-side">
|
||||
@if (!string.IsNullOrEmpty(Image))
|
||||
{
|
||||
if (InstallState != GlobalState.InstallStateEnum.Installing)
|
||||
{
|
||||
<InfoImage Image="@Image"/>
|
||||
}
|
||||
else if (InstallState == GlobalState.InstallStateEnum.Installing)
|
||||
{
|
||||
// TODO: [Low] Implement featured mod slideshow.
|
||||
<InfoImage Image="@Image" Title="Some Mod Title" Subtitle="Author and others" Description="This mod adds something cool but I'm not going to tell you anything."/>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-side">
|
||||
@* TODO: whatever this is *@
|
||||
@* @if (!string.IsNullOrEmpty(Image)) *@
|
||||
@* { *@
|
||||
@* if (InstallState != GlobalState.InstallStateEnum.Installing) *@
|
||||
@* { *@
|
||||
@* <InfoImage Image="@Image"/> *@
|
||||
@* } *@
|
||||
@* else if (InstallState == GlobalState.InstallStateEnum.Installing) *@
|
||||
@* { *@
|
||||
@* // TODO: [Low] Implement featured mod slideshow. *@
|
||||
@* <InfoImage Image="@Image" Title="Some Mod Title" Subtitle="Author and others" Description="This mod adds something cool but I'm not going to tell you anything."/> *@
|
||||
@* } *@
|
||||
@* } *@
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@if (InstallState == GlobalState.InstallStateEnum.Installing)
|
||||
@if (InstallState == InstallState.Installing)
|
||||
{
|
||||
<div class="logger-container">
|
||||
<VirtualLogger Messages="_loggerProvider.Messages"/>
|
||||
<VirtualLogger Messages="LoggerProvider.Messages"/>
|
||||
</div>
|
||||
}
|
||||
@if (InstallState != GlobalState.InstallStateEnum.Installing)
|
||||
else
|
||||
{
|
||||
<div class="settings">
|
||||
<div class="locations">
|
||||
@ -57,9 +55,9 @@
|
||||
<span>Download Location</span>
|
||||
</div>
|
||||
<div class="paths">
|
||||
<span class="modlist-file">@ModListPath</span>
|
||||
<span class="install-location" @onclick="SelectInstallFolder">@InstallPath</span>
|
||||
<span class="download-location" @onclick="SelectDownloadFolder">@DownloadPath</span>
|
||||
<span class="modlist-file">@ModlistPath.ToString()</span>
|
||||
<span class="install-location" @onclick="SelectInstallFolder">@InstallPath.ToString()</span>
|
||||
<span class="download-location" @onclick="SelectDownloadFolder">@DownloadPath.ToString()</span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@ -75,3 +73,7 @@
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
public const string Route = "/configure";
|
||||
}
|
||||
|
@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Wabbajack.DTOs;
|
||||
@ -12,6 +10,8 @@ using Wabbajack.Downloaders.GameFile;
|
||||
using Wabbajack.Hashing.xxHash64;
|
||||
using Wabbajack.Services.OSIntegrated;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.JSInterop;
|
||||
using Wabbajack.App.Blazor.Models;
|
||||
using Wabbajack.App.Blazor.State;
|
||||
|
||||
@ -19,71 +19,74 @@ namespace Wabbajack.App.Blazor.Pages;
|
||||
|
||||
public partial class Configure
|
||||
{
|
||||
[Inject] private NavigationManager NavigationManager { get; set; }
|
||||
[Inject] private GlobalState GlobalState { get; set; }
|
||||
[Inject] private DTOSerializer _dtos { get; set; }
|
||||
[Inject] private IServiceProvider _serviceProvider { get; set; }
|
||||
[Inject] private SystemParametersConstructor _parametersConstructor { get; set; }
|
||||
[Inject] private IGameLocator _gameLocator { get; set; }
|
||||
[Inject] private SettingsManager _settingsManager { get; set; }
|
||||
[Inject] private LoggerProvider _loggerProvider { get; set; }
|
||||
[Inject] private ILogger<Configure> Logger { get; set; } = default!;
|
||||
[Inject] private IStateContainer StateContainer { get; set; } = default!;
|
||||
[Inject] private DTOSerializer DTOs { get; set; } = default!;
|
||||
[Inject] private IServiceProvider ServiceProvider { get; set; } = default!;
|
||||
[Inject] private SystemParametersConstructor ParametersConstructor { get; set; } = default!;
|
||||
[Inject] private IGameLocator GameLocator { get; set; } = default!;
|
||||
[Inject] private SettingsManager SettingsManager { get; set; } = default!;
|
||||
[Inject] private LoggerProvider LoggerProvider { get; set; } = default!;
|
||||
[Inject] private JSRuntime JSRuntime { get; set; } = default!;
|
||||
|
||||
private ModList? Modlist => StateContainer.Modlist;
|
||||
|
||||
private string Image { get; set; }
|
||||
private ModList ModList { get; set; } = new(); // Init a new modlist so we can listen for changes in Blazor components.
|
||||
private AbsolutePath ModListPath { get; set; }
|
||||
private AbsolutePath InstallPath { get; set; }
|
||||
private AbsolutePath ModlistPath => StateContainer.ModlistPath;
|
||||
private AbsolutePath InstallPath { get; set; }
|
||||
private AbsolutePath DownloadPath { get; set; }
|
||||
|
||||
private string StatusText { get; set; }
|
||||
public GlobalState.InstallStateEnum InstallState { get; set; }
|
||||
private LoggerProvider.ILogMessage CurrentLog { get; set; }
|
||||
private string StatusText { get; set; } = string.Empty;
|
||||
private InstallState InstallState => StateContainer.InstallState;
|
||||
// private LoggerProvider.ILogMessage CurrentLog { get; set; }
|
||||
|
||||
private const string InstallSettingsPrefix = "install-settings-";
|
||||
|
||||
private bool _shouldRender;
|
||||
protected override bool ShouldRender() => _shouldRender;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
// var Location = KnownFolders.EntryPoint.Combine("downloaded_mod_lists", machineURL).WithExtension(Ext.Wabbajack);
|
||||
GlobalState.OnInstallStateChange += () => InstallState = GlobalState.InstallState;
|
||||
|
||||
await CheckValidInstallPath();
|
||||
await base.OnInitializedAsync();
|
||||
_shouldRender = true;
|
||||
}
|
||||
|
||||
private async Task CheckValidInstallPath()
|
||||
{
|
||||
if (GlobalState.ModListPath == AbsolutePath.Empty) return;
|
||||
if (ModlistPath == AbsolutePath.Empty) return;
|
||||
|
||||
var modlist = await StandardInstaller.LoadFromFile(DTOs, ModlistPath);
|
||||
StateContainer.Modlist = modlist;
|
||||
|
||||
ModListPath = GlobalState.ModListPath;
|
||||
ModList = await StandardInstaller.LoadFromFile(_dtos, ModListPath);
|
||||
GlobalState.ModList = ModList;
|
||||
var hex = (await ModlistPath.ToString().Hash()).ToHex();
|
||||
var prevSettings = await SettingsManager.Load<SavedInstallSettings>(InstallSettingsPrefix + hex);
|
||||
|
||||
string hex = (await ModListPath.ToString().Hash()).ToHex();
|
||||
var prevSettings = await _settingsManager.Load<SavedInstallSettings>(InstallSettingsPrefix + hex);
|
||||
|
||||
if (prevSettings.ModListLocation == ModListPath)
|
||||
if (prevSettings.ModlistLocation == ModlistPath)
|
||||
{
|
||||
ModListPath = prevSettings.ModListLocation;
|
||||
StateContainer.ModlistPath = prevSettings.ModlistLocation;
|
||||
InstallPath = prevSettings.InstallLocation;
|
||||
DownloadPath = prevSettings.DownloadLoadction;
|
||||
DownloadPath = prevSettings.DownloadLocation;
|
||||
//ModlistMetadata = metadata ?? prevSettings.Metadata;
|
||||
}
|
||||
|
||||
Stream image = await StandardInstaller.ModListImageStream(ModListPath);
|
||||
await using var reader = new MemoryStream();
|
||||
await image.CopyToAsync(reader);
|
||||
Image = $"data:image/png;base64,{Convert.ToBase64String(reader.ToArray())}";
|
||||
// see https://docs.microsoft.com/en-us/aspnet/core/blazor/images?view=aspnetcore-6.0#streaming-examples
|
||||
var imageStream = await StandardInstaller.ModListImageStream(ModlistPath);
|
||||
var dotnetImageStream = new DotNetStreamReference(imageStream);
|
||||
// setImageUsingStreaming accepts the img id and the data stream
|
||||
await JSRuntime.InvokeVoidAsync("setImageUsingStreaming", "background-image", dotnetImageStream);
|
||||
}
|
||||
|
||||
private async void SelectInstallFolder()
|
||||
{
|
||||
try
|
||||
{
|
||||
AbsolutePath? thing = await Dialog.ShowDialogNonBlocking(true);
|
||||
if (thing != null) InstallPath = (AbsolutePath)thing;
|
||||
StateHasChanged();
|
||||
var installPath = await Dialog.ShowDialogNonBlocking(true);
|
||||
if (installPath is not null) InstallPath = (AbsolutePath)installPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Print(ex.Message);
|
||||
Logger.LogError(e, "Exception selecting install folder");
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,68 +94,68 @@ public partial class Configure
|
||||
{
|
||||
try
|
||||
{
|
||||
AbsolutePath? thing = await Dialog.ShowDialogNonBlocking(true);
|
||||
if (thing != null) DownloadPath = (AbsolutePath)thing;
|
||||
StateHasChanged();
|
||||
var downloadPath = await Dialog.ShowDialogNonBlocking(true);
|
||||
if (downloadPath is not null) DownloadPath = (AbsolutePath)downloadPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Print(ex.Message);
|
||||
Logger.LogError(e, "Exception selecting download folder");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Install()
|
||||
{
|
||||
GlobalState.InstallState = GlobalState.InstallStateEnum.Installing;
|
||||
await Task.Run(BeginInstall);
|
||||
if (Modlist is null) return;
|
||||
|
||||
StateContainer.InstallState = InstallState.Installing;
|
||||
await Task.Run(() => BeginInstall(Modlist));
|
||||
}
|
||||
|
||||
private async Task BeginInstall()
|
||||
private async Task BeginInstall(ModList modlist)
|
||||
{
|
||||
string postfix = (await ModListPath.ToString().Hash()).ToHex();
|
||||
await _settingsManager.Save(InstallSettingsPrefix + postfix, new SavedInstallSettings
|
||||
var postfix = (await ModlistPath.ToString().Hash()).ToHex();
|
||||
await SettingsManager.Save(InstallSettingsPrefix + postfix, new SavedInstallSettings
|
||||
{
|
||||
ModListLocation = ModListPath,
|
||||
InstallLocation = InstallPath,
|
||||
DownloadLoadction = DownloadPath
|
||||
ModlistLocation = ModlistPath,
|
||||
InstallLocation = InstallPath,
|
||||
DownloadLocation = DownloadPath
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
var installer = StandardInstaller.Create(_serviceProvider, new InstallerConfiguration
|
||||
var installer = StandardInstaller.Create(ServiceProvider, new InstallerConfiguration
|
||||
{
|
||||
Game = ModList.GameType,
|
||||
Downloads = DownloadPath,
|
||||
Install = InstallPath,
|
||||
ModList = ModList,
|
||||
ModlistArchive = ModListPath,
|
||||
SystemParameters = _parametersConstructor.Create(),
|
||||
GameFolder = _gameLocator.GameLocation(ModList.GameType)
|
||||
Game = modlist.GameType,
|
||||
Downloads = DownloadPath,
|
||||
Install = InstallPath,
|
||||
ModList = modlist,
|
||||
ModlistArchive = ModlistPath,
|
||||
SystemParameters = ParametersConstructor.Create(),
|
||||
GameFolder = GameLocator.GameLocation(modlist.GameType)
|
||||
});
|
||||
|
||||
|
||||
|
||||
installer.OnStatusUpdate = update =>
|
||||
{
|
||||
if (StatusText != update.StatusText)
|
||||
{
|
||||
StatusText = update.StatusText;
|
||||
InvokeAsync(StateHasChanged);
|
||||
}
|
||||
var (statusText, _, _) = update;
|
||||
if (StatusText == statusText) return;
|
||||
StatusText = statusText;
|
||||
};
|
||||
|
||||
await installer.Begin(CancellationToken.None);
|
||||
StateContainer.InstallState = InstallState.Success;
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Print(ex.Message);
|
||||
Logger.LogError(e, "Exception installing Modlist");
|
||||
StateContainer.InstallState = InstallState.Failure;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class SavedInstallSettings
|
||||
{
|
||||
public AbsolutePath ModListLocation { get; set; }
|
||||
public AbsolutePath InstallLocation { get; set; }
|
||||
public AbsolutePath DownloadLoadction { get; set; }
|
||||
public ModlistMetadata Metadata { get; set; }
|
||||
public AbsolutePath ModlistLocation { get; set; } = AbsolutePath.Empty;
|
||||
public AbsolutePath InstallLocation { get; set; } = AbsolutePath.Empty;
|
||||
public AbsolutePath DownloadLocation { get; set; } = AbsolutePath.Empty;
|
||||
// public ModlistMetadata Metadata { get; set; }
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
@page "/Create"
|
||||
@page "/create"
|
||||
@namespace Wabbajack.App.Blazor.Pages
|
||||
|
||||
<div id="content">
|
||||
@ -6,3 +6,8 @@
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code
|
||||
{
|
||||
public const string Route = "/create";
|
||||
}
|
||||
|
@ -1,24 +1,51 @@
|
||||
@page "/Gallery"
|
||||
@page "/gallery"
|
||||
|
||||
@using Wabbajack.DTOs
|
||||
@using Wabbajack.RateLimiter
|
||||
@using System.Globalization
|
||||
|
||||
@namespace Wabbajack.App.Blazor.Pages
|
||||
|
||||
<div id="content">
|
||||
@foreach (ModlistMetadata modlist in _listItems)
|
||||
@if (_errorLoadingModlists)
|
||||
{
|
||||
<ModlistItem Metadata=@modlist>
|
||||
<InteractionIcon Icon="images/icons/install.svg" Label="Install" Size="75px" OnClick="@(() => OnClickDownload(modlist))"/>
|
||||
<InteractionIcon Icon="images/icons/info.svg" Label="Information" Size="75px" OnClick="@(() => OnClickInformation(modlist))"/>
|
||||
</ModlistItem>
|
||||
@* TODO: error *@
|
||||
}
|
||||
@if (DownloadProgress != Percent.Zero)
|
||||
else if (!Modlists.Any())
|
||||
{
|
||||
<BottomBar Image="@DownloadingMetaData.Links.ImageUri" Title="Downloading..." Subtitle="@DownloadingMetaData.Title">
|
||||
@* TODO: loading *@
|
||||
}
|
||||
else
|
||||
{
|
||||
@foreach (var modlist in Modlists)
|
||||
{
|
||||
<div @key="modlist.Title" class="item">
|
||||
<div class="display">
|
||||
<img src="@modlist.Links.ImageUri" loading="lazy" class="image" alt="@modlist.Title">
|
||||
<div class="interaction">
|
||||
<InteractionIcon Icon="images/icons/install.svg" Label="Install" Size="75px" OnClick="@(() => OnClickDownload(modlist))"/>
|
||||
<InteractionIcon Icon="images/icons/info.svg" Label="Information" Size="75px" OnClick="@(() => OnClickInformation(modlist))"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info">
|
||||
<div class="title">@modlist.Title</div>
|
||||
<div class="author">@modlist.Author</div>
|
||||
<div class="description">@modlist.Description</div>
|
||||
</div>
|
||||
<div class="tags"></div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
@if (_downloadProgress != Percent.Zero && _downloadingMetaData is not null)
|
||||
{
|
||||
<BottomBar Image="@_downloadingMetaData.Links.ImageUri" Title="Downloading..." Subtitle="@_downloadingMetaData.Title">
|
||||
<div style="height:1.5rem;">
|
||||
<ProgressBar Percentage="@DownloadProgress" Text="@DownloadProgress.Value.ToString()"/>
|
||||
<ProgressBar Percentage="@_downloadProgress" Text="@_downloadProgress.Value.ToString(CultureInfo.InvariantCulture)"/>
|
||||
</div>
|
||||
</BottomBar>
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
public const string Route = "/gallery";
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Shell;
|
||||
@ -20,33 +21,35 @@ namespace Wabbajack.App.Blazor.Pages;
|
||||
|
||||
public partial class Gallery
|
||||
{
|
||||
[Inject] private GlobalState GlobalState { get; set; }
|
||||
[Inject] private NavigationManager NavigationManager { get; set; }
|
||||
[Inject] private ILogger<Gallery> _logger { get; set; }
|
||||
[Inject] private Client _client { get; set; }
|
||||
[Inject] private ModListDownloadMaintainer _maintainer { get; set; }
|
||||
[Inject] private ILogger<Gallery> Logger { get; set; } = default!;
|
||||
[Inject] private IStateContainer StateContainer { get; set; } = default!;
|
||||
[Inject] private NavigationManager NavigationManager { get; set; } = default!;
|
||||
[Inject] private ModListDownloadMaintainer Maintainer { get; set; } = default!;
|
||||
|
||||
public Percent DownloadProgress { get; set; } = Percent.Zero;
|
||||
public ModlistMetadata DownloadingMetaData { get; set; } = new ModlistMetadata();
|
||||
private Percent _downloadProgress = Percent.Zero;
|
||||
private ModlistMetadata? _downloadingMetaData;
|
||||
|
||||
private List<ModlistMetadata> _listItems { get; set; } = new();
|
||||
private IEnumerable<ModlistMetadata> Modlists => StateContainer.Modlists;
|
||||
|
||||
private bool _errorLoadingModlists;
|
||||
|
||||
private bool _shouldRender;
|
||||
protected override bool ShouldRender() => _shouldRender;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
if (!StateContainer.Modlists.Any())
|
||||
{
|
||||
_logger.LogInformation("Getting modlists...");
|
||||
ModlistMetadata[] modLists = await _client.LoadLists();
|
||||
_listItems.AddRange(modLists.ToList());
|
||||
StateHasChanged();
|
||||
var res = await StateContainer.LoadModlistMetadata();
|
||||
if (!res)
|
||||
{
|
||||
_errorLoadingModlists = true;
|
||||
_shouldRender = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//TODO: [Critical] Figure out why an exception is thrown on first navigation.
|
||||
_logger.LogError(ex, "Error while loading lists");
|
||||
}
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
|
||||
_shouldRender = true;
|
||||
}
|
||||
|
||||
private async void OnClickDownload(ModlistMetadata metadata)
|
||||
@ -62,38 +65,42 @@ public partial class Gallery
|
||||
|
||||
private async Task Download(ModlistMetadata metadata)
|
||||
{
|
||||
GlobalState.NavigationAllowed = false;
|
||||
DownloadingMetaData = metadata;
|
||||
await using Timer timer = new(_ => InvokeAsync(StateHasChanged));
|
||||
timer.Change(TimeSpan.FromMilliseconds(250), TimeSpan.FromMilliseconds(250));
|
||||
StateContainer.NavigationAllowed = false;
|
||||
_downloadingMetaData = metadata;
|
||||
|
||||
try
|
||||
{
|
||||
(IObservable<Percent> progress, Task task) = _maintainer.DownloadModlist(metadata);
|
||||
var (progress, task) = Maintainer.DownloadModlist(metadata);
|
||||
|
||||
GlobalState.SetTaskBarState(TaskbarItemProgressState.Indeterminate,$"Downloading {metadata.Title}");
|
||||
|
||||
var dispose = progress.Subscribe(p =>
|
||||
var dispose = progress
|
||||
.DistinctUntilChanged(p => p.Value)
|
||||
.Throttle(TimeSpan.FromMilliseconds(100))
|
||||
.Subscribe(p =>
|
||||
{
|
||||
DownloadProgress = p;
|
||||
GlobalState.SetTaskBarState(TaskbarItemProgressState.Indeterminate,$"Downloading {metadata.Title}", p.Value);
|
||||
});
|
||||
_downloadProgress = p;
|
||||
StateContainer.TaskBarState = new TaskBarState
|
||||
{
|
||||
Description = $"Downloading {metadata.Title}",
|
||||
State = TaskbarItemProgressState.Indeterminate,
|
||||
ProgressValue = p.Value
|
||||
};
|
||||
}, () => { StateContainer.TaskBarState = new TaskBarState(); });
|
||||
|
||||
await task;
|
||||
//await _wjClient.SendMetric("downloading", Metadata.Title);
|
||||
dispose.Dispose();
|
||||
GlobalState.SetTaskBarState();
|
||||
|
||||
|
||||
AbsolutePath path = KnownFolders.EntryPoint.Combine("downloaded_mod_lists", metadata.Links.MachineURL).WithExtension(Ext.Wabbajack);
|
||||
GlobalState.ModListPath = path;
|
||||
NavigationManager.NavigateTo("/Configure");
|
||||
|
||||
var path = KnownFolders.EntryPoint.Combine("downloaded_mod_lists", metadata.Links.MachineURL).WithExtension(Ext.Wabbajack);
|
||||
StateContainer.ModlistPath = path;
|
||||
NavigationManager.NavigateTo(Configure.Route);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Print(e.Message);
|
||||
Logger.LogError(e, "Exception downloading Modlist {Name}", metadata.Title);
|
||||
}
|
||||
finally
|
||||
{
|
||||
StateContainer.TaskBarState = new TaskBarState();
|
||||
StateContainer.NavigationAllowed = true;
|
||||
}
|
||||
|
||||
await timer.DisposeAsync();
|
||||
GlobalState.NavigationAllowed = true;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,89 @@
|
||||
#content {
|
||||
@import "../Shared/Globals.scss";
|
||||
|
||||
#content {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
}
|
||||
|
||||
$display-height: 225px;
|
||||
$hover-icon-size: 75px;
|
||||
|
||||
.item {
|
||||
width: 400px;
|
||||
height: 450px;
|
||||
overflow: hidden;
|
||||
margin: 0.5rem;
|
||||
padding: 1rem;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
|
||||
backdrop-filter: blur(7px);
|
||||
-webkit-backdrop-filter: blur(7px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.31);
|
||||
|
||||
&:hover .display .image {
|
||||
filter: blur(2px) brightness(70%);
|
||||
}
|
||||
|
||||
&:hover .display .interaction {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.display {
|
||||
position: relative;
|
||||
height: $display-height;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.image {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
transition: all 250ms ease-in-out;
|
||||
}
|
||||
|
||||
.interaction {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
transition: all 250ms ease-in-out;
|
||||
|
||||
::deep img {
|
||||
width: $hover-icon-size;
|
||||
height: $hover-icon-size;
|
||||
margin: 0;
|
||||
transition: all 150ms ease-in-out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info {
|
||||
padding-bottom: 1rem;
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
|
||||
.title {
|
||||
color: white;
|
||||
font-weight: 100;
|
||||
font-size: 2rem;
|
||||
line-height: 2.5rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.author {
|
||||
color: lightgray;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: grey;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
|
||||
.tags {
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
@page "/Install"
|
||||
@page "/install"
|
||||
|
||||
@namespace Wabbajack.App.Blazor.Pages
|
||||
|
||||
@ -24,3 +24,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
public const string Route = "/install";
|
||||
}
|
||||
|
@ -8,8 +8,8 @@ namespace Wabbajack.App.Blazor.Pages;
|
||||
|
||||
public partial class Install
|
||||
{
|
||||
[Inject] private NavigationManager NavigationManager { get; set; }
|
||||
[Inject] private GlobalState GlobalState { get; set; }
|
||||
[Inject] private NavigationManager NavigationManager { get; set; } = default!;
|
||||
[Inject] private IStateContainer StateContainer { get; set; } = default!;
|
||||
|
||||
private void SelectFile()
|
||||
{
|
||||
@ -18,10 +18,10 @@ public partial class Install
|
||||
dialog.Multiselect = false;
|
||||
dialog.Filters.Add(new CommonFileDialogFilter("Wabbajack File", "*" + Ext.Wabbajack));
|
||||
if (dialog.ShowDialog() != CommonFileDialogResult.Ok) return;
|
||||
GlobalState.ModListPath = dialog.FileName.ToAbsolutePath();
|
||||
StateContainer.ModlistPath = dialog.FileName.ToAbsolutePath();
|
||||
}
|
||||
|
||||
NavigationManager.NavigateTo("/Configure");
|
||||
NavigationManager.NavigateTo(Configure.Route);
|
||||
}
|
||||
|
||||
private void VerifyFile(AbsolutePath path) { }
|
||||
|
@ -46,4 +46,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,3 +21,7 @@
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
@code {
|
||||
public const string Route = "/";
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
@page "/Settings"
|
||||
@page "/settings"
|
||||
@namespace Wabbajack.App.Blazor.Pages
|
||||
|
||||
<div id="content">
|
||||
@ -6,3 +6,7 @@
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
public const string Route = "/settings";
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ public partial class Settings
|
||||
{
|
||||
try
|
||||
{
|
||||
ResourceSettingsManager.ResourceSetting resource = await _resourceSettingsManager.GetSettings("Downloads");
|
||||
var resource = await _resourceSettingsManager.GetSettings("Downloads");
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex) { }
|
||||
|
@ -1,90 +0,0 @@
|
||||
using System;
|
||||
using System.Windows.Shell;
|
||||
using Wabbajack.DTOs;
|
||||
using Wabbajack.Paths;
|
||||
|
||||
namespace Wabbajack.App.Blazor.State;
|
||||
|
||||
public class GlobalState
|
||||
{
|
||||
#region Navigation Allowed
|
||||
|
||||
private bool _navigationAllowed = true;
|
||||
|
||||
public event Action OnNavigationStateChange;
|
||||
|
||||
public bool NavigationAllowed
|
||||
{
|
||||
get => _navigationAllowed;
|
||||
set
|
||||
{
|
||||
_navigationAllowed = value;
|
||||
OnNavigationStateChange?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Install
|
||||
|
||||
private InstallStateEnum _installState;
|
||||
private AbsolutePath _modListPath;
|
||||
private ModList _modList;
|
||||
|
||||
public event Action OnModListPathChange;
|
||||
public event Action OnModListChange;
|
||||
public event Action OnInstallStateChange;
|
||||
|
||||
public event Action<TaskBarState> OnTaskBarStateChange;
|
||||
|
||||
public void SetTaskBarState(TaskbarItemProgressState state = TaskbarItemProgressState.None, string description="", double progress = 0)
|
||||
{
|
||||
OnTaskBarStateChange?.Invoke(new TaskBarState
|
||||
{
|
||||
State = state,
|
||||
ProgressValue = progress,
|
||||
Description = description
|
||||
});
|
||||
}
|
||||
|
||||
public AbsolutePath ModListPath
|
||||
{
|
||||
get => _modListPath;
|
||||
set
|
||||
{
|
||||
_modListPath = value;
|
||||
OnModListPathChange?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public ModList ModList
|
||||
{
|
||||
get => _modList;
|
||||
set
|
||||
{
|
||||
_modList = value;
|
||||
OnModListChange?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public InstallStateEnum InstallState
|
||||
{
|
||||
get => _installState;
|
||||
set
|
||||
{
|
||||
_installState = value;
|
||||
OnInstallStateChange?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public enum InstallStateEnum
|
||||
{
|
||||
Waiting,
|
||||
Configuration,
|
||||
Installing,
|
||||
Success,
|
||||
Failure
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
28
Wabbajack.App.Blazor/State/IStateContainer.cs
Normal file
28
Wabbajack.App.Blazor/State/IStateContainer.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Wabbajack.DTOs;
|
||||
using Wabbajack.Paths;
|
||||
|
||||
namespace Wabbajack.App.Blazor.State;
|
||||
|
||||
public interface IStateContainer
|
||||
{
|
||||
IEnumerable<ModlistMetadata> Modlists { get; }
|
||||
Task<bool> LoadModlistMetadata();
|
||||
|
||||
IObservable<bool> NavigationAllowedObservable { get; }
|
||||
bool NavigationAllowed { get; set; }
|
||||
|
||||
IObservable<AbsolutePath> ModlistPathObservable { get; }
|
||||
AbsolutePath ModlistPath { get; set; }
|
||||
|
||||
IObservable<ModList?> ModlistObservable { get; }
|
||||
ModList? Modlist { get; set; }
|
||||
|
||||
IObservable<InstallState> InstallStateObservable { get; }
|
||||
InstallState InstallState { get; set; }
|
||||
|
||||
IObservable<TaskBarState> TaskBarStateObservable { get; }
|
||||
TaskBarState TaskBarState { get; set; }
|
||||
}
|
10
Wabbajack.App.Blazor/State/InstallState.cs
Normal file
10
Wabbajack.App.Blazor/State/InstallState.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace Wabbajack.App.Blazor.State;
|
||||
|
||||
public enum InstallState
|
||||
{
|
||||
Waiting,
|
||||
Configuration,
|
||||
Installing,
|
||||
Success,
|
||||
Failure
|
||||
}
|
81
Wabbajack.App.Blazor/State/StateContainer.cs
Normal file
81
Wabbajack.App.Blazor/State/StateContainer.cs
Normal file
@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.DTOs;
|
||||
using Wabbajack.Networking.WabbajackClientApi;
|
||||
using Wabbajack.Paths;
|
||||
|
||||
namespace Wabbajack.App.Blazor.State;
|
||||
|
||||
public class StateContainer : IStateContainer
|
||||
{
|
||||
private readonly ILogger<StateContainer> _logger;
|
||||
private readonly Client _client;
|
||||
|
||||
public StateContainer(ILogger<StateContainer> logger, Client client)
|
||||
{
|
||||
_logger = logger;
|
||||
_client = client;
|
||||
}
|
||||
|
||||
private ModlistMetadata[] _modlists = Array.Empty<ModlistMetadata>();
|
||||
public IEnumerable<ModlistMetadata> Modlists => _modlists;
|
||||
|
||||
public async Task<bool> LoadModlistMetadata()
|
||||
{
|
||||
try
|
||||
{
|
||||
var lists = await _client.LoadLists();
|
||||
_modlists = lists;
|
||||
return _modlists.Any();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Exception loading Modlists");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly CustomObservable<bool> _navigationAllowedObservable = new(true);
|
||||
public IObservable<bool> NavigationAllowedObservable => _navigationAllowedObservable;
|
||||
public bool NavigationAllowed
|
||||
{
|
||||
get => _navigationAllowedObservable.Value;
|
||||
set => _navigationAllowedObservable.Value = value;
|
||||
}
|
||||
|
||||
private readonly CustomObservable<AbsolutePath> _modlistPathObservable = new(AbsolutePath.Empty);
|
||||
public IObservable<AbsolutePath> ModlistPathObservable => _modlistPathObservable;
|
||||
public AbsolutePath ModlistPath
|
||||
{
|
||||
get => _modlistPathObservable.Value;
|
||||
set => _modlistPathObservable.Value = value;
|
||||
}
|
||||
|
||||
private readonly CustomObservable<ModList?> _modlistObservable = new(null);
|
||||
public IObservable<ModList?> ModlistObservable => _modlistObservable;
|
||||
public ModList? Modlist
|
||||
{
|
||||
get => _modlistObservable.Value;
|
||||
set => _modlistObservable.Value = value;
|
||||
}
|
||||
|
||||
private readonly CustomObservable<InstallState> _installStateObservable = new(InstallState.Waiting);
|
||||
public IObservable<InstallState> InstallStateObservable => _installStateObservable;
|
||||
public InstallState InstallState
|
||||
{
|
||||
get => _installStateObservable.Value;
|
||||
set => _installStateObservable.Value = value;
|
||||
}
|
||||
|
||||
private readonly CustomObservable<TaskBarState> _taskBarStateObservable = new(new TaskBarState());
|
||||
public IObservable<TaskBarState> TaskBarStateObservable => _taskBarStateObservable;
|
||||
public TaskBarState TaskBarState
|
||||
{
|
||||
get => _taskBarStateObservable.Value;
|
||||
set => _taskBarStateObservable.Value = value;
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ namespace Wabbajack.App.Blazor.State;
|
||||
|
||||
public class TaskBarState
|
||||
{
|
||||
public string Description { get; set; }
|
||||
public string Description { get; set; } = string.Empty;
|
||||
public double ProgressValue { get; set; }
|
||||
public TaskbarItemProgressState State { get; set; }
|
||||
public TaskbarItemProgressState State { get; set; } = TaskbarItemProgressState.None;
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ public static class Dialog
|
||||
var dialog = new CommonOpenFileDialog();
|
||||
dialog.IsFolderPicker = isFolderPicker;
|
||||
dialog.Multiselect = false;
|
||||
CommonFileDialogResult result = dialog.ShowDialog(newWindow);
|
||||
var result = dialog.ShowDialog(newWindow);
|
||||
return result == CommonFileDialogResult.Ok ? dialog.FileName : null;
|
||||
}, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext())
|
||||
.ContinueWith(result => result.Result?.ToAbsolutePath())
|
||||
|
@ -40,14 +40,14 @@ public class SystemParametersConstructor
|
||||
SetProcessDPIAware();
|
||||
unsafe
|
||||
{
|
||||
List<(int Width, int Height, bool IsPrimary)>? col = new List<(int Width, int Height, bool IsPrimary)>();
|
||||
var col = new List<(int Width, int Height, bool IsPrimary)>();
|
||||
|
||||
EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero,
|
||||
delegate(IntPtr hMonitor, IntPtr hdcMonitor, RECT* lprcMonitor, void* dwData)
|
||||
{
|
||||
var mi = new MONITORINFOEX();
|
||||
mi.cbSize = Marshal.SizeOf(mi);
|
||||
bool success = GetMonitorInfo(hMonitor, (MONITORINFO*)&mi);
|
||||
var success = GetMonitorInfo(hMonitor, (MONITORINFO*)&mi);
|
||||
if (success)
|
||||
col.Add((mi.Monitor.right - mi.Monitor.left, mi.Monitor.bottom - mi.Monitor.top,
|
||||
mi.Flags == MONITORINFO_Flags.MONITORINFOF_PRIMARY));
|
||||
@ -60,17 +60,17 @@ public class SystemParametersConstructor
|
||||
|
||||
public SystemParameters Create()
|
||||
{
|
||||
(int width, int height, _) = GetDisplays().First(d => d.IsPrimary);
|
||||
(var width, var height, _) = GetDisplays().First(d => d.IsPrimary);
|
||||
|
||||
/*using var f = new SharpDX.DXGI.Factory1();
|
||||
var video_memory = f.Adapters1.Select(a =>
|
||||
Math.Max(a.Description.DedicatedSystemMemory, (long)a.Description.DedicatedVideoMemory)).Max();*/
|
||||
|
||||
ulong dxgiMemory = 0UL;
|
||||
var dxgiMemory = 0UL;
|
||||
|
||||
unsafe
|
||||
{
|
||||
using DXGI? api = DXGI.GetApi();
|
||||
using var api = DXGI.GetApi();
|
||||
|
||||
IDXGIFactory1* factory1 = default;
|
||||
|
||||
@ -79,15 +79,15 @@ public class SystemParametersConstructor
|
||||
//https://docs.microsoft.com/en-us/windows/win32/api/dxgi/nf-dxgi-createdxgifactory1
|
||||
SilkMarshal.ThrowHResult(api.CreateDXGIFactory1(SilkMarshal.GuidPtrOf<IDXGIFactory1>(), (void**)&factory1));
|
||||
|
||||
uint i = 0u;
|
||||
var i = 0u;
|
||||
while (true)
|
||||
{
|
||||
IDXGIAdapter1* adapter1 = default;
|
||||
|
||||
//https://docs.microsoft.com/en-us/windows/win32/api/dxgi/nf-dxgi-idxgifactory1-enumadapters1
|
||||
int res = factory1->EnumAdapters1(i, &adapter1);
|
||||
var res = factory1->EnumAdapters1(i, &adapter1);
|
||||
|
||||
Exception? exception = Marshal.GetExceptionForHR(res);
|
||||
var exception = Marshal.GetExceptionForHR(res);
|
||||
if (exception != null) break;
|
||||
|
||||
AdapterDesc1 adapterDesc = default;
|
||||
@ -95,10 +95,10 @@ public class SystemParametersConstructor
|
||||
//https://docs.microsoft.com/en-us/windows/win32/api/dxgi/nf-dxgi-idxgiadapter1-getdesc1
|
||||
SilkMarshal.ThrowHResult(adapter1->GetDesc1(&adapterDesc));
|
||||
|
||||
ulong systemMemory = (ulong)adapterDesc.DedicatedSystemMemory;
|
||||
ulong videoMemory = (ulong)adapterDesc.DedicatedVideoMemory;
|
||||
var systemMemory = (ulong)adapterDesc.DedicatedSystemMemory;
|
||||
var videoMemory = (ulong)adapterDesc.DedicatedVideoMemory;
|
||||
|
||||
ulong maxMemory = Math.Max(systemMemory, videoMemory);
|
||||
var maxMemory = Math.Max(systemMemory, videoMemory);
|
||||
if (maxMemory > dxgiMemory)
|
||||
dxgiMemory = maxMemory;
|
||||
|
||||
@ -117,7 +117,7 @@ public class SystemParametersConstructor
|
||||
}
|
||||
}
|
||||
|
||||
MEMORYSTATUSEX? memory = GetMemoryStatus();
|
||||
var memory = GetMemoryStatus();
|
||||
var p = new SystemParameters
|
||||
{
|
||||
ScreenWidth = width,
|
||||
|
@ -22,6 +22,7 @@
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="6.0.1" />
|
||||
<PackageReference Include="PInvoke.User32" Version="0.7.104" />
|
||||
<PackageReference Include="Silk.NET.DXGI" Version="2.12.0" />
|
||||
<PackageReference Include="System.Reactive" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
@ -32,6 +32,5 @@
|
||||
<div id="app"></div>
|
||||
|
||||
<script src="_framework/blazor.webview.js"></script>
|
||||
<script src="_content/Fluxor.Blazor.Web/scripts/index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
59
Wabbajack.Common/CustomObservable.cs
Normal file
59
Wabbajack.Common/CustomObservable.cs
Normal file
@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reactive;
|
||||
|
||||
namespace Wabbajack.Common;
|
||||
|
||||
public class CustomObservable<T> : ObservableBase<T>
|
||||
{
|
||||
private readonly List<IObserver<T>> _observers = new();
|
||||
|
||||
private T _value;
|
||||
public T Value
|
||||
{
|
||||
get => _value;
|
||||
set
|
||||
{
|
||||
if (EqualityComparer<T>.Default.Equals(value, _value)) return;
|
||||
_value = value;
|
||||
|
||||
foreach (var observer in _observers)
|
||||
{
|
||||
observer.OnNext(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public CustomObservable(T value)
|
||||
{
|
||||
_value = value;
|
||||
}
|
||||
|
||||
protected override IDisposable SubscribeCore(IObserver<T> observer)
|
||||
{
|
||||
if (!_observers.Contains(observer))
|
||||
{
|
||||
_observers.Add(observer);
|
||||
observer.OnNext(Value);
|
||||
}
|
||||
|
||||
return new Unsubscriber<T>(_observers, observer);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class Unsubscriber<T> : IDisposable
|
||||
{
|
||||
private readonly List<IObserver<T>> _observers;
|
||||
private readonly IObserver<T> _observer;
|
||||
|
||||
public Unsubscriber(List<IObserver<T>> observers, IObserver<T> observer)
|
||||
{
|
||||
_observers = observers;
|
||||
_observer = observer;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_observers.Contains(_observer)) _observers.Remove(_observer);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user