wabbajack/Wabbajack.App.Wpf/View Models/MainWindowVM.cs
Luca fab17a6ae0
Wabbajack 3.3.0.0 Update (#2416)
* added more visible error messages to avoid user confusion

added hard drive free space detection, added red error message text, removed overwrite checkbox, added wiki button link

extended the error text for starting wabbajack in protected location

removed debug code

shortened error message to fit in text box

* restored warning removed in error, updated changelog, removed debug includes

* Update InstallerVM.cs

* Update InstallerVM.cs

* Update MainWindowViewModel.cs

* added json optional flag to only show version number over modlist image in installer view, if the modlist image already contains the title

removed debug code

change to pascal case and match existing code style

update changelog

* Fix manual downloads sometimes launching in browser

* Fix manual downloads from secure servers

* Remove duplicate user agent code

* Create configuration project and performance settings

* Bind new performance settings to UI

* Use performance settings to limit maximum memory per download

* Remove unused settings and related classes

* Updated CHANGELOG.md

* update CHANGELOG.md

* moved the existing files popup to an error message , heralding the return of the overwrite install checkbox

* added newline

* reverted erroneous edit

* gogID for fallout4 added

* update CHANGELOG.md

* Fix deadlock when loading new settings

* change folder/directory check logic

* update CHANGELOG.md

* revert unnecessary change

* update CHANGELOG.md

* Bump Wabbajack to .NET 7

* Bump ReactiveUI packages & deps

* Update GameFinder to 4.0.0

* Update CHANGELOG.md

* Update CHANGELOG.md

---------

Co-authored-by: JanuarySnow <bobfordiscord12@gmail.com>
Co-authored-by: JanuarySnow <85711747+JanuarySnow@users.noreply.github.com>
Co-authored-by: UrbanCMC <UrbanCMC@web.de>
Co-authored-by: trawzified <55751269+tr4wzified@users.noreply.github.com>
2023-10-12 12:33:06 -06:00

282 lines
11 KiB
C#

using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Orc.FileAssociation;
using Wabbajack.Common;
using Wabbajack.DTOs.Interventions;
using Wabbajack.Interventions;
using Wabbajack.Messages;
using Wabbajack.Models;
using Wabbajack.Networking.WabbajackClientApi;
using Wabbajack.Paths;
using Wabbajack.Paths.IO;
using Wabbajack.UserIntervention;
using Wabbajack.View_Models;
namespace Wabbajack
{
/// <summary>
/// Main View Model for the application.
/// Keeps track of which sub view is being shown in the window, and has some singleton wiring like WorkQueue and Logging.
/// </summary>
public class MainWindowVM : ViewModel
{
public MainWindow MainWindow { get; }
[Reactive]
public ViewModel ActivePane { get; private set; }
public ObservableCollectionExtended<IStatusMessage> Log { get; } = new ObservableCollectionExtended<IStatusMessage>();
public readonly CompilerVM Compiler;
public readonly InstallerVM Installer;
public readonly SettingsVM SettingsPane;
public readonly ModListGalleryVM Gallery;
public readonly ModeSelectionVM ModeSelectionVM;
public readonly WebBrowserVM WebBrowserVM;
public readonly Lazy<ModListContentsVM> ModListContentsVM;
public readonly UserInterventionHandlers UserInterventionHandlers;
private readonly Client _wjClient;
private readonly ILogger<MainWindowVM> _logger;
private readonly ResourceMonitor _resourceMonitor;
private List<ViewModel> PreviousPanes = new();
private readonly IServiceProvider _serviceProvider;
public ICommand CopyVersionCommand { get; }
public ICommand ShowLoginManagerVM { get; }
public ICommand OpenSettingsCommand { get; }
public string VersionDisplay { get; }
[Reactive]
public string ResourceStatus { get; set; }
[Reactive]
public string AppName { get; set; }
[Reactive]
public bool UpdateAvailable { get; private set; }
public MainWindowVM(ILogger<MainWindowVM> logger, Client wjClient,
IServiceProvider serviceProvider, ModeSelectionVM modeSelectionVM, ModListGalleryVM modListGalleryVM, ResourceMonitor resourceMonitor,
InstallerVM installer, CompilerVM compilerVM, SettingsVM settingsVM, WebBrowserVM webBrowserVM)
{
_logger = logger;
_wjClient = wjClient;
_resourceMonitor = resourceMonitor;
_serviceProvider = serviceProvider;
ConverterRegistration.Register();
Installer = installer;
Compiler = compilerVM;
SettingsPane = settingsVM;
Gallery = modListGalleryVM;
ModeSelectionVM = modeSelectionVM;
WebBrowserVM = webBrowserVM;
ModListContentsVM = new Lazy<ModListContentsVM>(() => new ModListContentsVM(serviceProvider.GetRequiredService<ILogger<ModListContentsVM>>(), this));
UserInterventionHandlers = new UserInterventionHandlers(serviceProvider.GetRequiredService<ILogger<UserInterventionHandlers>>(), this);
MessageBus.Current.Listen<NavigateToGlobal>()
.Subscribe(m => HandleNavigateTo(m.Screen))
.DisposeWith(CompositeDisposable);
MessageBus.Current.Listen<NavigateTo>()
.Subscribe(m => HandleNavigateTo(m.ViewModel))
.DisposeWith(CompositeDisposable);
MessageBus.Current.Listen<NavigateBack>()
.Subscribe(HandleNavigateBack)
.DisposeWith(CompositeDisposable);
MessageBus.Current.Listen<SpawnBrowserWindow>()
.ObserveOnGuiThread()
.Subscribe(HandleSpawnBrowserWindow)
.DisposeWith(CompositeDisposable);
_resourceMonitor.Updates
.Select(r => string.Join(", ", r.Where(r => r.Throughput > 0)
.Select(s => $"{s.Name} - {s.Throughput.ToFileSizeString()}/sec")))
.BindToStrict(this, view => view.ResourceStatus);
if (IsStartingFromModlist(out var path))
{
LoadModlistForInstalling.Send(path, null);
NavigateToGlobal.Send(NavigateToGlobal.ScreenType.Installer);
}
else
{
// Start on mode selection
NavigateToGlobal.Send(NavigateToGlobal.ScreenType.ModeSelectionView);
}
try
{
var assembly = Assembly.GetExecutingAssembly();
var assemblyLocation = assembly.Location;
var processLocation = Process.GetCurrentProcess().MainModule?.FileName ?? throw new Exception("Process location is unavailable!");
_logger.LogInformation("Assembly Location: {AssemblyLocation}", assemblyLocation);
_logger.LogInformation("Process Location: {ProcessLocation}", processLocation);
var fvi = FileVersionInfo.GetVersionInfo(string.IsNullOrWhiteSpace(assemblyLocation) ? processLocation : assemblyLocation);
Consts.CurrentMinimumWabbajackVersion = Version.Parse(fvi.FileVersion);
VersionDisplay = $"v{fvi.FileVersion}";
AppName = "WABBAJACK " + VersionDisplay;
_logger.LogInformation("Wabbajack Version: {FileVersion}", fvi.FileVersion);
Task.Run(() => _wjClient.SendMetric("started_wabbajack", fvi.FileVersion)).FireAndForget();
Task.Run(() => _wjClient.SendMetric("started_sha", ThisAssembly.Git.Sha));
// setup file association
try
{
var applicationRegistrationService = _serviceProvider.GetRequiredService<IApplicationRegistrationService>();
var applicationInfo = new ApplicationInfo("Wabbajack", "Wabbajack", "Wabbajack", processLocation);
applicationInfo.SupportedExtensions.Add("wabbajack");
applicationRegistrationService.RegisterApplication(applicationInfo);
}
catch (Exception ex)
{
_logger.LogError(ex, "While setting up file associations");
}
}
catch (Exception ex)
{
_logger.LogError(ex, "During App configuration");
VersionDisplay = "ERROR";
}
CopyVersionCommand = ReactiveCommand.Create(() =>
{
Clipboard.SetText($"Wabbajack {VersionDisplay}\n{ThisAssembly.Git.Sha}");
});
OpenSettingsCommand = ReactiveCommand.Create(
canExecute: this.WhenAny(x => x.ActivePane)
.Select(active => !object.ReferenceEquals(active, SettingsPane)),
execute: () => NavigateToGlobal.Send(NavigateToGlobal.ScreenType.Settings));
}
private void HandleNavigateTo(ViewModel objViewModel)
{
ActivePane = objViewModel;
}
private void HandleNavigateBack(NavigateBack navigateBack)
{
ActivePane = PreviousPanes.Last();
PreviousPanes.RemoveAt(PreviousPanes.Count - 1);
}
private void HandleManualDownload(ManualDownload manualDownload)
{
var handler = _serviceProvider.GetRequiredService<ManualDownloadHandler>();
handler.Intervention = manualDownload;
//MessageBus.Current.SendMessage(new OpenBrowserTab(handler));
}
private void HandleManualBlobDownload(ManualBlobDownload manualDownload)
{
var handler = _serviceProvider.GetRequiredService<ManualBlobDownloadHandler>();
handler.Intervention = manualDownload;
//MessageBus.Current.SendMessage(new OpenBrowserTab(handler));
}
private void HandleSpawnBrowserWindow(SpawnBrowserWindow msg)
{
var window = _serviceProvider.GetRequiredService<BrowserWindow>();
window.DataContext = msg.Vm;
window.Show();
}
private void HandleNavigateTo(NavigateToGlobal.ScreenType s)
{
if (s is NavigateToGlobal.ScreenType.Settings)
PreviousPanes.Add(ActivePane);
ActivePane = s switch
{
NavigateToGlobal.ScreenType.ModeSelectionView => ModeSelectionVM,
NavigateToGlobal.ScreenType.ModListGallery => Gallery,
NavigateToGlobal.ScreenType.Installer => Installer,
NavigateToGlobal.ScreenType.Compiler => Compiler,
NavigateToGlobal.ScreenType.Settings => SettingsPane,
_ => ActivePane
};
}
private static bool IsStartingFromModlist(out AbsolutePath modlistPath)
{
var args = Environment.GetCommandLineArgs();
if (args.Length == 2)
{
var arg = args[1].ToAbsolutePath();
if (arg.FileExists() && arg.Extension == Ext.Wabbajack)
{
modlistPath = arg;
return true;
}
}
modlistPath = default;
return false;
}
public void CancelRunningTasks(TimeSpan timeout)
{
var endTime = DateTime.Now.Add(timeout);
var cancellationTokenSource = _serviceProvider.GetRequiredService<CancellationTokenSource>();
cancellationTokenSource.Cancel();
bool IsInstalling() => Installer.InstallState is InstallState.Installing;
while (DateTime.Now < endTime && IsInstalling())
{
Thread.Sleep(TimeSpan.FromSeconds(1));
}
}
/*
public void NavigateTo(ViewModel vm)
{
ActivePane = vm;
}*/
/*
public void NavigateTo<T>(T vm)
where T : ViewModel, IBackNavigatingVM
{
vm.NavigateBackTarget = ActivePane;
ActivePane = vm;
}*/
public async Task ShutdownApplication()
{
/*
Dispose();
Settings.PosX = MainWindow.Left;
Settings.PosY = MainWindow.Top;
Settings.Width = MainWindow.Width;
Settings.Height = MainWindow.Height;
await MainSettings.SaveSettings(Settings);
Application.Current.Shutdown();
*/
}
}
}